summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2006-02-16 19:19:58 +0000
committerPierre Ossman <ossman@cendio.se>2006-02-16 19:19:58 +0000
commite205b25d65ccb380fa158711e24d55b6de5d9bc1 (patch)
tree470b4ced153c9ff18b8e35deb05c7a95efc838fc /src
parent5b881e62282f26b353635120935d114e0c7c3f3c (diff)
Reorganised the source tree. We now have src/ with a couple of subdirs:
* daemon/ - Contains the files specific to the polypaudio daemon. * modules/ - All loadable modules. * polyp/ - Files that are part of the public, application interface or are only used in libpolyp. * polypcore/ - All other shared files. * tests/ - Test programs. * utils/ - Utility programs. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@487 fefdeb5f-60dc-0310-8127-8f9354f1896f
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1061
-rw-r--r--src/client.conf.in39
-rw-r--r--src/daemon.conf.in77
-rw-r--r--src/daemon/caps.c131
-rw-r--r--src/daemon/caps.h29
-rw-r--r--src/daemon/cmdline.c300
-rw-r--r--src/daemon/cmdline.h35
-rw-r--r--src/daemon/cpulimit.c236
-rw-r--r--src/daemon/cpulimit.h34
-rw-r--r--src/daemon/daemon-conf.c297
-rw-r--r--src/daemon/daemon-conf.h80
-rw-r--r--src/daemon/dumpmodules.c99
-rw-r--r--src/daemon/dumpmodules.h31
-rw-r--r--src/daemon/main.c472
-rwxr-xr-xsrc/default.pa.in66
-rw-r--r--src/default.pa.win3243
-rwxr-xr-xsrc/depmod.py73
-rw-r--r--src/modules/module-alsa-sink.c292
-rw-r--r--src/modules/module-alsa-source.c278
-rw-r--r--src/modules/module-cli.c88
-rw-r--r--src/modules/module-combine.c394
-rw-r--r--src/modules/module-defs.h.m429
-rw-r--r--src/modules/module-detect.c227
-rw-r--r--src/modules/module-esound-compat-spawnfd.c81
-rw-r--r--src/modules/module-esound-compat-spawnpid.c79
-rw-r--r--src/modules/module-esound-sink.c427
-rw-r--r--src/modules/module-lirc.c241
-rw-r--r--src/modules/module-match.c235
-rw-r--r--src/modules/module-mmkbd-evdev.c250
-rw-r--r--src/modules/module-native-protocol-fd.c85
-rw-r--r--src/modules/module-null-sink.c150
-rw-r--r--src/modules/module-oss-mmap.c426
-rw-r--r--src/modules/module-oss.c468
-rw-r--r--src/modules/module-pipe-sink.c237
-rw-r--r--src/modules/module-pipe-source.c210
-rw-r--r--src/modules/module-protocol-stub.c261
-rw-r--r--src/modules/module-sine.c182
-rw-r--r--src/modules/module-solaris.c488
-rw-r--r--src/modules/module-tunnel.c688
-rw-r--r--src/modules/module-waveout.c583
-rw-r--r--src/modules/module-x11-bell.c173
-rw-r--r--src/modules/module-x11-publish.c192
-rw-r--r--src/modules/module-zeroconf-publish.c508
-rw-r--r--src/polyp/cdecl.h42
-rw-r--r--src/polyp/channelmap.c202
-rw-r--r--src/polyp/channelmap.h98
-rw-r--r--src/polyp/client-conf-x11.c91
-rw-r--r--src/polyp/client-conf-x11.h31
-rw-r--r--src/polyp/client-conf.c191
-rw-r--r--src/polyp/client-conf.h52
-rw-r--r--src/polyp/glib-mainloop.c538
-rw-r--r--src/polyp/glib-mainloop.h55
-rw-r--r--src/polyp/glib12-mainloop.c500
-rw-r--r--src/polyp/mainloop-api.c68
-rw-r--r--src/polyp/mainloop-api.h120
-rw-r--r--src/polyp/mainloop-signal.c267
-rw-r--r--src/polyp/mainloop-signal.h60
-rw-r--r--src/polyp/mainloop.c812
-rw-r--r--src/polyp/mainloop.h90
-rw-r--r--src/polyp/polyplib-browser.c312
-rw-r--r--src/polyp/polyplib-browser.h65
-rw-r--r--src/polyp/polyplib-context.c871
-rw-r--r--src/polyp/polyplib-context.h117
-rw-r--r--src/polyp/polyplib-def.h213
-rw-r--r--src/polyp/polyplib-error.c54
-rw-r--r--src/polyp/polyplib-error.h38
-rw-r--r--src/polyp/polyplib-internal.h154
-rw-r--r--src/polyp/polyplib-introspect.c1003
-rw-r--r--src/polyp/polyplib-introspect.h279
-rw-r--r--src/polyp/polyplib-operation.c103
-rw-r--r--src/polyp/polyplib-operation.h51
-rw-r--r--src/polyp/polyplib-scache.c127
-rw-r--r--src/polyp/polyplib-scache.h50
-rw-r--r--src/polyp/polyplib-simple.c393
-rw-r--r--src/polyp/polyplib-simple.h80
-rw-r--r--src/polyp/polyplib-stream.c807
-rw-r--r--src/polyp/polyplib-stream.h181
-rw-r--r--src/polyp/polyplib-subscribe.c81
-rw-r--r--src/polyp/polyplib-subscribe.h47
-rw-r--r--src/polyp/polyplib-version.h.in47
-rw-r--r--src/polyp/polyplib.h86
-rw-r--r--src/polyp/sample.c149
-rw-r--r--src/polyp/sample.h120
-rw-r--r--src/polyp/volume.c176
-rw-r--r--src/polyp/volume.h107
-rw-r--r--src/polypcore/alsa-util.c124
-rw-r--r--src/polypcore/alsa-util.h35
-rw-r--r--src/polypcore/authkey-prop.c87
-rw-r--r--src/polypcore/authkey-prop.h43
-rw-r--r--src/polypcore/authkey.c201
-rw-r--r--src/polypcore/authkey.h32
-rw-r--r--src/polypcore/autoload.c180
-rw-r--r--src/polypcore/autoload.h58
-rw-r--r--src/polypcore/cli-command.c834
-rw-r--r--src/polypcore/cli-command.h40
-rw-r--r--src/polypcore/cli-text.c385
-rw-r--r--src/polypcore/cli-text.h42
-rw-r--r--src/polypcore/cli.c147
-rw-r--r--src/polypcore/cli.h38
-rw-r--r--src/polypcore/client.c91
-rw-r--r--src/polypcore/client.h57
-rw-r--r--src/polypcore/conf-parser.c176
-rw-r--r--src/polypcore/conf-parser.h47
-rw-r--r--src/polypcore/core.c157
-rw-r--r--src/polypcore/core.h82
-rw-r--r--src/polypcore/dllmain.c46
-rw-r--r--src/polypcore/dynarray.c102
-rw-r--r--src/polypcore/dynarray.h49
-rw-r--r--src/polypcore/endianmacros.h77
-rw-r--r--src/polypcore/esound.h209
-rw-r--r--src/polypcore/g711.c2531
-rw-r--r--src/polypcore/g711.h40
-rw-r--r--src/polypcore/gccmacro.h53
-rw-r--r--src/polypcore/hashmap.c194
-rw-r--r--src/polypcore/hashmap.h53
-rw-r--r--src/polypcore/howl-wrap.c116
-rw-r--r--src/polypcore/howl-wrap.h37
-rw-r--r--src/polypcore/idxset.c390
-rw-r--r--src/polypcore/idxset.h94
-rw-r--r--src/polypcore/inet_ntop.c80
-rw-r--r--src/polypcore/inet_ntop.h12
-rw-r--r--src/polypcore/iochannel.c292
-rw-r--r--src/polypcore/iochannel.h72
-rw-r--r--src/polypcore/ioline.c368
-rw-r--r--src/polypcore/ioline.h51
-rw-r--r--src/polypcore/llist.h69
-rw-r--r--src/polypcore/log.c150
-rw-r--r--src/polypcore/log.h69
-rw-r--r--src/polypcore/mcalign.c188
-rw-r--r--src/polypcore/mcalign.h77
-rw-r--r--src/polypcore/memblock.c169
-rw-r--r--src/polypcore/memblock.h87
-rw-r--r--src/polypcore/memblockq.c343
-rw-r--r--src/polypcore/memblockq.h104
-rw-r--r--src/polypcore/memchunk.c58
-rw-r--r--src/polypcore/memchunk.h45
-rw-r--r--src/polypcore/modargs.c260
-rw-r--r--src/polypcore/modargs.h49
-rw-r--r--src/polypcore/modinfo.c84
-rw-r--r--src/polypcore/modinfo.h43
-rw-r--r--src/polypcore/module.c275
-rw-r--r--src/polypcore/module.h70
-rw-r--r--src/polypcore/namereg.c212
-rw-r--r--src/polypcore/namereg.h43
-rw-r--r--src/polypcore/native-common.h114
-rw-r--r--src/polypcore/oss-util.c167
-rw-r--r--src/polypcore/oss-util.h32
-rw-r--r--src/polypcore/packet.c69
-rw-r--r--src/polypcore/packet.h41
-rw-r--r--src/polypcore/parseaddr.c112
-rw-r--r--src/polypcore/parseaddr.h42
-rw-r--r--src/polypcore/pdispatch.c295
-rw-r--r--src/polypcore/pdispatch.h50
-rw-r--r--src/polypcore/pid.c286
-rw-r--r--src/polypcore/pid.h30
-rw-r--r--src/polypcore/play-memchunk.c118
-rw-r--r--src/polypcore/play-memchunk.h36
-rw-r--r--src/polypcore/poll.c190
-rw-r--r--src/polypcore/poll.h57
-rw-r--r--src/polypcore/props.c119
-rw-r--r--src/polypcore/props.h58
-rw-r--r--src/polypcore/protocol-cli.c95
-rw-r--r--src/polypcore/protocol-cli.h35
-rw-r--r--src/polypcore/protocol-esound.c1166
-rw-r--r--src/polypcore/protocol-esound.h35
-rw-r--r--src/polypcore/protocol-http.c265
-rw-r--r--src/polypcore/protocol-http.h35
-rw-r--r--src/polypcore/protocol-native.c2216
-rw-r--r--src/polypcore/protocol-native.h37
-rw-r--r--src/polypcore/protocol-simple.c453
-rw-r--r--src/polypcore/protocol-simple.h35
-rw-r--r--src/polypcore/pstream-util.c61
-rw-r--r--src/polypcore/pstream-util.h35
-rw-r--r--src/polypcore/pstream.c492
-rw-r--r--src/polypcore/pstream.h52
-rw-r--r--src/polypcore/queue.c108
-rw-r--r--src/polypcore/queue.h40
-rw-r--r--src/polypcore/random.c71
-rw-r--r--src/polypcore/random.h27
-rw-r--r--src/polypcore/resampler.c614
-rw-r--r--src/polypcore/resampler.h73
-rw-r--r--src/polypcore/sample-util.c328
-rw-r--r--src/polypcore/sample-util.h53
-rw-r--r--src/polypcore/scache.c392
-rw-r--r--src/polypcore/scache.h62
-rw-r--r--src/polypcore/sconv-s16be.c41
-rw-r--r--src/polypcore/sconv-s16be.h28
-rw-r--r--src/polypcore/sconv-s16le.c101
-rw-r--r--src/polypcore/sconv-s16le.h28
-rw-r--r--src/polypcore/sconv.c168
-rw-r--r--src/polypcore/sconv.h33
-rw-r--r--src/polypcore/sink-input.c368
-rw-r--r--src/polypcore/sink-input.h106
-rw-r--r--src/polypcore/sink.c445
-rw-r--r--src/polypcore/sink.h100
-rw-r--r--src/polypcore/sioman.c42
-rw-r--r--src/polypcore/sioman.h28
-rw-r--r--src/polypcore/socket-client.c513
-rw-r--r--src/polypcore/socket-client.h47
-rw-r--r--src/polypcore/socket-server.c432
-rw-r--r--src/polypcore/socket-server.h45
-rw-r--r--src/polypcore/socket-util.c246
-rw-r--r--src/polypcore/socket-util.h38
-rw-r--r--src/polypcore/sound-file-stream.c179
-rw-r--r--src/polypcore/sound-file-stream.h29
-rw-r--r--src/polypcore/sound-file.c139
-rw-r--r--src/polypcore/sound-file.h32
-rw-r--r--src/polypcore/source-output.c228
-rw-r--r--src/polypcore/source-output.h92
-rw-r--r--src/polypcore/source.c195
-rw-r--r--src/polypcore/source.h86
-rw-r--r--src/polypcore/strbuf.c167
-rw-r--r--src/polypcore/strbuf.h38
-rw-r--r--src/polypcore/strlist.c137
-rw-r--r--src/polypcore/strlist.h47
-rw-r--r--src/polypcore/subscribe.c230
-rw-r--r--src/polypcore/subscribe.h37
-rw-r--r--src/polypcore/tagstruct.c609
-rw-r--r--src/polypcore/tagstruct.h92
-rw-r--r--src/polypcore/tokenizer.c88
-rw-r--r--src/polypcore/tokenizer.h32
-rw-r--r--src/polypcore/util.c1176
-rw-r--r--src/polypcore/util.h99
-rw-r--r--src/polypcore/winsock.h23
-rw-r--r--src/polypcore/x11prop.c70
-rw-r--r--src/polypcore/x11prop.h33
-rw-r--r--src/polypcore/x11wrap.c235
-rw-r--r--src/polypcore/x11wrap.h52
-rw-r--r--src/polypcore/xmalloc.c122
-rw-r--r--src/polypcore/xmalloc.h58
-rw-r--r--src/tests/cpulimit-test.c92
-rw-r--r--src/tests/mainloop-test.c146
-rw-r--r--src/tests/mcalign-test.c96
-rw-r--r--src/tests/pacat-simple.c101
-rw-r--r--src/tests/parec-simple.c100
-rw-r--r--src/tests/strlist-test.c42
-rw-r--r--src/tests/voltest.c20
-rwxr-xr-xsrc/utils/esdcompat.sh.in98
-rw-r--r--src/utils/pabrowse.c139
-rw-r--r--src/utils/pacat.c541
-rw-r--r--src/utils/pacmd.c183
-rw-r--r--src/utils/pactl.c784
-rw-r--r--src/utils/paplay.c386
-rw-r--r--src/utils/pax11publish.c217
244 files changed, 48095 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 00000000..92c7dae1
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,1061 @@
+# $Id$
+#
+# This file is part of polypaudio.
+#
+# polypaudio 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 of the License, or
+# (at your option) any later version.
+#
+# polypaudio 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with polypaudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+
+###################################
+# Extra directories #
+###################################
+
+polypincludedir=$(includedir)/polyp
+polypcoreincludedir=$(includedir)/polypcore
+polypconfdir=$(sysconfdir)/polypaudio
+
+modlibdir=$(libdir)/polypaudio
+
+###################################
+# Defines #
+###################################
+
+POLYPAUDIO_BINARY=$(bindir)/polypaudio$(EXEEXT)
+if OS_IS_WIN32
+DEFAULT_CONFIG_DIR=%POLYP_ROOT%
+else
+DEFAULT_CONFIG_DIR=$(polypconfdir)
+endif
+
+###################################
+# Compiler/linker flags #
+###################################
+
+AM_CFLAGS = -I$(top_srcdir)/src
+AM_CFLAGS += $(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS
+AM_CFLAGS += $(LTDLINCL)
+AM_CFLAGS += $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS)
+AM_CFLAGS += -DDLSEARCHPATH=\"$(modlibdir)\"
+#AM_CFLAGS += -DDLSEARCHPATH=\"$(shell pwd)\"
+AM_CFLAGS += -DDEFAULT_CONFIG_DIR=\"$(DEFAULT_CONFIG_DIR)\"
+AM_CFLAGS += -DPOLYPAUDIO_BINARY=\"$(POLYPAUDIO_BINARY)\"
+
+# This cool debug trap works on i386/gcc only
+AM_CFLAGS += '-DDEBUG_TRAP=__asm__("int $$3")'
+
+AM_LIBADD = $(PTHREAD_LIBS)
+AM_LDADD = $(PTHREAD_LIBS)
+
+# Only required on some platforms but defined for all to avoid errors
+AM_LDFLAGS = -no-undefined
+
+if STATIC_BINS
+BINLDFLAGS = -static
+endif
+
+if OS_IS_WIN32
+AM_LDFLAGS+=-Wl,--export-all-symbols
+WINSOCK_LIBS=-lwsock32 -lws2_32 -lwininet
+endif
+
+###################################
+# Extra files #
+###################################
+
+EXTRA_DIST = \
+ client.conf.in \
+ daemon.conf.in \
+ default.pa.in \
+ depmod.py \
+ utils/esdcompat.sh.in \
+ modules/module-defs.h.m4
+
+polypconf_DATA = default.pa daemon.conf client.conf
+
+BUILT_SOURCES = polyp/polyplib-version.h
+
+###################################
+# Main daemon #
+###################################
+
+bin_PROGRAMS = polypaudio
+
+polypaudio_SOURCES = \
+ daemon/caps.h daemon/caps.c \
+ daemon/cmdline.c daemon/cmdline.h \
+ daemon/cpulimit.c daemon/cpulimit.h \
+ daemon/daemon-conf.c daemon/daemon-conf.h \
+ daemon/dumpmodules.c daemon/dumpmodules.h \
+ daemon/main.c \
+ polypcore/gccmacro.h
+
+polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+polypaudio_CPPFLAGS = $(AM_CPPFLAGS)
+polypaudio_LDADD = $(AM_LDADD) libpolypcore.la $(LIBLTDL) \
+ $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS)
+polypaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f))
+
+if PREOPEN_MODS
+PREOPEN_LIBS = $(PREOPEN_MODS)
+else
+PREOPEN_LIBS = $(modlib_LTLIBRARIES)
+endif
+
+###################################
+# Utility programs #
+###################################
+
+bin_PROGRAMS += \
+ pacat \
+ pactl \
+ paplay
+
+if HAVE_AF_UNIX
+bin_PROGRAMS += pacmd
+endif
+
+if HAVE_X11
+bin_PROGRAMS += pax11publish
+endif
+
+if HAVE_HOWL
+bin_PROGRAMS += pabrowse
+endif
+
+bin_SCRIPTS = utils/esdcompat.sh
+
+pacat_SOURCES = utils/pacat.c
+pacat_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la
+pacat_CFLAGS = $(AM_CFLAGS)
+pacat_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+paplay_SOURCES = utils/paplay.c
+paplay_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la $(LIBSNDFILE_LIBS)
+paplay_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
+paplay_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pactl_SOURCES = utils/pactl.c
+pactl_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la $(LIBSNDFILE_LIBS)
+pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
+pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pacmd_SOURCES = utils/pacmd.c polypcore/pid.c polypcore/pid.h
+pacmd_CFLAGS = $(AM_CFLAGS)
+pacmd_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la
+pacmd_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pax11publish_SOURCES = utils/pax11publish.c
+pax11publish_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+pax11publish_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
+pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pabrowse_SOURCES = utils/pabrowse.c
+pabrowse_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la libpolyp-browse-@PA_MAJORMINOR@.la
+pabrowse_CFLAGS = $(AM_CFLAGS)
+pabrowse_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+###################################
+# Test programs #
+###################################
+
+noinst_PROGRAMS = \
+ mainloop-test \
+ mcalign-test \
+ pacat-simple \
+ parec-simple \
+ strlist-test \
+ voltest
+
+if HAVE_SIGXCPU
+noinst_PROGRAMS += \
+ cpulimit-test \
+ cpulimit-test2
+endif
+
+if HAVE_GLIB20
+noinst_PROGRAMS += \
+ mainloop-test-glib
+endif
+
+if HAVE_GLIB12
+noinst_PROGRAMS += \
+ mainloop-test-glib12
+endif
+
+mainloop_test_SOURCES = tests/mainloop-test.c
+mainloop_test_CFLAGS = $(AM_CFLAGS)
+mainloop_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la libpolyp-@PA_MAJORMINOR@.la
+mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+mcalign_test_SOURCES = tests/mcalign-test.c
+mcalign_test_CFLAGS = $(AM_CFLAGS)
+mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpolypcore.la
+mcalign_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pacat_simple_SOURCES = tests/pacat-simple.c
+pacat_simple_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-simple-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la
+pacat_simple_CFLAGS = $(AM_CFLAGS)
+pacat_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+parec_simple_SOURCES = tests/parec-simple.c
+parec_simple_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-simple-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la
+parec_simple_CFLAGS = $(AM_CFLAGS)
+parec_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+strlist_test_SOURCES = tests/strlist-test.c
+strlist_test_CFLAGS = $(AM_CFLAGS)
+strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpolypcore.la libstrlist.la
+strlist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+voltest_SOURCES = tests/voltest.c
+voltest_CFLAGS = $(AM_CFLAGS)
+voltest_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la
+voltest_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+cpulimit_test_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h
+cpulimit_test_CFLAGS = $(AM_CFLAGS)
+cpulimit_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la libpolypcore.la
+cpulimit_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+cpulimit_test2_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h
+cpulimit_test2_CFLAGS = $(AM_CFLAGS) -DTEST2
+cpulimit_test2_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la libpolypcore.la
+cpulimit_test2_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+mainloop_test_glib_SOURCES = $(mainloop_test_SOURCES)
+mainloop_test_glib_CFLAGS = $(mainloop_test_CFLAGS) $(GLIB20_CFLAGS) -DGLIB_MAIN_LOOP
+mainloop_test_glib_LDADD = $(mainloop_test_LDADD) $(GLIB20_LIBS) libpolyp-mainloop-glib-@PA_MAJORMINOR@.la
+mainloop_test_glib_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+mainloop_test_glib12_SOURCES = $(mainloop_test_SOURCES)
+mainloop_test_glib12_CFLAGS = $(mainloop_test_CFLAGS) $(GLIB12_CFLAGS) -DGLIB_MAIN_LOOP
+mainloop_test_glib12_LDADD = $(mainloop_test_LDADD) $(GLIB12_LIBS) libpolyp-mainloop-glib12-@PA_MAJORMINOR@.la
+mainloop_test_glib12_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+###################################
+# Client library #
+###################################
+
+polypinclude_HEADERS = \
+ polyp/cdecl.h \
+ polyp/channelmap.h \
+ polyp/glib-mainloop.h \
+ polyp/mainloop.h \
+ polyp/mainloop-api.h \
+ polyp/mainloop-signal.h \
+ polyp/polyplib.h \
+ polyp/polyplib-context.h \
+ polyp/polyplib-def.h \
+ polyp/polyplib-error.h \
+ polyp/polyplib-introspect.h \
+ polyp/polyplib-operation.h \
+ polyp/polyplib-scache.h \
+ polyp/polyplib-simple.h \
+ polyp/polyplib-stream.h \
+ polyp/polyplib-subscribe.h \
+ polyp/polyplib-version.h \
+ polyp/sample.h \
+ polyp/volume.h
+
+if HAVE_HOWL
+polypinclude_HEADERS += \
+ polyp/polyplib-browser.h
+endif
+
+lib_LTLIBRARIES = \
+ libpolyp-@PA_MAJORMINOR@.la \
+ libpolyp-error-@PA_MAJORMINOR@.la \
+ libpolyp-mainloop-@PA_MAJORMINOR@.la \
+ libpolyp-simple-@PA_MAJORMINOR@.la
+
+if HAVE_HOWL
+lib_LTLIBRARIES += \
+ libpolyp-browse-@PA_MAJORMINOR@.la
+endif
+
+if HAVE_GLIB20
+lib_LTLIBRARIES += \
+ libpolyp-mainloop-glib-@PA_MAJORMINOR@.la
+endif
+
+if HAVE_GLIB12
+lib_LTLIBRARIES += \
+ libpolyp-mainloop-glib12-@PA_MAJORMINOR@.la
+endif
+
+# Public interface
+libpolyp_@PA_MAJORMINOR@_la_SOURCES = \
+ polyp/cdecl.h \
+ polyp/channelmap.c polyp/channelmap.h \
+ polyp/client-conf.c polyp/client-conf.h \
+ polyp/llist.h \
+ polyp/mainloop-api.c polyp/mainloop-api.h \
+ polyp/polyplib.h \
+ polyp/polyplib-context.c polyp/polyplib-context.h \
+ polyp/polyplib-def.h \
+ polyp/polyplib-internal.h \
+ polyp/polyplib-introspect.c polyp/polyplib-introspect.h \
+ polyp/polyplib-operation.c polyp/polyplib-operation.h \
+ polyp/polyplib-scache.c polyp/polyplib-scache.h \
+ polyp/polyplib-stream.c polyp/polyplib-stream.h \
+ polyp/polyplib-subscribe.c polyp/polyplib-subscribe.h \
+ polyp/sample.c polyp/sample.h \
+ polyp/volume.c polyp/volume.h
+
+# Internal stuff that is shared with libpolypcore
+libpolyp_@PA_MAJORMINOR@_la_SOURCES += \
+ polypcore/authkey.c polypcore/authkey.h \
+ polypcore/conf-parser.c polypcore/conf-parser.h \
+ polypcore/dllmain.c \
+ polypcore/dynarray.c polypcore/dynarray.h \
+ polypcore/gccmacro.h \
+ polypcore/idxset.c polypcore/idxset.h \
+ polypcore/iochannel.c polypcore/iochannel.h \
+ polypcore/log.c polypcore/log.h \
+ polypcore/mcalign.c polypcore/mcalign.h \
+ polypcore/memblock.c polypcore/memblock.h \
+ polypcore/memchunk.c polypcore/memchunk.h \
+ polypcore/native-common.h \
+ polypcore/packet.c polypcore/packet.h \
+ polypcore/parseaddr.c polypcore/parseaddr.h \
+ polypcore/pdispatch.c polypcore/pdispatch.h \
+ polypcore/pstream.c polypcore/pstream.h \
+ polypcore/pstream-util.c polypcore/pstream-util.h \
+ polypcore/queue.c polypcore/queue.h \
+ polypcore/random.c polypcore/random.h \
+ polypcore/socket-client.c polypcore/socket-client.h \
+ polypcore/socket-util.c polypcore/socket-util.h \
+ polypcore/strbuf.c polypcore/strbuf.h \
+ polypcore/strlist.c polypcore/strlist.h \
+ polypcore/tagstruct.c polypcore/tagstruct.h \
+ polypcore/util.c polypcore/util.h \
+ polypcore/winsock.h \
+ polypcore/xmalloc.c polypcore/xmalloc.h
+
+if HAVE_X11
+libpolyp_@PA_MAJORMINOR@_la_SOURCES += \
+ polyp/client-conf-x11.c polyp/client-conf-x11.h \
+ polypcore/x11prop.c polypcore/x11prop.h
+endif
+
+libpolyp_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS)
+libpolyp_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
+libpolyp_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS)
+
+if HAVE_X11
+libpolyp_@PA_MAJORMINOR@_la_CFLAGS += $(X_CFLAGS)
+libpolyp_@PA_MAJORMINOR@_la_LDFLAGS += $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
+endif
+
+if HAVE_LIBASYNCNS
+libpolyp_@PA_MAJORMINOR@_la_CFLAGS += $(LIBASYNCNS_CFLAGS)
+libpolyp_@PA_MAJORMINOR@_la_LIBADD += $(LIBASYNCNS_LIBS)
+endif
+
+libpolyp_error_@PA_MAJORMINOR@_la_SOURCES = polyp/polyplib-error.c polyp/polyplib-error.h
+libpolyp_error_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS)
+libpolyp_error_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la
+libpolyp_error_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
+
+libpolyp_mainloop_@PA_MAJORMINOR@_la_SOURCES = \
+ polyp/mainloop.c polyp/mainloop.h \
+ polyp/mainloop-signal.c polyp/mainloop-signal.h \
+ polypcore/poll.c polypcore/poll.h
+libpolyp_mainloop_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS)
+libpolyp_mainloop_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la $(WINSOCK_LIBS)
+libpolyp_mainloop_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
+
+libpolyp_simple_@PA_MAJORMINOR@_la_SOURCES = polyp/polyplib-simple.c polyp/polyplib-simple.h
+libpolyp_simple_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS)
+libpolyp_simple_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la
+libpolyp_simple_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
+
+libpolyp_browse_@PA_MAJORMINOR@_la_SOURCES = polyp/polyplib-browser.c polyp/polyplib-browser.h
+libpolyp_browse_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS)
+libpolyp_browse_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la $(HOWL_LIBS)
+libpolyp_browse_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
+
+libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_SOURCES = polyp/glib-mainloop.h polyp/glib-mainloop.c
+libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS)
+libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-mainloop-@PA_MAJORMINOR@.la $(GLIB20_LIBS)
+libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
+
+libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_SOURCES = polyp/glib-mainloop.h polyp/glib12-mainloop.c
+libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(GLIB12_CFLAGS)
+libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-mainloop-@PA_MAJORMINOR@.la $(GLIB12_LIBS)
+libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
+
+###################################
+# Daemon core library #
+###################################
+
+polypcoreinclude_HEADERS = \
+ polypcore/cli-command.h \
+ polypcore/client.h \
+ polypcore/core.h \
+ polypcore/dynarray.h \
+ polypcore/endianmacros.h \
+ polypcore/hashmap.h \
+ polypcore/idxset.h \
+ polypcore/iochannel.h \
+ polypcore/log.h \
+ polypcore/memblock.h \
+ polypcore/memblockq.h \
+ polypcore/memchunk.h \
+ polypcore/modargs.h \
+ polypcore/module.h \
+ polypcore/namereg.h \
+ polypcore/queue.h \
+ polypcore/resampler.h \
+ polypcore/sample-util.h \
+ polypcore/sink.h \
+ polypcore/sink-input.h \
+ polypcore/sioman.h \
+ polypcore/socket-server.h \
+ polypcore/socket-client.h \
+ polypcore/socket-util.h \
+ polypcore/source.h \
+ polypcore/source-output.h \
+ polypcore/strbuf.h \
+ polypcore/tokenizer.h \
+ polypcore/tagstruct.h \
+ polypcore/util.h
+
+lib_LTLIBRARIES += libpolypcore.la
+
+# Some public stuff is used even in the core.
+libpolypcore_la_SOURCES = \
+ polyp/channelmap.c polyp/channelmap.h \
+ polyp/mainloop.c polyp/mainloop.h \
+ polyp/mainloop-api.c polyp/mainloop-api.h \
+ polyp/mainloop-signal.c polyp/mainloop-signal.h \
+ polyp/sample.c polyp/sample.h \
+ polyp/volume.c polyp/volume.h
+
+# Pure core stuff (some are shared in libpolyp though).
+libpolypcore_la_SOURCES += \
+ polypcore/autoload.c polypcore/autoload.h \
+ polypcore/cli-command.c polypcore/cli-command.h \
+ polypcore/cli-text.c polypcore/cli-text.h \
+ polypcore/client.c polypcore/client.h \
+ polypcore/conf-parser.c polypcore/conf-parser.h \
+ polypcore/core.c polypcore/core.h \
+ polypcore/dllmain.c \
+ polypcore/dynarray.c polypcore/dynarray.h \
+ polypcore/endianmacros.h \
+ polypcore/g711.c polypcore/g711.h \
+ polypcore/hashmap.c polypcore/hashmap.h \
+ polypcore/idxset.c polypcore/idxset.h \
+ polypcore/log.c polypcore/log.h \
+ polypcore/mcalign.c polypcore/mcalign.h \
+ polypcore/memblock.c polypcore/memblock.h \
+ polypcore/memblockq.c polypcore/memblockq.h \
+ polypcore/memchunk.c polypcore/memchunk.h \
+ polypcore/modargs.c polypcore/modargs.h \
+ polypcore/modinfo.c polypcore/modinfo.h \
+ polypcore/module.c polypcore/module.h \
+ polypcore/namereg.c polypcore/namereg.h \
+ polypcore/pid.c polypcore/pid.h \
+ polypcore/play-memchunk.c polypcore/play-memchunk.h \
+ polypcore/poll.c polypcore/poll.h \
+ polypcore/props.c polypcore/props.h \
+ polypcore/queue.c polypcore/queue.h \
+ polypcore/random.c polypcore/random.h \
+ polypcore/resampler.c polypcore/resampler.h \
+ polypcore/sample-util.c polypcore/sample-util.h \
+ polypcore/scache.c polypcore/scache.h \
+ polypcore/sconv.c polypcore/sconv.h \
+ polypcore/sconv-s16be.c polypcore/sconv-s16be.h \
+ polypcore/sconv-s16le.c polypcore/sconv-s16le.h \
+ polypcore/sink.c polypcore/sink.h \
+ polypcore/sink-input.c polypcore/sink-input.h \
+ polypcore/sioman.c polypcore/sioman.h \
+ polypcore/sound-file.c polypcore/sound-file.h \
+ polypcore/sound-file-stream.c polypcore/sound-file-stream.h \
+ polypcore/source.c polypcore/source.h \
+ polypcore/source-output.c polypcore/source-output.h \
+ polypcore/strbuf.c polypcore/strbuf.h \
+ polypcore/subscribe.c polypcore/subscribe.h \
+ polypcore/tokenizer.c polypcore/tokenizer.h \
+ polypcore/util.c polypcore/util.h \
+ polypcore/winsock.h \
+ polypcore/xmalloc.c polypcore/xmalloc.h
+
+libpolypcore_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBOIL_CFLAGS)
+libpolypcore_la_LDFLAGS = -avoid-version
+libpolypcore_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS)
+
+###################################
+# Plug-in support libraries #
+###################################
+
+### Warning! Due to an obscure bug in libtool/automake it is required
+### that the libraries in modlib_LTLIBRARIES are specified in-order,
+### i.e. libraries near the end of the list depend on libraries near
+### the head, and not the other way!
+
+modlib_LTLIBRARIES = \
+ libsocket-util.la \
+ libiochannel.la \
+ libsocket-server.la \
+ libsocket-client.la \
+ libparseaddr.la \
+ libpacket.la \
+ libpstream.la \
+ libioline.la \
+ libcli.la \
+ libprotocol-cli.la \
+ libtagstruct.la \
+ libpstream-util.la \
+ libpdispatch.la \
+ libauthkey.la \
+ libauthkey-prop.la \
+ libstrlist.la \
+ libprotocol-simple.la \
+ libprotocol-esound.la \
+ libprotocol-native.la \
+ libprotocol-http.la
+
+if HAVE_X11
+modlib_LTLIBRARIES += \
+ libx11wrap.la \
+ libx11prop.la
+endif
+
+if HAVE_OSS
+modlib_LTLIBRARIES += \
+ liboss-util.la
+endif
+
+if HAVE_ALSA
+modlib_LTLIBRARIES += \
+ libalsa-util.la
+endif
+
+if HAVE_HOWL
+modlib_LTLIBRARIES += \
+ libhowl-wrap.la
+endif
+
+libprotocol_simple_la_SOURCES = polypcore/protocol-simple.c polypcore/protocol-simple.h
+libprotocol_simple_la_LDFLAGS = -avoid-version
+libprotocol_simple_la_LIBADD = $(AM_LIBADD) libpolypcore.la libsocket-server.la libiochannel.la
+
+libsocket_server_la_SOURCES = \
+ polypcore/inet_ntop.c polypcore/inet_ntop.h \
+ polypcore/socket-server.c polypcore/socket-server.h
+libsocket_server_la_LDFLAGS = -avoid-version
+libsocket_server_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la libsocket-util.la $(LIBWRAP_LIBS) $(WINSOCK_LIBS)
+
+libsocket_client_la_SOURCES = polypcore/socket-client.c polypcore/socket-client.h
+libsocket_client_la_LDFLAGS = -avoid-version
+libsocket_client_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la libsocket-util.la libparseaddr.la $(LIBASYNCNS_LIBS) $(WINSOCK_LIBS)
+libsocket_client_la_CFLAGS = $(AM_CFLAGS) $(LIBASYNCNS_CFLAGS)
+
+libparseaddr_la_SOURCES = polypcore/parseaddr.c polypcore/parseaddr.h
+libparseaddr_la_LDFLAGS = -avoid-version
+libparseaddr_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+libpstream_la_SOURCES = polypcore/pstream.c polypcore/pstream.h
+libpstream_la_LDFLAGS = -avoid-version
+libpstream_la_LIBADD = $(AM_LIBADD) libpolypcore.la libpacket.la libiochannel.la $(WINSOCK_LIBS)
+
+libpstream_util_la_SOURCES = polypcore/pstream-util.c polypcore/pstream-util.h
+libpstream_util_la_LDFLAGS = -avoid-version
+libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la
+
+libpdispatch_la_SOURCES = polypcore/pdispatch.c polypcore/pdispatch.h
+libpdispatch_la_LDFLAGS = -avoid-version
+libpdispatch_la_LIBADD = $(AM_LIBADD) libtagstruct.la libpolypcore.la
+
+libiochannel_la_SOURCES = polypcore/iochannel.c polypcore/iochannel.h
+libiochannel_la_LDFLAGS = -avoid-version
+libiochannel_la_LIBADD = $(AM_LIBADD) libsocket-util.la libpolypcore.la $(WINSOCK_LIBS)
+
+libpacket_la_SOURCES = polypcore/packet.c polypcore/packet.h
+libpacket_la_LDFLAGS = -avoid-version
+libpacket_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+libioline_la_SOURCES = polypcore/ioline.c polypcore/ioline.h
+libioline_la_LDFLAGS = -avoid-version
+libioline_la_LIBADD = $(AM_LIBADD) libiochannel.la libpolypcore.la
+
+libcli_la_SOURCES = polypcore/cli.c polypcore/cli.h
+libcli_la_CPPFLAGS = $(AM_CPPFLAGS)
+libcli_la_LDFLAGS = -avoid-version
+libcli_la_LIBADD = $(AM_LIBADD) libiochannel.la libioline.la libpolypcore.la
+
+libstrlist_la_SOURCES = polypcore/strlist.c polypcore/strlist.h
+libstrlist_la_LDFLAGS = -avoid-version
+libstrlist_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+libprotocol_cli_la_SOURCES = polypcore/protocol-cli.c polypcore/protocol-cli.h
+libprotocol_cli_la_LDFLAGS = -avoid-version
+libprotocol_cli_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libcli.la libpolypcore.la
+
+libprotocol_http_la_SOURCES = polypcore/protocol-http.c polypcore/protocol-http.h
+libprotocol_http_la_LDFLAGS = -avoid-version
+libprotocol_http_la_LIBADD = $(AM_LIBADD) libsocket-server.la libioline.la libpolypcore.la libiochannel.la
+
+libprotocol_native_la_SOURCES = polypcore/protocol-native.c polypcore/protocol-native.h polypcore/native-common.h
+libprotocol_native_la_LDFLAGS = -avoid-version
+libprotocol_native_la_LIBADD = $(AM_LIBADD) libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libstrlist.la libpolypcore.la libiochannel.la
+
+libtagstruct_la_SOURCES = polypcore/tagstruct.c polypcore/tagstruct.h
+libtagstruct_la_LDFLAGS = -avoid-version
+libtagstruct_la_LIBADD = $(AM_LIBADD) libpolypcore.la $(WINSOCK_LIBS)
+
+libprotocol_esound_la_SOURCES = polypcore/protocol-esound.c polypcore/protocol-esound.h polypcore/esound.h
+libprotocol_esound_la_LDFLAGS = -avoid-version
+libprotocol_esound_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libauthkey.la libpolypcore.la
+
+libauthkey_la_SOURCES = polypcore/authkey.c polypcore/authkey.h
+libauthkey_la_LDFLAGS = -avoid-version
+libauthkey_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+libauthkey_prop_la_SOURCES = polypcore/authkey-prop.c polypcore/authkey-prop.h
+libauthkey_prop_la_LDFLAGS = -avoid-version
+libauthkey_prop_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+libsocket_util_la_SOURCES = polypcore/socket-util.c polypcore/socket-util.h
+libsocket_util_la_LDFLAGS = -avoid-version
+libsocket_util_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS)
+
+# X11
+
+libx11wrap_la_SOURCES = polypcore/x11wrap.c polypcore/x11wrap.h
+libx11wrap_la_LDFLAGS = -avoid-version
+libx11wrap_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+libx11wrap_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
+
+libx11prop_la_SOURCES = polypcore/x11prop.c polypcore/x11prop.h
+libx11prop_la_LDFLAGS = -avoid-version
+libx11prop_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+libx11prop_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
+
+# OSS
+
+liboss_util_la_SOURCES = polypcore/oss-util.c polypcore/oss-util.h
+liboss_util_la_LDFLAGS = -avoid-version
+
+# ALSA
+
+libalsa_util_la_SOURCES = polypcore/alsa-util.c polypcore/alsa-util.h
+libalsa_util_la_LDFLAGS = -avoid-version
+libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS)
+libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+# HOWL
+
+libhowl_wrap_la_SOURCES = polypcore/howl-wrap.c polypcore/howl-wrap.h
+libhowl_wrap_la_LDFLAGS = -avoid-version
+libhowl_wrap_la_LIBADD = $(AM_LIBADD) $(HOWL_LIBS)
+libhowl_wrap_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS)
+
+###################################
+# Plug-in libraries #
+###################################
+
+modlib_LTLIBRARIES += \
+ module-cli.la \
+ module-cli-protocol-tcp.la \
+ module-cli-protocol-tcp6.la \
+ module-simple-protocol-tcp.la \
+ module-simple-protocol-tcp6.la \
+ module-esound-protocol-tcp.la \
+ module-esound-protocol-tcp6.la \
+ module-native-protocol-tcp.la \
+ module-native-protocol-tcp6.la \
+ module-native-protocol-fd.la \
+ module-sine.la \
+ module-combine.la \
+ module-tunnel-sink.la \
+ module-tunnel-source.la \
+ module-null-sink.la \
+ module-esound-sink.la \
+ module-http-protocol-tcp.la \
+ module-http-protocol-tcp6.la \
+ module-detect.la
+
+if HAVE_AF_UNIX
+modlib_LTLIBRARIES += \
+ module-cli-protocol-unix.la \
+ module-simple-protocol-unix.la \
+ module-esound-protocol-unix.la \
+ module-native-protocol-unix.la \
+ module-http-protocol-unix.la
+endif
+
+if HAVE_MKFIFO
+modlib_LTLIBRARIES += \
+ module-pipe-sink.la \
+ module-pipe-source.la
+endif
+
+if !OS_IS_WIN32
+modlib_LTLIBRARIES += \
+ module-esound-compat-spawnfd.la \
+ module-esound-compat-spawnpid.la
+endif
+
+if HAVE_REGEX
+modlib_LTLIBRARIES += \
+ module-match.la
+endif
+
+if HAVE_X11
+modlib_LTLIBRARIES += \
+ module-x11-bell.la \
+ module-x11-publish.la
+endif
+
+if HAVE_OSS
+modlib_LTLIBRARIES += \
+ module-oss.la \
+ module-oss-mmap.la
+endif
+
+if HAVE_ALSA
+modlib_LTLIBRARIES += \
+ module-alsa-sink.la \
+ module-alsa-source.la
+endif
+
+if HAVE_SOLARIS
+modlib_LTLIBRARIES += \
+ module-solaris.la
+endif
+
+if HAVE_HOWL
+modlib_LTLIBRARIES += \
+ module-zeroconf-publish.la
+endif
+
+if HAVE_LIRC
+modlib_LTLIBRARIES += \
+ module-lirc.la
+endif
+
+if HAVE_EVDEV
+modlib_LTLIBRARIES += \
+ module-mmkbd-evdev.la
+endif
+
+if OS_IS_WIN32
+modlib_LTLIBRARIES += \
+ module-waveout.la
+endif
+
+# These are generated by a M4 script
+
+SYMDEF_FILES = \
+ modules/module-cli-symdef.h \
+ modules/module-cli-protocol-tcp-symdef.h \
+ modules/module-cli-protocol-tcp6-symdef.h \
+ modules/module-cli-protocol-unix-symdef.h \
+ modules/module-pipe-sink-symdef.h \
+ modules/module-pipe-source-symdef.h \
+ modules/module-simple-protocol-tcp-symdef.h \
+ modules/module-simple-protocol-tcp6-symdef.h \
+ modules/module-simple-protocol-unix-symdef.h \
+ modules/module-esound-protocol-tcp-symdef.h \
+ modules/module-esound-protocol-tcp6-symdef.h \
+ modules/module-esound-protocol-unix-symdef.h \
+ modules/module-native-protocol-tcp-symdef.h \
+ modules/module-native-protocol-tcp6-symdef.h \
+ modules/module-native-protocol-unix-symdef.h \
+ modules/module-native-protocol-fd-symdef.h \
+ modules/module-sine-symdef.h \
+ modules/module-combine-symdef.h \
+ modules/module-esound-compat-spawnfd-symdef.h \
+ modules/module-esound-compat-spawnpid-symdef.h \
+ modules/module-match-symdef.h \
+ modules/module-tunnel-sink-symdef.h \
+ modules/module-tunnel-source-symdef.h \
+ modules/module-null-sink-symdef.h \
+ modules/module-esound-sink-symdef.h \
+ modules/module-zeroconf-publish-symdef.h \
+ modules/module-lirc-symdef.h \
+ modules/module-mmkbd-evdev-symdef.h \
+ modules/module-http-protocol-tcp-symdef.h \
+ modules/module-http-protocol-tcp6-symdef.h \
+ modules/module-http-protocol-unix-symdef.h \
+ modules/module-x11-bell-symdef.h \
+ modules/module-x11-publish-symdef.h \
+ modules/module-oss-symdef.h \
+ modules/module-oss-mmap-symdef.h \
+ modules/module-alsa-sink-symdef.h \
+ modules/module-alsa-source-symdef.h \
+ modules/module-solaris-symdef.h \
+ modules/module-waveout-symdef.h \
+ modules/module-detect-symdef.h
+
+EXTRA_DIST += $(SYMDEF_FILES)
+BUILT_SOURCES += $(SYMDEF_FILES)
+
+$(SYMDEF_FILES): modules/module-defs.h.m4
+ $(M4) -Dfname="$@" $< > $@
+
+# Simple protocol
+
+module_simple_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
+module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_simple_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-simple.la libsocket-server.la
+
+module_simple_protocol_tcp6_la_SOURCES = modules/module-protocol-stub.c
+module_simple_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
+module_simple_protocol_tcp6_la_LDFLAGS = -module -avoid-version
+module_simple_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-simple.la libsocket-server.la
+
+module_simple_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
+module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-simple.la libsocket-server.la libsocket-util.la
+
+# CLI protocol
+
+module_cli_la_SOURCES = modules/module-cli.c
+module_cli_la_LDFLAGS = -module -avoid-version
+module_cli_la_LIBADD = $(AM_LIBADD) libcli.la libiochannel.la libpolypcore.la
+
+module_cli_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
+module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_cli_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-cli.la libsocket-server.la
+
+module_cli_protocol_tcp6_la_SOURCES = modules/module-protocol-stub.c
+module_cli_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
+module_cli_protocol_tcp6_la_LDFLAGS = -module -avoid-version
+module_cli_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-cli.la libsocket-server.la
+
+module_cli_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
+module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_cli_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-cli.la libsocket-server.la libsocket-util.la
+
+# HTTP protocol
+
+module_http_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_http_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
+module_http_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_http_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-http.la libsocket-server.la
+
+module_http_protocol_tcp6_la_SOURCES = modules/module-protocol-stub.c
+module_http_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
+module_http_protocol_tcp6_la_LDFLAGS = -module -avoid-version
+module_http_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-http.la libsocket-server.la
+
+module_http_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
+module_http_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-http.la libsocket-server.la libsocket-util.la
+
+# Native protocol
+
+module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
+module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_native_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la
+
+module_native_protocol_tcp6_la_SOURCES = modules/module-protocol-stub.c
+module_native_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
+module_native_protocol_tcp6_la_LDFLAGS = -module -avoid-version
+module_native_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la
+
+module_native_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
+module_native_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_native_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la libsocket-util.la
+
+module_native_protocol_fd_la_SOURCES = modules/module-native-protocol-fd.c
+module_native_protocol_fd_la_CFLAGS = $(AM_CFLAGS)
+module_native_protocol_fd_la_LDFLAGS = -module -avoid-version
+module_native_protocol_fd_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la libsocket-util.la libiochannel.la
+
+# EsounD protocol
+
+module_esound_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
+module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_esound_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-esound.la libsocket-server.la
+
+module_esound_protocol_tcp6_la_SOURCES = modules/module-protocol-stub.c
+module_esound_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
+module_esound_protocol_tcp6_la_LDFLAGS = -module -avoid-version
+module_esound_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-esound.la libsocket-server.la
+
+module_esound_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
+module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_esound_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-esound.la libsocket-server.la libsocket-util.la
+
+module_esound_compat_spawnfd_la_SOURCES = modules/module-esound-compat-spawnfd.c
+module_esound_compat_spawnfd_la_LDFLAGS = -module -avoid-version
+module_esound_compat_spawnfd_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+module_esound_compat_spawnpid_la_SOURCES = modules/module-esound-compat-spawnpid.c
+module_esound_compat_spawnpid_la_LDFLAGS = -module -avoid-version
+module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+module_esound_sink_la_SOURCES = modules/module-esound-sink.c
+module_esound_sink_la_LDFLAGS = -module -avoid-version
+module_esound_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la libsocket-client.la libauthkey.la
+
+# Pipes
+
+module_pipe_sink_la_SOURCES = modules/module-pipe-sink.c
+module_pipe_sink_la_LDFLAGS = -module -avoid-version
+module_pipe_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la
+
+module_pipe_source_la_SOURCES = modules/module-pipe-source.c
+module_pipe_source_la_LDFLAGS = -module -avoid-version
+module_pipe_source_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la
+
+# Fake sources/sinks
+
+module_sine_la_SOURCES = modules/module-sine.c
+module_sine_la_LDFLAGS = -module -avoid-version
+module_sine_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+module_null_sink_la_SOURCES = modules/module-null-sink.c
+module_null_sink_la_LDFLAGS = -module -avoid-version
+module_null_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+# Couplings
+
+module_combine_la_SOURCES = modules/module-combine.c
+module_combine_la_LDFLAGS = -module -avoid-version
+module_combine_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+module_match_la_SOURCES = modules/module-match.c
+module_match_la_LDFLAGS = -module -avoid-version
+module_match_la_LIBADD = $(AM_LIBADD) libpolypcore.la
+
+module_tunnel_sink_la_SOURCES = modules/module-tunnel.c
+module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS)
+module_tunnel_sink_la_LDFLAGS = -module -avoid-version
+module_tunnel_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la
+
+module_tunnel_source_la_SOURCES = modules/module-tunnel.c
+module_tunnel_source_la_LDFLAGS = -module -avoid-version
+module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpolypcore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la
+
+# X11
+
+module_x11_bell_la_SOURCES = modules/module-x11-bell.c
+module_x11_bell_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+module_x11_bell_la_LDFLAGS = -module -avoid-version
+module_x11_bell_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la
+
+module_x11_publish_la_SOURCES = modules/module-x11-publish.c
+module_x11_publish_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+module_x11_publish_la_LDFLAGS = -module -avoid-version
+module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libauthkey.la libauthkey-prop.la libx11prop.la libstrlist.la
+
+# OSS
+
+module_oss_la_SOURCES = modules/module-oss.c
+module_oss_la_LDFLAGS = -module -avoid-version
+module_oss_la_LIBADD = $(AM_LIBADD) libiochannel.la liboss-util.la
+
+module_oss_mmap_la_SOURCES = modules/module-oss-mmap.c
+module_oss_mmap_la_LDFLAGS = -module -avoid-version
+module_oss_mmap_la_LIBADD = $(AM_LIBADD) liboss-util.la
+
+# ALSA
+
+module_alsa_sink_la_SOURCES = modules/module-alsa-sink.c
+module_alsa_sink_la_LDFLAGS = -module -avoid-version
+module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
+module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+module_alsa_source_la_SOURCES = modules/module-alsa-source.c
+module_alsa_source_la_LDFLAGS = -module -avoid-version
+module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
+module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+# Solaris
+
+module_solaris_la_SOURCES = modules/module-solaris.c
+module_solaris_la_LDFLAGS = -module -avoid-version
+module_solaris_la_LIBADD = $(AM_LIBADD) libiochannel.la
+
+# HOWL
+
+module_zeroconf_publish_la_SOURCES = modules/module-zeroconf-publish.c
+module_zeroconf_publish_la_LDFLAGS = -module -avoid-version
+module_zeroconf_publish_la_LIBADD = $(AM_LIBADD) $(HOWL_LIBS) libhowl-wrap.la
+module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS)
+
+# LIRC
+
+module_lirc_la_SOURCES = modules/module-lirc.c
+module_lirc_la_LDFLAGS = -module -avoid-version
+module_lirc_la_LIBADD = $(AM_LIBADD) $(LIRC_LIBS)
+module_lirc_la_CFLAGS = $(AM_CFLAGS) $(LIRC_CFLAGS)
+
+# Linux evdev
+
+module_mmkbd_evdev_la_SOURCES = modules/module-mmkbd-evdev.c
+module_mmkbd_evdev_la_LDFLAGS = -module -avoid-version
+module_mmkbd_evdev_la_LIBADD = $(AM_LIBADD)
+module_mmkbd_evdev_la_CFLAGS = $(AM_CFLAGS)
+
+# Windows waveout
+
+module_waveout_la_SOURCES = modules/module-waveout.c
+module_waveout_la_LDFLAGS = -module -avoid-version
+module_waveout_la_LIBADD = $(AM_LIBADD) libpolypcore.la -lwinmm
+module_waveout_la_CFLAGS = $(AM_CFLAGS)
+
+# Hardware autodetection module
+module_detect_la_SOURCES = modules/module-detect.c
+module_detect_la_LDFLAGS = -module -avoid-version
+module_detect_la_LIBADD = $(AM_LIBADD)
+module_detect_la_CFLAGS = $(AM_CFLAGS)
+
+###################################
+# Some minor stuff #
+###################################
+
+suid: polypaudio
+ chown root $<
+ chmod u+s $<
+
+utils/esdcompat.sh: utils/esdcompat.sh.in Makefile
+ sed -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \
+ -e 's,@PACKAGE_NAME\@,$(PACKAGE_NAME),g' \
+ -e 's,@POLYPAUDIO_BINARY\@,$(POLYPAUDIO_BINARY),g' < $< > $@
+
+client.conf: client.conf.in Makefile
+ sed -e 's,@POLYPAUDIO_BINARY\@,$(POLYPAUDIO_BINARY),g' < $< > $@
+
+if OS_IS_WIN32
+default.pa: default.pa.win32
+ cp $< $@
+else
+default.pa: default.pa.in Makefile
+ sed -e 's,@POLYPAUDIO_BINARY\@,$(POLYPAUDIO_BINARY),g' < $< > $@
+endif
+
+daemon.conf: daemon.conf.in Makefile
+ sed -e 's,@DLSEARCHPATH\@,$(modlibdir),g' \
+ -e 's,@DEFAULT_CONFIG_FILE\@,$(DEFAULT_CONFIG_DIR),g' < $< > $@
+
+install-exec-hook:
+ chown root $(DESTDIR)$(bindir)/polypaudio ; true
+ chmod u+s $(DESTDIR)$(bindir)/polypaudio
+ ln -sf pacat $(DESTDIR)$(bindir)/parec
diff --git a/src/client.conf.in b/src/client.conf.in
new file mode 100644
index 00000000..fbf645a4
--- /dev/null
+++ b/src/client.conf.in
@@ -0,0 +1,39 @@
+# $Id$
+#
+# This file is part of polypaudio.
+#
+# polypaudio 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 of the License, or
+# (at your option) any later version.
+#
+# polypaudio 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with polypaudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+## Configuration file for polypaudio clients. Default values are
+## commented out. Use either ; or # for commenting
+
+## Path to the polypaudio daemon to run when autospawning.
+; daemon-binary = @POLYPAUDIO_BINARY@
+
+## Extra arguments to pass to the polypaudio daemon
+; extra-arguments = --log-target=syslog --exit-idle-time=5
+
+## The default sink to connect to
+; default-sink =
+
+## The default source to connect to
+; default-source =
+
+## The default sever to connect to
+; default-server =
+
+## Autospawn daemons?
+; autospawn = 0
diff --git a/src/daemon.conf.in b/src/daemon.conf.in
new file mode 100644
index 00000000..d5373018
--- /dev/null
+++ b/src/daemon.conf.in
@@ -0,0 +1,77 @@
+# $Id$
+#
+# This file is part of polypaudio.
+#
+# polypaudio 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 of the License, or
+# (at your option) any later version.
+#
+# polypaudio 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with polypaudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+## Configuration file for the polypaudio daemon. Default values are
+## commented out. Use either ; or # for commenting
+
+# Extra verbositiy
+; verbose = 0
+
+## Daemonize after startup
+; daemonize = 0
+
+## Quit if startup fails
+; fail = 1
+
+## Renice the daemon to level -15 and try to get SCHED_FIFO
+## scheduling. This a good idea if you hear annyoing noise in the
+## playback. However, this is a certain security issue, since it works
+## when called SUID root only. root is dropped immediately after gaining
+## the nice level and SCHED_FIFO scheduling on startup.
+; high-priority = 0
+
+## Disallow module loading after startup
+; disallow-module-loading = 0
+
+## Terminate the daemon after the last client quit and this time
+## passed. Use a negative value to disable this feature.
+; exit-idle-time = -1
+
+## Unload autoloaded modules after being idle for this time
+; module-idle-time = 20
+
+## The path were to look for dynamic shared objects (DSOs aka
+## plugins). You may specify more than one path seperated by
+## colons.
+; dl-search-path = @DLSEARCHPATH@
+
+## The default script file to load. Specify an empty string for not
+## loading a default script file. The
+; default-script-file = @DEFAULT_CONFIG_FILE@
+
+## The default log target. Use either "stderr", "syslog" or
+## "auto". The latter is equivalent to "sylog" in case daemonize is
+## true, otherwise to "stderr".
+; log-target = auto
+
+## The resampling algorithm to use. Use one of src-sinc-best-quality,
+## src-sinc-medium-quality, src-sinc-fastest, src-zero-order-hold,
+## src-linear, trivial. See the documentation of libsamplerate for an
+## explanation for the different methods. The method 'trivial' is the
+## only algorithm implemented without usage of floating point
+## numbers. If you're tight on CPU consider using this. On the other
+## hand it has the worst quality of all.
+; resample-method = sinc-fastest
+
+## Create a PID file in /tmp/polypaudio-$USER/pid. Of this is enabled
+## you may use commands like "polypaudio --kill" or "polypaudio
+## --check". If you are planning to start more than one polypaudio
+## process per user, you better disable this option since it
+## effectively disables multiple instances.
+; use-pid-file = 1
diff --git a/src/daemon/caps.c b/src/daemon/caps.c
new file mode 100644
index 00000000..8d429459
--- /dev/null
+++ b/src/daemon/caps.c
@@ -0,0 +1,131 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
+#include <polypcore/log.h>
+#include "caps.h"
+
+#ifdef HAVE_GETUID
+
+/* Drop root rights when called SUID root */
+void pa_drop_root(void) {
+ uid_t uid = getuid();
+
+ if (uid == 0 || geteuid() != 0)
+ return;
+
+ pa_log_info(__FILE__": dropping root rights.\n");
+
+#if defined(HAVE_SETRESUID)
+ setresuid(uid, uid, uid);
+#elif defined(HAVE_SETREUID)
+ setreuid(uid, uid);
+#else
+ setuid(uid);
+ seteuid(uid);
+#endif
+}
+
+#else
+
+void pa_drop_root(void) {
+}
+
+#endif
+
+#ifdef HAVE_SYS_CAPABILITY_H
+
+/* Limit capabilities set to CAPSYS_NICE */
+int pa_limit_caps(void) {
+ int r = -1;
+ cap_t caps;
+ cap_value_t nice_cap = CAP_SYS_NICE;
+
+ caps = cap_init();
+ assert(caps);
+
+ cap_clear(caps);
+
+ cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET);
+ cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET);
+
+ if (cap_set_proc(caps) < 0)
+ goto fail;
+
+ pa_log_info(__FILE__": dropped capabilities successfully.\n");
+
+ r = 0;
+
+fail:
+ cap_free (caps);
+
+ return r;
+}
+
+/* Drop all capabilities, effectively becoming a normal user */
+int pa_drop_caps(void) {
+ cap_t caps;
+ int r = -1;
+
+ caps = cap_init();
+ assert(caps);
+
+ cap_clear(caps);
+
+ if (cap_set_proc(caps) < 0) {
+ pa_log(__FILE__": failed to drop capabilities: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ r = 0;
+
+fail:
+ cap_free (caps);
+
+ return r;
+}
+
+#else
+
+/* NOOPs in case capabilities are not available. */
+int pa_limit_caps(void) {
+ return 0;
+}
+
+int pa_drop_caps(void) {
+ pa_drop_root();
+ return 0;
+}
+
+#endif
+
diff --git a/src/daemon/caps.h b/src/daemon/caps.h
new file mode 100644
index 00000000..3bb861d1
--- /dev/null
+++ b/src/daemon/caps.h
@@ -0,0 +1,29 @@
+#ifndef foocapshfoo
+#define foocapshfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+void pa_drop_root(void);
+int pa_limit_caps(void);
+int pa_drop_caps(void);
+
+#endif
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
new file mode 100644
index 00000000..0b5f9ec7
--- /dev/null
+++ b/src/daemon/cmdline.c
@@ -0,0 +1,300 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/stat.h>
+
+#include "cmdline.h"
+#include <polypcore/util.h>
+#include <polypcore/strbuf.h>
+#include <polypcore/xmalloc.h>
+
+/* Argument codes for getopt_long() */
+enum {
+ ARG_HELP = 256,
+ ARG_VERSION,
+ ARG_DUMP_CONF,
+ ARG_DUMP_MODULES,
+ ARG_DAEMONIZE,
+ ARG_FAIL,
+ ARG_LOG_LEVEL,
+ ARG_HIGH_PRIORITY,
+ ARG_DISALLOW_MODULE_LOADING,
+ ARG_EXIT_IDLE_TIME,
+ ARG_MODULE_IDLE_TIME,
+ ARG_SCACHE_IDLE_TIME,
+ ARG_LOG_TARGET,
+ ARG_LOAD,
+ ARG_FILE,
+ ARG_DL_SEARCH_PATH,
+ ARG_RESAMPLE_METHOD,
+ ARG_KILL,
+ ARG_USE_PID_FILE,
+ ARG_CHECK
+};
+
+/* Tabel for getopt_long() */
+static struct option long_options[] = {
+ {"help", 0, 0, ARG_HELP},
+ {"version", 0, 0, ARG_VERSION},
+ {"dump-conf", 0, 0, ARG_DUMP_CONF},
+ {"dump-modules", 0, 0, ARG_DUMP_MODULES},
+ {"daemonize", 2, 0, ARG_DAEMONIZE},
+ {"fail", 2, 0, ARG_FAIL},
+ {"verbose", 2, 0, ARG_LOG_LEVEL},
+ {"log-level", 2, 0, ARG_LOG_LEVEL},
+ {"high-priority", 2, 0, ARG_HIGH_PRIORITY},
+ {"disallow-module-loading", 2, 0, ARG_DISALLOW_MODULE_LOADING},
+ {"exit-idle-time", 2, 0, ARG_EXIT_IDLE_TIME},
+ {"module-idle-time", 2, 0, ARG_MODULE_IDLE_TIME},
+ {"scache-idle-time", 2, 0, ARG_SCACHE_IDLE_TIME},
+ {"log-target", 1, 0, ARG_LOG_TARGET},
+ {"load", 1, 0, ARG_LOAD},
+ {"file", 1, 0, ARG_FILE},
+ {"dl-search-path", 1, 0, ARG_DL_SEARCH_PATH},
+ {"resample-method", 1, 0, ARG_RESAMPLE_METHOD},
+ {"kill", 0, 0, ARG_KILL},
+ {"use-pid-file", 2, 0, ARG_USE_PID_FILE},
+ {"check", 0, 0, ARG_CHECK},
+ {NULL, 0, 0, 0}
+};
+
+void pa_cmdline_help(const char *argv0) {
+ const char *e;
+
+ if ((e = strrchr(argv0, '/')))
+ e++;
+ else
+ e = argv0;
+
+ printf("%s [options]\n\n"
+ "COMMANDS:\n"
+ " -h, --help Show this help\n"
+ " --version Show version\n"
+ " --dump-conf Dump default configuration\n"
+ " --dump-modules Dump list of available modules\n"
+ " -k --kill Kill a running daemon\n"
+ " --check Check for a running daemon\n\n"
+
+ "OPTIONS:\n"
+ " -D, --daemonize[=BOOL] Daemonize after startup\n"
+ " --fail[=BOOL] Quit when startup fails\n"
+ " --high-priority[=BOOL] Try to set high process priority\n"
+ " (only available as root)\n"
+ " --disallow-module-loading[=BOOL] Disallow module loading after startup\n"
+ " --exit-idle-time=SECS Terminate the daemon when idle and this\n"
+ " time passed\n"
+ " --module-idle-time=SECS Unload autoloaded modules when idle and\n"
+ " this time passed\n"
+ " --scache-idle-time=SECS Unload autoloaded samples when idle and\n"
+ " this time passed\n"
+ " --log-level[=LEVEL] Increase or set verbosity level\n"
+ " -v Increase the verbosity level\n"
+ " --log-target={auto,syslog,stderr} Specify the log target\n"
+ " -p, --dl-search-path=PATH Set the search path for dynamic shared\n"
+ " objects (plugins)\n"
+ " --resample-method=[METHOD] Use the specified resampling method\n"
+ " (one of src-sinc-medium-quality,\n"
+ " src-sinc-best-quality,src-sinc-fastest\n"
+ " src-zero-order-hold,src-linear,trivial)\n"
+ " --use-pid-file[=BOOL] Create a PID file\n\n"
+
+ "STARTUP SCRIPT:\n"
+ " -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module with\n"
+ " the specified argument\n"
+ " -F, --file=FILENAME Run the specified script\n"
+ " -C Open a command line on the running TTY\n"
+ " after startup\n\n"
+
+ " -n Don't load default script file\n", e);
+}
+
+int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) {
+ pa_strbuf *buf = NULL;
+ int c;
+ assert(conf && argc && argv);
+
+ buf = pa_strbuf_new();
+
+ if (conf->script_commands)
+ pa_strbuf_puts(buf, conf->script_commands);
+
+ while ((c = getopt_long(argc, argv, "L:F:ChDnp:kv", long_options, NULL)) != -1) {
+ switch (c) {
+ case ARG_HELP:
+ case 'h':
+ conf->cmd = PA_CMD_HELP;
+ break;
+
+ case ARG_VERSION:
+ conf->cmd = PA_CMD_VERSION;
+ break;
+
+ case ARG_DUMP_CONF:
+ conf->cmd = PA_CMD_DUMP_CONF;
+ break;
+
+ case ARG_DUMP_MODULES:
+ conf->cmd = PA_CMD_DUMP_MODULES;
+ break;
+
+ case 'k':
+ case ARG_KILL:
+ conf->cmd = PA_CMD_KILL;
+ break;
+
+ case ARG_CHECK:
+ conf->cmd = PA_CMD_CHECK;
+ break;
+
+ case ARG_LOAD:
+ case 'L':
+ pa_strbuf_printf(buf, "load-module %s\n", optarg);
+ break;
+
+ case ARG_FILE:
+ case 'F':
+ pa_strbuf_printf(buf, ".include %s\n", optarg);
+ break;
+
+ case 'C':
+ pa_strbuf_puts(buf, "load-module module-cli\n");
+ break;
+
+ case ARG_DAEMONIZE:
+ case 'D':
+ if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(__FILE__": --daemonize expects boolean argument\n");
+ goto fail;
+ }
+ break;
+
+ case ARG_FAIL:
+ if ((conf->fail = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(__FILE__": --fail expects boolean argument\n");
+ goto fail;
+ }
+ break;
+
+ case 'v':
+ case ARG_LOG_LEVEL:
+
+ if (optarg) {
+ if (pa_daemon_conf_set_log_level(conf, optarg) < 0) {
+ pa_log(__FILE__": --log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error).\n");
+ goto fail;
+ }
+ } else {
+ if (conf->log_level < PA_LOG_LEVEL_MAX-1)
+ conf->log_level++;
+ }
+
+ break;
+
+ case ARG_HIGH_PRIORITY:
+ if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(__FILE__": --high-priority expects boolean argument\n");
+ goto fail;
+ }
+ break;
+
+ case ARG_DISALLOW_MODULE_LOADING:
+ if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(__FILE__": --disallow-module-loading expects boolean argument\n");
+ goto fail;
+ }
+ break;
+
+ case ARG_USE_PID_FILE:
+ if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(__FILE__": --use-pid-file expects boolean argument\n");
+ goto fail;
+ }
+ break;
+
+ case 'p':
+ case ARG_DL_SEARCH_PATH:
+ pa_xfree(conf->dl_search_path);
+ conf->dl_search_path = *optarg ? pa_xstrdup(optarg) : NULL;
+ break;
+
+ case 'n':
+ pa_xfree(conf->default_script_file);
+ conf->default_script_file = NULL;
+ break;
+
+ case ARG_LOG_TARGET:
+ if (pa_daemon_conf_set_log_target(conf, optarg) < 0) {
+ pa_log(__FILE__": Invalid log target: use either 'syslog', 'stderr' or 'auto'.\n");
+ goto fail;
+ }
+ break;
+
+ case ARG_EXIT_IDLE_TIME:
+ conf->exit_idle_time = atoi(optarg);
+ break;
+
+ case ARG_MODULE_IDLE_TIME:
+ conf->module_idle_time = atoi(optarg);
+ break;
+
+ case ARG_SCACHE_IDLE_TIME:
+ conf->scache_idle_time = atoi(optarg);
+ break;
+
+ case ARG_RESAMPLE_METHOD:
+ if (pa_daemon_conf_set_resample_method(conf, optarg) < 0) {
+ pa_log(__FILE__": Invalid resample method '%s'.\n", optarg);
+ goto fail;
+ }
+ break;
+
+ default:
+ goto fail;
+ }
+ }
+
+ pa_xfree(conf->script_commands);
+ conf->script_commands = pa_strbuf_tostring_free(buf);
+
+ if (!conf->script_commands) {
+ pa_xfree(conf->script_commands);
+ conf->script_commands = NULL;
+ }
+
+ *d = optind;
+
+ return 0;
+
+fail:
+ if (buf)
+ pa_strbuf_free(buf);
+
+ return -1;
+}
diff --git a/src/daemon/cmdline.h b/src/daemon/cmdline.h
new file mode 100644
index 00000000..e2eaf0d2
--- /dev/null
+++ b/src/daemon/cmdline.h
@@ -0,0 +1,35 @@
+#ifndef foocmdlinehfoo
+#define foocmdlinehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "daemon-conf.h"
+
+/* Parese the command line and store its data in *c. Return the index
+ * of the first unparsed argument in *d. */
+int pa_cmdline_parse(pa_daemon_conf*c, int argc, char *const argv [], int *d);
+
+/* Show the command line help. The command name is extracted from
+ * argv[0] which should be passed in argv0. */
+void pa_cmdline_help(const char *argv0);
+
+#endif
diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c
new file mode 100644
index 00000000..6887796f
--- /dev/null
+++ b/src/daemon/cpulimit.c
@@ -0,0 +1,236 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; 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 "cpulimit.h"
+#include <polypcore/util.h>
+#include <polypcore/log.h>
+
+#ifdef HAVE_SIGXCPU
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+/* This module implements a watchdog that makes sure that the current
+ * process doesn't consume more than 70% CPU time for 10 seconds. This
+ * is very useful when using SCHED_FIFO scheduling which effectively
+ * disables multitasking. */
+
+/* Method of operation: Using SIGXCPU a signal handler is called every
+ * 10s process CPU time. That function checks if less than 14s system
+ * time have passed. In that case, it tries to contact the main event
+ * loop through a pipe. After two additional seconds it is checked
+ * whether the main event loop contact was successful. If not, the
+ * program is terminated forcibly. */
+
+/* Utilize this much CPU time at maximum */
+#define CPUTIME_PERCENT 70
+
+/* Check every 10s */
+#define CPUTIME_INTERVAL_SOFT (10)
+
+/* Recheck after 2s */
+#define CPUTIME_INTERVAL_HARD (2)
+
+/* Time of the last CPU load check */
+static time_t last_time = 0;
+
+/* Pipe for communicating with the main loop */
+static int the_pipe[2] = {-1, -1};
+
+/* Main event loop and IO event for the FIFO */
+static pa_mainloop_api *api = NULL;
+static pa_io_event *io_event = NULL;
+
+/* Saved sigaction struct for SIGXCPU */
+static struct sigaction sigaction_prev;
+
+/* Nonzero after pa_cpu_limit_init() */
+static int installed = 0;
+
+/* The current state of operation */
+static enum {
+ PHASE_IDLE, /* Normal state */
+ PHASE_SOFT /* After CPU overload has been detected */
+} phase = PHASE_IDLE;
+
+/* Reset the SIGXCPU timer to the next t seconds */
+static void reset_cpu_time(int t) {
+ int r;
+ long n;
+ struct rlimit rl;
+ struct rusage ru;
+
+ /* Get the current CPU time of the current process */
+ r = getrusage(RUSAGE_SELF, &ru);
+ assert(r >= 0);
+
+ n = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec + t;
+
+ r = getrlimit(RLIMIT_CPU, &rl);
+ assert(r >= 0);
+
+ rl.rlim_cur = n;
+ r = setrlimit(RLIMIT_CPU, &rl);
+ assert(r >= 0);
+}
+
+/* A simple, thread-safe puts() work-alike */
+static void write_err(const char *p) {
+ pa_loop_write(2, p, strlen(p));
+}
+
+/* The signal handler, called on every SIGXCPU */
+static void signal_handler(int sig) {
+ assert(sig == SIGXCPU);
+
+ if (phase == PHASE_IDLE) {
+ time_t now;
+
+#ifdef PRINT_CPU_LOAD
+ char t[256];
+#endif
+
+ time(&now);
+
+#ifdef PRINT_CPU_LOAD
+ snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", (double)CPUTIME_INTERVAL_SOFT/(now-last_time)*100);
+ write_err(t);
+#endif
+
+ if (CPUTIME_INTERVAL_SOFT >= ((now-last_time)*(double)CPUTIME_PERCENT/100)) {
+ static const char c = 'X';
+
+ write_err("Soft CPU time limit exhausted, terminating.\n");
+
+ /* Try a soft cleanup */
+ write(the_pipe[1], &c, sizeof(c));
+ phase = PHASE_SOFT;
+ reset_cpu_time(CPUTIME_INTERVAL_HARD);
+
+ } else {
+
+ /* Everything's fine */
+ reset_cpu_time(CPUTIME_INTERVAL_SOFT);
+ last_time = now;
+ }
+
+ } else if (phase == PHASE_SOFT) {
+ write_err("Hard CPU time limit exhausted, terminating forcibly.\n");
+ _exit(1); /* Forced exit */
+ }
+}
+
+/* Callback for IO events on the FIFO */
+static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags f, void *userdata) {
+ char c;
+ assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]);
+ read(the_pipe[0], &c, sizeof(c));
+ m->quit(m, 1); /* Quit the main loop */
+}
+
+/* Initializes CPU load limiter */
+int pa_cpu_limit_init(pa_mainloop_api *m) {
+ struct sigaction sa;
+ assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1 && !installed);
+
+ time(&last_time);
+
+ /* Prepare the main loop pipe */
+ if (pipe(the_pipe) < 0) {
+ pa_log(__FILE__": pipe() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ pa_make_nonblock_fd(the_pipe[0]);
+ pa_make_nonblock_fd(the_pipe[1]);
+ pa_fd_set_cloexec(the_pipe[0], 1);
+ pa_fd_set_cloexec(the_pipe[1], 1);
+
+ api = m;
+ io_event = api->io_new(m, the_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
+
+ phase = PHASE_IDLE;
+
+ /* Install signal handler for SIGXCPU */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(SIGXCPU, &sa, &sigaction_prev) < 0) {
+ pa_cpu_limit_done();
+ return -1;
+ }
+
+ installed = 1;
+
+ reset_cpu_time(CPUTIME_INTERVAL_SOFT);
+
+ return 0;
+}
+
+/* Shutdown CPU load limiter */
+void pa_cpu_limit_done(void) {
+ int r;
+
+ if (io_event) {
+ assert(api);
+ api->io_free(io_event);
+ io_event = NULL;
+ api = NULL;
+ }
+
+ if (the_pipe[0] >= 0)
+ close(the_pipe[0]);
+ if (the_pipe[1] >= 0)
+ close(the_pipe[1]);
+ the_pipe[0] = the_pipe[1] = -1;
+
+ if (installed) {
+ r = sigaction(SIGXCPU, &sigaction_prev, NULL);
+ assert(r >= 0);
+ installed = 0;
+ }
+}
+
+#else /* HAVE_SIGXCPU */
+
+int pa_cpu_limit_init(PA_GCC_UNUSED pa_mainloop_api *m) {
+ return 0;
+}
+
+void pa_cpu_limit_done(void) {
+}
+
+#endif
diff --git a/src/daemon/cpulimit.h b/src/daemon/cpulimit.h
new file mode 100644
index 00000000..f3c5534d
--- /dev/null
+++ b/src/daemon/cpulimit.h
@@ -0,0 +1,34 @@
+#ifndef foocpulimithfoo
+#define foocpulimithfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/mainloop-api.h>
+
+/* This kills the polypaudio process if it eats more than 70% of the
+ * CPU time. This is build around setrlimit() and SIGXCPU. It is handy
+ * in case of using SCHED_FIFO which may freeze the whole machine */
+
+int pa_cpu_limit_init(pa_mainloop_api *m);
+void pa_cpu_limit_done(void);
+
+#endif
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
new file mode 100644
index 00000000..8fe3c4cc
--- /dev/null
+++ b/src/daemon/daemon-conf.c
@@ -0,0 +1,297 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include "daemon-conf.h"
+#include <polypcore/util.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/strbuf.h>
+#include <polypcore/conf-parser.h>
+#include <polypcore/resampler.h>
+
+#ifndef DEFAULT_CONFIG_DIR
+# ifndef OS_IS_WIN32
+# define DEFAULT_CONFIG_DIR "/etc/polypaudio"
+# else
+# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%"
+# endif
+#endif
+
+#ifndef OS_IS_WIN32
+# define PATH_SEP "/"
+#else
+# define PATH_SEP "\\"
+#endif
+
+#define DEFAULT_SCRIPT_FILE DEFAULT_CONFIG_DIR PATH_SEP "default.pa"
+#define DEFAULT_SCRIPT_FILE_USER ".polypaudio" PATH_SEP "default.pa"
+#define DEFAULT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "daemon.conf"
+#define DEFAULT_CONFIG_FILE_USER ".polypaudio" PATH_SEP "daemon.conf"
+
+#define ENV_SCRIPT_FILE "POLYP_SCRIPT"
+#define ENV_CONFIG_FILE "POLYP_CONFIG"
+#define ENV_DL_SEARCH_PATH "POLYP_DLPATH"
+
+static const pa_daemon_conf default_conf = {
+ .cmd = PA_CMD_DAEMON,
+ .daemonize = 0,
+ .fail = 1,
+ .high_priority = 0,
+ .disallow_module_loading = 0,
+ .exit_idle_time = -1,
+ .module_idle_time = 20,
+ .scache_idle_time = 20,
+ .auto_log_target = 1,
+ .script_commands = NULL,
+ .dl_search_path = NULL,
+ .default_script_file = NULL,
+ .log_target = PA_LOG_SYSLOG,
+ .log_level = PA_LOG_NOTICE,
+ .resample_method = PA_RESAMPLER_SRC_SINC_FASTEST,
+ .config_file = NULL,
+ .use_pid_file = 1
+};
+
+pa_daemon_conf* pa_daemon_conf_new(void) {
+ FILE *f;
+ pa_daemon_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
+
+ if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file)))
+ fclose(f);
+
+#ifdef DLSEARCHPATH
+ c->dl_search_path = pa_xstrdup(DLSEARCHPATH);
+#endif
+ return c;
+}
+
+void pa_daemon_conf_free(pa_daemon_conf *c) {
+ assert(c);
+ pa_xfree(c->script_commands);
+ pa_xfree(c->dl_search_path);
+ pa_xfree(c->default_script_file);
+ pa_xfree(c->config_file);
+ pa_xfree(c);
+}
+
+int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) {
+ assert(c && string);
+
+ if (!strcmp(string, "auto"))
+ c->auto_log_target = 1;
+ else if (!strcmp(string, "syslog")) {
+ c->auto_log_target = 0;
+ c->log_target = PA_LOG_SYSLOG;
+ } else if (!strcmp(string, "stderr")) {
+ c->auto_log_target = 0;
+ c->log_target = PA_LOG_STDERR;
+ } else
+ return -1;
+
+ return 0;
+}
+
+int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) {
+ uint32_t u;
+ assert(c && string);
+
+ if (pa_atou(string, &u) >= 0) {
+ if (u >= PA_LOG_LEVEL_MAX)
+ return -1;
+
+ c->log_level = (pa_log_level_t) u;
+ } else if (pa_startswith(string, "debug"))
+ c->log_level = PA_LOG_DEBUG;
+ else if (pa_startswith(string, "info"))
+ c->log_level = PA_LOG_INFO;
+ else if (pa_startswith(string, "notice"))
+ c->log_level = PA_LOG_NOTICE;
+ else if (pa_startswith(string, "warn"))
+ c->log_level = PA_LOG_WARN;
+ else if (pa_startswith(string, "err"))
+ c->log_level = PA_LOG_ERROR;
+ else
+ return -1;
+
+ return 0;
+}
+
+int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {
+ int m;
+ assert(c && string);
+
+ if ((m = pa_parse_resample_method(string)) < 0)
+ return -1;
+
+ c->resample_method = m;
+ return 0;
+}
+
+static int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ pa_daemon_conf *c = data;
+ assert(filename && lvalue && rvalue && data);
+
+ if (pa_daemon_conf_set_log_target(c, rvalue) < 0) {
+ pa_log(__FILE__": [%s:%u] Invalid log target '%s'.\n", filename, line, rvalue);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int parse_log_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ pa_daemon_conf *c = data;
+ assert(filename && lvalue && rvalue && data);
+
+ if (pa_daemon_conf_set_log_level(c, rvalue) < 0) {
+ pa_log(__FILE__": [%s:%u] Invalid log level '%s'.\n", filename, line, rvalue);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ pa_daemon_conf *c = data;
+ assert(filename && lvalue && rvalue && data);
+
+ if (pa_daemon_conf_set_resample_method(c, rvalue) < 0) {
+ pa_log(__FILE__": [%s:%u] Inavalid resample method '%s'.\n", filename, line, rvalue);
+ return -1;
+ }
+
+ return 0;
+}
+
+int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
+ int r = -1;
+ FILE *f = NULL;
+
+ pa_config_item table[] = {
+ { "daemonize", pa_config_parse_bool, NULL },
+ { "fail", pa_config_parse_bool, NULL },
+ { "high-priority", pa_config_parse_bool, NULL },
+ { "disallow-module-loading", pa_config_parse_bool, NULL },
+ { "exit-idle-time", pa_config_parse_int, NULL },
+ { "module-idle-time", pa_config_parse_int, NULL },
+ { "scache-idle-time", pa_config_parse_int, NULL },
+ { "dl-search-path", pa_config_parse_string, NULL },
+ { "default-script-file", pa_config_parse_string, NULL },
+ { "log-target", parse_log_target, NULL },
+ { "log-level", parse_log_level, NULL },
+ { "verbose", parse_log_level, NULL },
+ { "resample-method", parse_resample_method, NULL },
+ { "use-pid-file", pa_config_parse_bool, NULL },
+ { NULL, NULL, NULL },
+ };
+
+ table[0].data = &c->daemonize;
+ table[1].data = &c->fail;
+ table[2].data = &c->high_priority;
+ table[3].data = &c->disallow_module_loading;
+ table[4].data = &c->exit_idle_time;
+ table[5].data = &c->module_idle_time;
+ table[6].data = &c->scache_idle_time;
+ table[7].data = &c->dl_search_path;
+ table[8].data = &c->default_script_file;
+ table[9].data = c;
+ table[10].data = c;
+ table[11].data = c;
+ table[12].data = c;
+ table[13].data = &c->use_pid_file;
+
+ pa_xfree(c->config_file);
+ c->config_file = NULL;
+
+ f = filename ?
+ fopen(c->config_file = pa_xstrdup(filename), "r") :
+ pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
+
+ if (!f && errno != ENOENT) {
+ pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno));
+ goto finish;
+ }
+
+ r = f ? pa_config_parse(c->config_file, f, table, NULL) : 0;
+
+finish:
+ if (f)
+ fclose(f);
+
+ return r;
+}
+
+int pa_daemon_conf_env(pa_daemon_conf *c) {
+ char *e;
+
+ if ((e = getenv(ENV_DL_SEARCH_PATH))) {
+ pa_xfree(c->dl_search_path);
+ c->dl_search_path = pa_xstrdup(e);
+ }
+ if ((e = getenv(ENV_SCRIPT_FILE))) {
+ pa_xfree(c->default_script_file);
+ c->default_script_file = pa_xstrdup(e);
+ }
+
+ return 0;
+}
+
+static const char* const log_level_to_string[] = {
+ [PA_LOG_DEBUG] = "debug",
+ [PA_LOG_INFO] = "info",
+ [PA_LOG_NOTICE] = "notice",
+ [PA_LOG_WARN] = "warning",
+ [PA_LOG_ERROR] = "error"
+};
+
+char *pa_daemon_conf_dump(pa_daemon_conf *c) {
+ pa_strbuf *s = pa_strbuf_new();
+
+ if (c->config_file)
+ pa_strbuf_printf(s, "### Read from configuration file: %s ###\n", c->config_file);
+
+ assert(c->log_level <= PA_LOG_LEVEL_MAX);
+
+ pa_strbuf_printf(s, "daemonize = %i\n", !!c->daemonize);
+ pa_strbuf_printf(s, "fail = %i\n", !!c->fail);
+ pa_strbuf_printf(s, "high-priority = %i\n", !!c->high_priority);
+ pa_strbuf_printf(s, "disallow-module-loading = %i\n", !!c->disallow_module_loading);
+ pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
+ pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
+ pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
+ pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : "");
+ pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file);
+ pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
+ pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
+ pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
+ pa_strbuf_printf(s, "use-pid-file = %i\n", c->use_pid_file);
+
+ return pa_strbuf_tostring_free(s);
+}
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
new file mode 100644
index 00000000..d5131419
--- /dev/null
+++ b/src/daemon/daemon-conf.h
@@ -0,0 +1,80 @@
+#ifndef foodaemonconfhfoo
+#define foodaemonconfhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polypcore/log.h>
+
+/* The actual command to execute */
+typedef enum pa_daemon_conf_cmd {
+ PA_CMD_DAEMON, /* the default */
+ PA_CMD_HELP,
+ PA_CMD_VERSION,
+ PA_CMD_DUMP_CONF,
+ PA_CMD_DUMP_MODULES,
+ PA_CMD_KILL,
+ PA_CMD_CHECK
+} pa_daemon_conf_cmd_t;
+
+/* A structure containing configuration data for the Polypaudio server . */
+typedef struct pa_daemon_conf {
+ pa_daemon_conf_cmd_t cmd;
+ int daemonize,
+ fail,
+ high_priority,
+ disallow_module_loading,
+ exit_idle_time,
+ module_idle_time,
+ scache_idle_time,
+ auto_log_target,
+ use_pid_file;
+ char *script_commands, *dl_search_path, *default_script_file;
+ pa_log_target_t log_target;
+ pa_log_level_t log_level;
+ int resample_method;
+ char *config_file;
+} pa_daemon_conf;
+
+/* Allocate a new structure and fill it with sane defaults */
+pa_daemon_conf* pa_daemon_conf_new(void);
+void pa_daemon_conf_free(pa_daemon_conf*c);
+
+/* Load configuration data from the specified file overwriting the
+ * current settings in *c. If filename is NULL load the default daemon
+ * configuration file */
+int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename);
+
+/* Pretty print the current configuration data of the daemon. The
+ * returned string has to be freed manually. The output of this
+ * function may be parsed with pa_daemon_conf_load(). */
+char *pa_daemon_conf_dump(pa_daemon_conf *c);
+
+/* Load the configuration data from the process' environment
+ * overwriting the current settings in *c. */
+int pa_daemon_conf_env(pa_daemon_conf *c);
+
+/* Set these configuration variables in the structure by passing a string */
+int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
+
+#endif
diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c
new file mode 100644
index 00000000..8d8eb0b9
--- /dev/null
+++ b/src/daemon/dumpmodules.c
@@ -0,0 +1,99 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <getopt.h>
+#include <assert.h>
+#include <stdio.h>
+#include <ltdl.h>
+
+#include "dumpmodules.h"
+#include <polypcore/modinfo.h>
+#include <polypcore/util.h>
+
+#define PREFIX "module-"
+
+static void short_info(const char *name, PA_GCC_UNUSED const char *path, pa_modinfo *i) {
+ assert(name && i);
+ printf("%-40s%s\n", name, i->description ? i->description : "n/a");
+}
+
+static void long_info(const char *name, const char *path, pa_modinfo *i) {
+ static int nl = 0;
+ assert(name && i);
+
+ if (nl)
+ printf("\n");
+
+ nl = 1;
+
+ printf("Name: %s\n", name);
+
+ if (!i->description && !i->version && !i->author && !i->usage)
+ printf("No module information available\n");
+ else {
+ if (i->version)
+ printf("Version: %s\n", i->version);
+ if (i->description)
+ printf("Description: %s\n", i->description);
+ if (i->author)
+ printf("Author: %s\n", i->author);
+ if (i->usage)
+ printf("Usage: %s\n", i->usage);
+ }
+
+ if (path)
+ printf("Path: %s\n", path);
+}
+
+static void show_info(const char *name, const char *path, void (*info)(const char *name, const char *path, pa_modinfo*i)) {
+ pa_modinfo *i;
+
+ if ((i = pa_modinfo_get_by_name(path ? path : name))) {
+ info(name, path, i);
+ pa_modinfo_free(i);
+ }
+}
+
+static int callback(const char *path, lt_ptr data) {
+ const char *e;
+ pa_daemon_conf *c = (data);
+
+ e = pa_path_get_filename(path);
+
+ if (strlen(e) > sizeof(PREFIX)-1 && !strncmp(e, PREFIX, sizeof(PREFIX)-1))
+ show_info(e, path, c->log_level >= PA_LOG_INFO ? long_info : short_info);
+
+ return 0;
+}
+
+void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) {
+ if (argc > 0) {
+ int i;
+ for (i = 0; i < argc; i++)
+ show_info(argv[i], NULL, long_info);
+ } else
+ lt_dlforeachfile(NULL, callback, c);
+}
diff --git a/src/daemon/dumpmodules.h b/src/daemon/dumpmodules.h
new file mode 100644
index 00000000..968d2de9
--- /dev/null
+++ b/src/daemon/dumpmodules.h
@@ -0,0 +1,31 @@
+#ifndef foodumpmoduleshfoo
+#define foodumpmoduleshfoo
+
+/* $Id*/
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "daemon-conf.h"
+
+/* Dump all available modules to STDOUT. If argc > 0 print information
+ * about the modules specified in argv[] instead. */
+void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]);
+
+#endif
diff --git a/src/daemon/main.c b/src/daemon/main.c
new file mode 100644
index 00000000..6be83d8c
--- /dev/null
+++ b/src/daemon/main.c
@@ -0,0 +1,472 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stddef.h>
+#include <assert.h>
+#include <ltdl.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <liboil/liboil.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_LIBWRAP
+#include <syslog.h>
+#include <tcpd.h>
+#endif
+
+#include <polypcore/winsock.h>
+
+#include <polypcore/core.h>
+#include <polypcore/memblock.h>
+#include <polyp/mainloop.h>
+#include <polypcore/module.h>
+#include <polyp/mainloop-signal.h>
+#include "cmdline.h"
+#include <polypcore/cli-command.h>
+#include <polypcore/util.h>
+#include <polypcore/sioman.h>
+#include <polypcore/xmalloc.h>
+#include "cpulimit.h"
+#include <polypcore/log.h>
+#include "daemon-conf.h"
+#include "dumpmodules.h"
+#include "caps.h"
+#include <polypcore/cli-text.h>
+#include <polypcore/pid.h>
+#include <polypcore/namereg.h>
+
+#ifdef HAVE_LIBWRAP
+/* Only one instance of these variables */
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif
+
+#ifdef OS_IS_WIN32
+
+static void message_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
+ MSG msg;
+
+ while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ if (msg.message == WM_QUIT)
+ raise(SIGTERM);
+ else {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+}
+
+#endif
+
+static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, int sig, void *userdata) {
+ pa_log_info(__FILE__": Got signal %s.\n", pa_strsignal(sig));
+
+ switch (sig) {
+#ifdef SIGUSR1
+ case SIGUSR1:
+ pa_module_load(userdata, "module-cli", NULL);
+ break;
+#endif
+
+#ifdef SIGUSR2
+ case SIGUSR2:
+ pa_module_load(userdata, "module-cli-protocol-unix", NULL);
+ break;
+#endif
+
+#ifdef SIGHUP
+ case SIGHUP: {
+ char *c = pa_full_status_string(userdata);
+ pa_log_notice(c);
+ pa_xfree(c);
+ return;
+ }
+#endif
+
+ case SIGINT:
+ case SIGTERM:
+ default:
+ pa_log_info(__FILE__": Exiting.\n");
+ m->quit(m, 1);
+ break;
+ }
+}
+
+static void close_pipe(int p[2]) {
+ if (p[0] != -1)
+ close(p[0]);
+ if (p[1] != -1)
+ close(p[1]);
+ p[0] = p[1] = -1;
+}
+
+int main(int argc, char *argv[]) {
+ pa_core *c;
+ pa_strbuf *buf = NULL;
+ pa_daemon_conf *conf;
+ pa_mainloop *mainloop;
+
+ char *s;
+ int r, retval = 1, d = 0;
+ int daemon_pipe[2] = { -1, -1 };
+ int suid_root;
+ int valid_pid_file = 0;
+
+#ifdef HAVE_GETUID
+ gid_t gid = (gid_t) -1;
+#endif
+
+#ifdef OS_IS_WIN32
+ pa_defer_event *defer;
+#endif
+
+ pa_limit_caps();
+
+#ifdef HAVE_GETUID
+ suid_root = getuid() != 0 && geteuid() == 0;
+
+ if (suid_root && (pa_uid_in_group("realtime", &gid) <= 0 || gid >= 1000)) {
+ pa_log_warn(__FILE__": WARNING: called SUID root, but not in group 'realtime'.\n");
+ pa_drop_root();
+ }
+#else
+ suid_root = 0;
+#endif
+
+ LTDL_SET_PRELOADED_SYMBOLS();
+
+ r = lt_dlinit();
+ assert(r == 0);
+
+#ifdef OS_IS_WIN32
+ {
+ WSADATA data;
+ WSAStartup(MAKEWORD(2, 0), &data);
+ }
+#endif
+
+ pa_log_set_ident("polypaudio");
+
+ conf = pa_daemon_conf_new();
+
+ if (pa_daemon_conf_load(conf, NULL) < 0)
+ goto finish;
+
+ if (pa_daemon_conf_env(conf) < 0)
+ goto finish;
+
+ if (pa_cmdline_parse(conf, argc, argv, &d) < 0) {
+ pa_log(__FILE__": failed to parse command line.\n");
+ goto finish;
+ }
+
+ pa_log_set_maximal_level(conf->log_level);
+ pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
+
+ if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
+ pa_raise_priority();
+
+ pa_drop_caps();
+
+ if (suid_root)
+ pa_drop_root();
+
+ if (conf->dl_search_path)
+ lt_dlsetsearchpath(conf->dl_search_path);
+
+ switch (conf->cmd) {
+ case PA_CMD_DUMP_MODULES:
+ pa_dump_modules(conf, argc-d, argv+d);
+ retval = 0;
+ goto finish;
+
+ case PA_CMD_DUMP_CONF: {
+ s = pa_daemon_conf_dump(conf);
+ fputs(s, stdout);
+ pa_xfree(s);
+ retval = 0;
+ goto finish;
+ }
+
+ case PA_CMD_HELP :
+ pa_cmdline_help(argv[0]);
+ retval = 0;
+ goto finish;
+
+ case PA_CMD_VERSION :
+ printf(PACKAGE_NAME" "PACKAGE_VERSION"\n");
+ retval = 0;
+ goto finish;
+
+ case PA_CMD_CHECK: {
+ pid_t pid;
+
+ if (pa_pid_file_check_running(&pid) < 0) {
+ pa_log_info(__FILE__": daemon not running\n");
+ } else {
+ pa_log_info(__FILE__": daemon running as PID %u\n", pid);
+ retval = 0;
+ }
+
+ goto finish;
+
+ }
+ case PA_CMD_KILL:
+
+ if (pa_pid_file_kill(SIGINT, NULL) < 0)
+ pa_log(__FILE__": failed to kill daemon.\n");
+ else
+ retval = 0;
+
+ goto finish;
+
+ default:
+ assert(conf->cmd == PA_CMD_DAEMON);
+ }
+
+ if (conf->daemonize) {
+ pid_t child;
+ int tty_fd;
+
+ if (pa_stdio_acquire() < 0) {
+ pa_log(__FILE__": failed to acquire stdio.\n");
+ goto finish;
+ }
+
+#ifdef HAVE_FORK
+ if (pipe(daemon_pipe) < 0) {
+ pa_log(__FILE__": failed to create pipe.\n");
+ goto finish;
+ }
+
+ if ((child = fork()) < 0) {
+ pa_log(__FILE__": fork() failed: %s\n", strerror(errno));
+ goto finish;
+ }
+
+ if (child != 0) {
+ /* Father */
+
+ close(daemon_pipe[1]);
+ daemon_pipe[1] = -1;
+
+ if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval)) != sizeof(retval)) {
+ pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+ retval = 1;
+ }
+
+ if (retval)
+ pa_log(__FILE__": daemon startup failed.\n");
+ else
+ pa_log_info(__FILE__": daemon startup successful.\n");
+
+ goto finish;
+ }
+
+ close(daemon_pipe[0]);
+ daemon_pipe[0] = -1;
+#endif
+
+ if (conf->auto_log_target)
+ pa_log_set_target(PA_LOG_SYSLOG, NULL);
+
+#ifdef HAVE_SETSID
+ setsid();
+#endif
+#ifdef HAVE_SETPGID
+ setpgid(0,0);
+#endif
+
+#ifndef OS_IS_WIN32
+ close(0);
+ close(1);
+ close(2);
+
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_WRONLY);
+ open("/dev/null", O_WRONLY);
+#else
+ FreeConsole();
+#endif
+
+#ifdef SIGTTOU
+ signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+
+#ifdef TIOCNOTTY
+ if ((tty_fd = open("/dev/tty", O_RDWR)) >= 0) {
+ ioctl(tty_fd, TIOCNOTTY, (char*) 0);
+ close(tty_fd);
+ }
+#endif
+ }
+
+ chdir("/");
+
+ if (conf->use_pid_file) {
+ if (pa_pid_file_create() < 0) {
+ pa_log(__FILE__": pa_pid_file_create() failed.\n");
+#ifdef HAVE_FORK
+ if (conf->daemonize)
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
+#endif
+ goto finish;
+ }
+
+ valid_pid_file = 1;
+ }
+
+ mainloop = pa_mainloop_new();
+ assert(mainloop);
+
+ c = pa_core_new(pa_mainloop_get_api(mainloop));
+ assert(c);
+
+ r = pa_signal_init(pa_mainloop_get_api(mainloop));
+ assert(r == 0);
+ pa_signal_new(SIGINT, signal_callback, c);
+ pa_signal_new(SIGTERM, signal_callback, c);
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+#ifdef OS_IS_WIN32
+ defer = pa_mainloop_get_api(mainloop)->defer_new(pa_mainloop_get_api(mainloop), message_cb, NULL);
+ assert(defer);
+#endif
+
+ if (conf->daemonize)
+ c->running_as_daemon = 1;
+
+#ifdef SIGUSR1
+ pa_signal_new(SIGUSR1, signal_callback, c);
+#endif
+#ifdef SIGUSR2
+ pa_signal_new(SIGUSR2, signal_callback, c);
+#endif
+#ifdef SIGHUP
+ pa_signal_new(SIGHUP, signal_callback, c);
+#endif
+
+ oil_init();
+
+ r = pa_cpu_limit_init(pa_mainloop_get_api(mainloop));
+ assert(r == 0);
+
+ buf = pa_strbuf_new();
+ assert(buf);
+ if (conf->default_script_file)
+ r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail);
+
+ if (r >= 0)
+ r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
+ pa_log(s = pa_strbuf_tostring_free(buf));
+ pa_xfree(s);
+
+ if (r < 0 && conf->fail) {
+ pa_log(__FILE__": failed to initialize daemon.\n");
+#ifdef HAVE_FORK
+ if (conf->daemonize)
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
+#endif
+ } else if (!c->modules || pa_idxset_size(c->modules) == 0) {
+ pa_log(__FILE__": daemon startup without any loaded modules, refusing to work.\n");
+#ifdef HAVE_FORK
+ if (conf->daemonize)
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
+#endif
+ } else {
+
+ retval = 0;
+#ifdef HAVE_FORK
+ if (conf->daemonize)
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
+#endif
+
+ c->disallow_module_loading = conf->disallow_module_loading;
+ c->exit_idle_time = conf->exit_idle_time;
+ c->module_idle_time = conf->module_idle_time;
+ c->scache_idle_time = conf->scache_idle_time;
+ c->resample_method = conf->resample_method;
+
+ if (c->default_sink_name &&
+ pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) {
+ pa_log_error("%s : Fatal error. Default sink name (%s) does not exist in name register.\n", __FILE__, c->default_sink_name);
+ retval = 1;
+ } else {
+ pa_log_info(__FILE__": Daemon startup complete.\n");
+ if (pa_mainloop_run(mainloop, &retval) < 0)
+ retval = 1;
+ pa_log_info(__FILE__": Daemon shutdown initiated.\n");
+ }
+ }
+
+#ifdef OS_IS_WIN32
+ pa_mainloop_get_api(mainloop)->defer_free(defer);
+#endif
+
+ pa_core_free(c);
+
+ pa_cpu_limit_done();
+ pa_signal_done();
+ pa_mainloop_free(mainloop);
+
+ pa_log_info(__FILE__": Daemon terminated.\n");
+
+finish:
+
+ if (conf)
+ pa_daemon_conf_free(conf);
+
+ if (valid_pid_file)
+ pa_pid_file_remove();
+
+ close_pipe(daemon_pipe);
+
+#ifdef OS_IS_WIN32
+ WSACleanup();
+#endif
+
+ lt_dlexit();
+
+ return retval;
+}
diff --git a/src/default.pa.in b/src/default.pa.in
new file mode 100755
index 00000000..3aaeeaf0
--- /dev/null
+++ b/src/default.pa.in
@@ -0,0 +1,66 @@
+#!@POLYPAUDIO_BINARY@ -nF
+
+#
+# This file is part of polypaudio.
+#
+# polypaudio 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 of the License, or
+# (at your option) any later version.
+#
+# polypaudio 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with polypaudio; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+
+# Load audio drivers statically
+
+#load-module module-alsa-sink
+# load-module module-alsa-source device=plughw:1,0
+load-module module-oss device="/dev/dsp" sink_name=output source_name=input
+#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
+load-module module-null-sink
+#load-module module-pipe-sink
+
+# Load audio drivers automatically on access
+
+#add-autoload-sink output module-oss device="/dev/dsp" sink_name=output source_name=input
+#add-autoload-source input module-oss device="/dev/dsp" sink_name=output source_name=input
+#add-autoload-sink output module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
+#add-autoload-source input module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
+#add-autoload-sink output module-alsa-sink sink_name=output
+#add-autoload-source input module-alsa-source source_name=input
+
+# Load several protocols
+load-module module-esound-protocol-unix
+#load-module module-esound-protocol-tcp
+load-module module-native-protocol-unix
+#load-module module-simple-protocol-tcp
+#load-module module-cli-protocol-unix
+
+# Load the CLI module
+load-module module-cli
+
+# Make some devices default
+set-default-sink output
+set-default-source input
+
+.nofail
+
+# Load something to the sample cache
+load-sample x11-bell /usr/share/sounds/KDE_Notify.wav
+load-sample-dir-lazy /usr/share/sounds/*.wav
+
+# Load X11 bell module
+load-module module-x11-bell sample=x11-bell sink=output
+
+# Publish connection data in the X11 root window
+load-module module-x11-publish
+
+#load-module module-pipe-source
+#load-module module-pipe-sink
diff --git a/src/default.pa.win32 b/src/default.pa.win32
new file mode 100644
index 00000000..3478adab
--- /dev/null
+++ b/src/default.pa.win32
@@ -0,0 +1,43 @@
+#
+# This file is part of polypaudio.
+#
+# polypaudio 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 of the License, or
+# (at your option) any later version.
+#
+# polypaudio 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with polypaudio; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+
+# Load audio drivers statically
+
+load-module module-waveout sink_name=output source_name=input
+load-module module-null-sink
+
+# Load audio drivers automatically on access
+
+#add-autoload-sink output module-waveout sink_name=output source_name=input
+#add-autoload-source input module-waveout sink_name=output source_name=input
+
+# Load several protocols
+#load-module module-esound-protocol-tcp
+#load-module module-native-protocol-tcp
+#load-module module-simple-protocol-tcp
+#load-module module-cli-protocol-tcp
+
+# Make some devices default
+set-default-sink output
+set-default-source input
+
+.nofail
+
+# Load something to the sample cache
+load-sample x11-bell %WINDIR%\Media\ding.wav
+load-sample-dir-lazy %WINDIR%\Media\*.wav
diff --git a/src/depmod.py b/src/depmod.py
new file mode 100755
index 00000000..7bb223b1
--- /dev/null
+++ b/src/depmod.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+# $Id$
+#
+# This file is part of polypaudio.
+#
+# polypaudio 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 of the License, or
+# (at your option) any later version.
+#
+# polypaudio 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with polypaudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+import sys, os, string
+
+exported_symbols = {}
+imported_symbols = {}
+
+for fn in sys.argv[1:]:
+ f = os.popen("nm '%s'" % fn, "r")
+
+ imported_symbols[fn] = []
+
+ for line in f:
+ sym_address = line[:7].strip()
+ sym_type = line[9].strip()
+ sym_name = line[11:].strip()
+
+ if sym_name in ('_fini', '_init'):
+ continue
+
+ if sym_type in ('T', 'B', 'R', 'D' 'G', 'S', 'D'):
+ if exported_symbols.has_key(sym_name):
+ sys.stderr.write("CONFLICT: %s defined in both '%s' and '%s'.\n" % (sym_name, fn, exported_symbols[sym_name]))
+ else:
+ exported_symbols[sym_name] = fn
+ elif sym_type in ('U',):
+ if sym_name[:3] == 'pa_':
+ imported_symbols[fn].append(sym_name)
+
+ f.close()
+
+dependencies = {}
+unresolved_symbols = {}
+
+for fn in imported_symbols:
+ dependencies[fn] = []
+
+ for sym in imported_symbols[fn]:
+ if exported_symbols.has_key(sym):
+ if exported_symbols[sym] not in dependencies[fn]:
+ dependencies[fn].append(exported_symbols[sym])
+ else:
+ if unresolved_symbols.has_key(sym):
+ unresolved_symbols[sym].append(fn)
+ else:
+ unresolved_symbols[sym] = [fn]
+
+for sym, files in unresolved_symbols.iteritems():
+ print "WARNING: Unresolved symbol '%s' in %s" % (sym, `files`)
+
+k = dependencies.keys()
+k.sort()
+for fn in k:
+ dependencies[fn].sort()
+ print "%s: %s" % (fn, string.join(dependencies[fn], " "))
diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c
new file mode 100644
index 00000000..48e90e9f
--- /dev/null
+++ b/src/modules/module-alsa-sink.c
@@ -0,0 +1,292 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#else
+#include "poll.h"
+#endif
+
+#include <asoundlib.h>
+
+#include <polypcore/core.h>
+#include <polypcore/module.h>
+#include <polypcore/memchunk.h>
+#include <polypcore/sink.h>
+#include <polypcore/modargs.h>
+#include <polypcore/util.h>
+#include <polypcore/sample-util.h>
+#include <polypcore/alsa-util.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+#include "module-alsa-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("ALSA Sink")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("sink_name=<name for the sink> device=<ALSA device> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size>")
+
+struct userdata {
+ snd_pcm_t *pcm_handle;
+ pa_sink *sink;
+ pa_io_event **io_events;
+ unsigned n_io_events;
+
+ size_t frame_size, fragment_size;
+ pa_memchunk memchunk, silence;
+ pa_module *module;
+};
+
+static const char* const valid_modargs[] = {
+ "device",
+ "sink_name",
+ "format",
+ "channels",
+ "rate",
+ "fragments",
+ "fragment_size",
+ NULL
+};
+
+#define DEFAULT_SINK_NAME "alsa_output"
+#define DEFAULT_DEVICE "default"
+
+static void update_usage(struct userdata *u) {
+ pa_module_set_used(u->module,
+ (u->sink ? pa_idxset_size(u->sink->inputs) : 0) +
+ (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0));
+}
+
+static void xrun_recovery(struct userdata *u) {
+ assert(u);
+
+ pa_log(__FILE__": *** ALSA-XRUN (playback) ***\n");
+
+ if (snd_pcm_prepare(u->pcm_handle) < 0)
+ pa_log(__FILE__": snd_pcm_prepare() failed\n");
+}
+
+static void do_write(struct userdata *u) {
+ assert(u);
+
+ update_usage(u);
+
+ for (;;) {
+ pa_memchunk *memchunk = NULL;
+ snd_pcm_sframes_t frames;
+
+ if (u->memchunk.memblock)
+ memchunk = &u->memchunk;
+ else {
+ if (pa_sink_render(u->sink, u->fragment_size, &u->memchunk) < 0)
+ memchunk = &u->silence;
+ else
+ memchunk = &u->memchunk;
+ }
+
+ assert(memchunk->memblock && memchunk->memblock->data && memchunk->length && memchunk->memblock->length && (memchunk->length % u->frame_size) == 0);
+
+ if ((frames = snd_pcm_writei(u->pcm_handle, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) {
+ if (frames == -EAGAIN)
+ return;
+
+ if (frames == -EPIPE) {
+ xrun_recovery(u);
+ continue;
+ }
+
+ pa_log(__FILE__": snd_pcm_writei() failed\n");
+ return;
+ }
+
+ if (memchunk == &u->memchunk) {
+ size_t l = frames * u->frame_size;
+ memchunk->index += l;
+ memchunk->length -= l;
+
+ if (memchunk->length == 0) {
+ pa_memblock_unref(memchunk->memblock);
+ memchunk->memblock = NULL;
+ memchunk->index = memchunk->length = 0;
+ }
+ }
+
+ break;
+ }
+}
+
+static void io_callback(pa_mainloop_api*a, pa_io_event *e, PA_GCC_UNUSED int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+ struct userdata *u = userdata;
+ assert(u && a && e);
+
+ if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
+ xrun_recovery(u);
+
+ do_write(u);
+}
+
+static pa_usec_t sink_get_latency_cb(pa_sink *s) {
+ pa_usec_t r = 0;
+ struct userdata *u = s->userdata;
+ snd_pcm_sframes_t frames;
+ assert(s && u && u->sink);
+
+ if (snd_pcm_delay(u->pcm_handle, &frames) < 0) {
+ pa_log(__FILE__": failed to get delay\n");
+ s->get_latency = NULL;
+ return 0;
+ }
+
+ if (frames < 0)
+ frames = 0;
+
+ r += pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
+
+ if (u->memchunk.memblock)
+ r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
+
+ return r;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_modargs *ma = NULL;
+ int ret = -1;
+ struct userdata *u = NULL;
+ const char *dev;
+ pa_sample_spec ss;
+ uint32_t periods, fragsize;
+ snd_pcm_uframes_t period_size;
+ size_t frame_size;
+ int err;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log(__FILE__": failed to parse sample specification\n");
+ goto fail;
+ }
+ frame_size = pa_frame_size(&ss);
+
+ periods = 8;
+ fragsize = 1024;
+ if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) {
+ pa_log(__FILE__": failed to parse buffer metrics\n");
+ goto fail;
+ }
+ period_size = fragsize;
+
+ u = pa_xmalloc0(sizeof(struct userdata));
+ m->userdata = u;
+ u->module = m;
+
+ snd_config_update_free_global();
+ if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
+ pa_log(__FILE__": Error opening PCM device %s: %s\n", dev, snd_strerror(err));
+ goto fail;
+ }
+
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) {
+ pa_log(__FILE__": Failed to set hardware parameters: %s\n", snd_strerror(err));
+ goto fail;
+ }
+
+ u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL);
+ assert(u->sink);
+
+ u->sink->get_latency = sink_get_latency_cb;
+ u->sink->userdata = u;
+ pa_sink_set_owner(u->sink, m);
+ u->sink->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev);
+
+ if (pa_create_io_events(u->pcm_handle, c->mainloop, &u->io_events, &u->n_io_events, io_callback, u) < 0) {
+ pa_log(__FILE__": failed to obtain file descriptors\n");
+ goto fail;
+ }
+
+ u->frame_size = frame_size;
+ u->fragment_size = period_size;
+
+ pa_log_info(__FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size);
+
+ u->silence.memblock = pa_memblock_new(u->silence.length = u->fragment_size, c->memblock_stat);
+ assert(u->silence.memblock);
+ pa_silence_memblock(u->silence.memblock, &ss);
+ u->silence.index = 0;
+
+ u->memchunk.memblock = NULL;
+ u->memchunk.index = u->memchunk.length = 0;
+
+ ret = 0;
+
+finish:
+ if (ma)
+ pa_modargs_free(ma);
+
+ return ret;
+
+fail:
+
+ if (u)
+ pa__done(c, m);
+
+ goto finish;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sink) {
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+ }
+
+ if (u->io_events)
+ pa_free_io_events(c->mainloop, u->io_events, u->n_io_events);
+
+ if (u->pcm_handle) {
+ snd_pcm_drop(u->pcm_handle);
+ snd_pcm_close(u->pcm_handle);
+ }
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+ if (u->silence.memblock)
+ pa_memblock_unref(u->silence.memblock);
+
+ pa_xfree(u);
+}
+
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
new file mode 100644
index 00000000..f03e51ad
--- /dev/null
+++ b/src/modules/module-alsa-source.c
@@ -0,0 +1,278 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#else
+#include "poll.h"
+#endif
+
+#include <asoundlib.h>
+
+#include <polypcore/core.h>
+#include <polypcore/module.h>
+#include <polypcore/memchunk.h>
+#include <polypcore/sink.h>
+#include <polypcore/modargs.h>
+#include <polypcore/util.h>
+#include <polypcore/sample-util.h>
+#include <polypcore/alsa-util.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+#include "module-alsa-source-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("ALSA Source")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("source_name=<name for the source> device=<ALSA device> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size>")
+
+struct userdata {
+ snd_pcm_t *pcm_handle;
+ pa_source *source;
+ pa_io_event **io_events;
+ unsigned n_io_events;
+
+ size_t frame_size, fragment_size;
+ pa_memchunk memchunk;
+ pa_module *module;
+};
+
+static const char* const valid_modargs[] = {
+ "device",
+ "source_name",
+ "channels",
+ "rate",
+ "format",
+ "fragments",
+ "fragment_size",
+ NULL
+};
+
+#define DEFAULT_SOURCE_NAME "alsa_input"
+#define DEFAULT_DEVICE "hw:0,0"
+
+static void update_usage(struct userdata *u) {
+ pa_module_set_used(u->module,
+ (u->source ? pa_idxset_size(u->source->outputs) : 0));
+}
+
+static void xrun_recovery(struct userdata *u) {
+ assert(u);
+
+ pa_log(__FILE__": *** ALSA-XRUN (capture) ***\n");
+
+ if (snd_pcm_prepare(u->pcm_handle) < 0)
+ pa_log(__FILE__": snd_pcm_prepare() failed\n");
+}
+
+static void do_read(struct userdata *u) {
+ assert(u);
+
+ update_usage(u);
+
+ for (;;) {
+ pa_memchunk post_memchunk;
+ snd_pcm_sframes_t frames;
+ size_t l;
+
+ if (!u->memchunk.memblock) {
+ u->memchunk.memblock = pa_memblock_new(u->memchunk.length = u->fragment_size, u->source->core->memblock_stat);
+ u->memchunk.index = 0;
+ }
+
+ assert(u->memchunk.memblock && u->memchunk.memblock->data && u->memchunk.length && u->memchunk.memblock->length && (u->memchunk.length % u->frame_size) == 0);
+
+ if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) {
+ if (frames == -EAGAIN)
+ return;
+
+ if (frames == -EPIPE) {
+ xrun_recovery(u);
+ continue;
+ }
+
+ pa_log(__FILE__": snd_pcm_readi() failed: %s\n", strerror(-frames));
+ return;
+ }
+
+ l = frames * u->frame_size;
+
+ post_memchunk = u->memchunk;
+ post_memchunk.length = l;
+
+ pa_source_post(u->source, &post_memchunk);
+
+ u->memchunk.index += l;
+ u->memchunk.length -= l;
+
+ if (u->memchunk.length == 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ u->memchunk.memblock = NULL;
+ u->memchunk.index = u->memchunk.length = 0;
+ }
+
+ break;
+ }
+}
+
+static void io_callback(pa_mainloop_api*a, pa_io_event *e, PA_GCC_UNUSED int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+ struct userdata *u = userdata;
+ assert(u && a && e);
+
+ if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
+ xrun_recovery(u);
+
+ do_read(u);
+}
+
+static pa_usec_t source_get_latency_cb(pa_source *s) {
+ struct userdata *u = s->userdata;
+ snd_pcm_sframes_t frames;
+ assert(s && u && u->source);
+
+ if (snd_pcm_delay(u->pcm_handle, &frames) < 0) {
+ pa_log(__FILE__": failed to get delay\n");
+ s->get_latency = NULL;
+ return 0;
+ }
+
+ return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_modargs *ma = NULL;
+ int ret = -1;
+ struct userdata *u = NULL;
+ const char *dev;
+ pa_sample_spec ss;
+ unsigned periods, fragsize;
+ snd_pcm_uframes_t period_size;
+ size_t frame_size;
+ int err;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log(__FILE__": failed to parse sample specification\n");
+ goto fail;
+ }
+ frame_size = pa_frame_size(&ss);
+
+ periods = 12;
+ fragsize = 1024;
+ if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) {
+ pa_log(__FILE__": failed to parse buffer metrics\n");
+ goto fail;
+ }
+ period_size = fragsize;
+
+ u = pa_xmalloc0(sizeof(struct userdata));
+ m->userdata = u;
+ u->module = m;
+
+ snd_config_update_free_global();
+ if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
+ pa_log(__FILE__": Error opening PCM device %s: %s\n", dev, snd_strerror(err));
+ goto fail;
+ }
+
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) {
+ pa_log(__FILE__": Failed to set hardware parameters: %s\n", snd_strerror(err));
+ goto fail;
+ }
+
+ u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL);
+ assert(u->source);
+
+ u->source->userdata = u;
+ u->source->get_latency = source_get_latency_cb;
+ pa_source_set_owner(u->source, m);
+ u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev);
+
+ if (pa_create_io_events(u->pcm_handle, c->mainloop, &u->io_events, &u->n_io_events, io_callback, u) < 0) {
+ pa_log(__FILE__": failed to obtain file descriptors\n");
+ goto fail;
+ }
+
+ u->frame_size = frame_size;
+ u->fragment_size = period_size;
+
+ pa_log(__FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size);
+
+ u->memchunk.memblock = NULL;
+ u->memchunk.index = u->memchunk.length = 0;
+
+ snd_pcm_start(u->pcm_handle);
+
+ ret = 0;
+
+finish:
+ if (ma)
+ pa_modargs_free(ma);
+
+ return ret;
+
+fail:
+
+ if (u)
+ pa__done(c, m);
+
+ goto finish;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->source) {
+ pa_source_disconnect(u->source);
+ pa_source_unref(u->source);
+ }
+
+ if (u->io_events)
+ pa_free_io_events(c->mainloop, u->io_events, u->n_io_events);
+
+ if (u->pcm_handle) {
+ snd_pcm_drop(u->pcm_handle);
+ snd_pcm_close(u->pcm_handle);
+ }
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+
+ pa_xfree(u);
+}
+
diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c
new file mode 100644
index 00000000..c782ff8d
--- /dev/null
+++ b/src/modules/module-cli.c
@@ -0,0 +1,88 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include <polypcore/module.h>
+#include <polypcore/iochannel.h>
+#include <polypcore/cli.h>
+#include <polypcore/sioman.h>
+#include <polypcore/log.h>
+
+#include "module-cli-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Command line interface")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("No arguments")
+
+static void eof_cb(pa_cli*c, void *userdata) {
+ pa_module *m = userdata;
+ assert(c && m);
+
+ pa_module_unload_request(m);
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_iochannel *io;
+ assert(c && m);
+
+ if (c->running_as_daemon) {
+ pa_log_info(__FILE__": Running as daemon so won't load this module.\n");
+ return 0;
+ }
+
+ if (m->argument) {
+ pa_log(__FILE__": module doesn't accept arguments.\n");
+ return -1;
+ }
+
+ if (pa_stdio_acquire() < 0) {
+ pa_log(__FILE__": STDIN/STDUSE already in use.\n");
+ return -1;
+ }
+
+ io = pa_iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO);
+ assert(io);
+ pa_iochannel_set_noclose(io, 1);
+
+ m->userdata = pa_cli_new(c, io, m);
+ assert(m->userdata);
+
+ pa_cli_set_eof_callback(m->userdata, eof_cb, m);
+
+ return 0;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ assert(c && m);
+
+ if (c->running_as_daemon == 0) {
+ pa_cli_free(m->userdata);
+ pa_stdio_release();
+ }
+}
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
new file mode 100644
index 00000000..aabb8f28
--- /dev/null
+++ b/src/modules/module-combine.c
@@ -0,0 +1,394 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdio.h>
+
+#include <polypcore/module.h>
+#include <polypcore/llist.h>
+#include <polypcore/sink.h>
+#include <polypcore/sink-input.h>
+#include <polypcore/memblockq.h>
+#include <polypcore/log.h>
+#include <polypcore/util.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/modargs.h>
+#include <polypcore/namereg.h>
+
+#include "module-combine-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Combine multiple sinks to one")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("sink_name=<name for the sink> master=<master sink> slaves=<slave sinks> adjust_time=<seconds> resample_method=<method>")
+
+#define DEFAULT_SINK_NAME "combined"
+#define MEMBLOCKQ_MAXLENGTH (1024*170)
+#define RENDER_SIZE (1024*10)
+
+#define DEFAULT_ADJUST_TIME 20
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ "master",
+ "slaves",
+ "adjust_time",
+ "resample_method",
+ NULL
+};
+
+struct output {
+ struct userdata *userdata;
+ pa_sink_input *sink_input;
+ size_t counter;
+ pa_memblockq *memblockq;
+ pa_usec_t total_latency;
+ PA_LLIST_FIELDS(struct output);
+};
+
+struct userdata {
+ pa_module *module;
+ pa_core *core;
+ pa_sink *sink;
+ unsigned n_outputs;
+ struct output *master;
+ pa_time_event *time_event;
+ uint32_t adjust_time;
+
+ PA_LLIST_HEAD(struct output, outputs);
+};
+
+static void output_free(struct output *o);
+static void clear_up(struct userdata *u);
+
+static void update_usage(struct userdata *u) {
+ pa_module_set_used(u->module,
+ (u->sink ? pa_idxset_size(u->sink->inputs) : 0) +
+ (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0));
+}
+
+
+static void adjust_rates(struct userdata *u) {
+ struct output *o;
+ pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency;
+ uint32_t base_rate;
+ assert(u && u->sink);
+
+ for (o = u->outputs; o; o = o->next) {
+ uint32_t sink_latency = o->sink_input->sink ? pa_sink_get_latency(o->sink_input->sink) : 0;
+
+ o->total_latency = sink_latency + pa_sink_input_get_latency(o->sink_input);
+
+ if (sink_latency > max_sink_latency)
+ max_sink_latency = sink_latency;
+
+ if (o->total_latency < min_total_latency)
+ min_total_latency = o->total_latency;
+ }
+
+ assert(min_total_latency != (pa_usec_t) -1);
+
+ target_latency = max_sink_latency > min_total_latency ? max_sink_latency : min_total_latency;
+
+ pa_log_info(__FILE__": [%s] target latency is %0.0f usec.\n", u->sink->name, (float) target_latency);
+
+ base_rate = u->sink->sample_spec.rate;
+
+ for (o = u->outputs; o; o = o->next) {
+ uint32_t r = base_rate;
+
+ if (o->total_latency < target_latency)
+ r -= (uint32_t) (((((double) target_latency - o->total_latency))/u->adjust_time)*r/ 1000000);
+ else if (o->total_latency > target_latency)
+ r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/ 1000000);
+
+ if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1))
+ pa_log_warn(__FILE__": [%s] sample rates too different, not adjusting (%u vs. %u).\n", o->sink_input->name, base_rate, r);
+ else {
+ pa_log_info(__FILE__": [%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.\n", o->sink_input->name, r, (double) r / base_rate, (float) o->total_latency);
+ pa_sink_input_set_rate(o->sink_input, r);
+ }
+ }
+}
+
+static void request_memblock(struct userdata *u) {
+ pa_memchunk chunk;
+ struct output *o;
+ assert(u && u->sink);
+
+ update_usage(u);
+
+ if (pa_sink_render(u->sink, RENDER_SIZE, &chunk) < 0)
+ return;
+
+ for (o = u->outputs; o; o = o->next)
+ pa_memblockq_push_align(o->memblockq, &chunk, 0);
+
+ pa_memblock_unref(chunk.memblock);
+}
+
+static void time_callback(pa_mainloop_api*a, pa_time_event* e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+ struct timeval n;
+ assert(u && a && u->time_event == e);
+
+ adjust_rates(u);
+
+ pa_gettimeofday(&n);
+ n.tv_sec += u->adjust_time;
+ u->sink->core->mainloop->time_restart(e, &n);
+}
+
+static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
+ struct output *o = i->userdata;
+ assert(i && o && o->sink_input && chunk);
+
+ if (pa_memblockq_peek(o->memblockq, chunk) >= 0)
+ return 0;
+
+ /* Try harder */
+ request_memblock(o->userdata);
+
+ return pa_memblockq_peek(o->memblockq, chunk);
+}
+
+static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
+ struct output *o = i->userdata;
+ assert(i && o && o->sink_input && chunk && length);
+
+ pa_memblockq_drop(o->memblockq, chunk, length);
+ o->counter += length;
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+ struct output *o = i->userdata;
+ assert(i && o && o->sink_input);
+ pa_module_unload_request(o->userdata->module);
+ clear_up(o->userdata);
+}
+
+static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
+ struct output *o = i->userdata;
+ assert(i && o && o->sink_input);
+
+ return pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &i->sample_spec);
+}
+
+static pa_usec_t sink_get_latency_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ assert(s && u && u->sink && u->master);
+
+ return pa_sink_input_get_latency(u->master->sink_input);
+}
+
+static struct output *output_new(struct userdata *u, pa_sink *sink, int resample_method) {
+ struct output *o = NULL;
+ char t[256];
+ assert(u && sink && u->sink);
+
+ o = pa_xmalloc(sizeof(struct output));
+ o->userdata = u;
+
+ o->counter = 0;
+ o->memblockq = pa_memblockq_new(MEMBLOCKQ_MAXLENGTH, MEMBLOCKQ_MAXLENGTH, pa_frame_size(&u->sink->sample_spec), 0, 0, sink->core->memblock_stat);
+
+ snprintf(t, sizeof(t), "%s: output #%u", u->sink->name, u->n_outputs+1);
+ if (!(o->sink_input = pa_sink_input_new(sink, __FILE__, t, &u->sink->sample_spec, &u->sink->channel_map, 1, resample_method)))
+ goto fail;
+
+ o->sink_input->get_latency = sink_input_get_latency_cb;
+ o->sink_input->peek = sink_input_peek_cb;
+ o->sink_input->drop = sink_input_drop_cb;
+ o->sink_input->kill = sink_input_kill_cb;
+ o->sink_input->userdata = o;
+ o->sink_input->owner = u->module;
+
+ PA_LLIST_PREPEND(struct output, u->outputs, o);
+ u->n_outputs++;
+ return o;
+
+fail:
+
+ if (o) {
+ if (o->sink_input) {
+ pa_sink_input_disconnect(o->sink_input);
+ pa_sink_input_unref(o->sink_input);
+ }
+
+ if (o->memblockq)
+ pa_memblockq_free(o->memblockq);
+
+ pa_xfree(o);
+ }
+
+ return NULL;
+}
+
+static void output_free(struct output *o) {
+ assert(o);
+ PA_LLIST_REMOVE(struct output, o->userdata->outputs, o);
+ o->userdata->n_outputs--;
+ pa_memblockq_free(o->memblockq);
+ pa_sink_input_disconnect(o->sink_input);
+ pa_sink_input_unref(o->sink_input);
+ pa_xfree(o);
+}
+
+static void clear_up(struct userdata *u) {
+ struct output *o;
+ assert(u);
+
+ if (u->time_event) {
+ u->core->mainloop->time_free(u->time_event);
+ u->time_event = NULL;
+ }
+
+ while ((o = u->outputs))
+ output_free(o);
+
+ u->master = NULL;
+
+ if (u->sink) {
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+ u->sink = NULL;
+ }
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ pa_modargs *ma = NULL;
+ const char *master_name, *slaves, *rm;
+ pa_sink *master_sink;
+ char *n = NULL;
+ const char*split_state;
+ struct timeval tv;
+ int resample_method = -1;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ if ((rm = pa_modargs_get_value(ma, "resample_method", NULL))) {
+ if ((resample_method = pa_parse_resample_method(rm)) < 0) {
+ pa_log(__FILE__": invalid resample method '%s'\n", rm);
+ goto fail;
+ }
+ }
+
+ u = pa_xmalloc(sizeof(struct userdata));
+ m->userdata = u;
+ u->sink = NULL;
+ u->n_outputs = 0;
+ u->master = NULL;
+ u->module = m;
+ u->core = c;
+ u->time_event = NULL;
+ u->adjust_time = DEFAULT_ADJUST_TIME;
+ PA_LLIST_HEAD_INIT(struct output, u->outputs);
+
+ if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) {
+ pa_log(__FILE__": failed to parse adjust_time value\n");
+ goto fail;
+ }
+
+ if (!(master_name = pa_modargs_get_value(ma, "master", NULL)) || !(slaves = pa_modargs_get_value(ma, "slaves", NULL))) {
+ pa_log(__FILE__": no master or slave sinks specified\n");
+ goto fail;
+ }
+
+ if (!(master_sink = pa_namereg_get(c, master_name, PA_NAMEREG_SINK, 1))) {
+ pa_log(__FILE__": invalid master sink '%s'\n", master_name);
+ goto fail;
+ }
+
+ if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &master_sink->sample_spec, &master_sink->channel_map))) {
+ pa_log(__FILE__": failed to create sink\n");
+ goto fail;
+ }
+
+ pa_sink_set_owner(u->sink, m);
+ u->sink->description = pa_sprintf_malloc("Combined sink");
+ u->sink->get_latency = sink_get_latency_cb;
+ u->sink->userdata = u;
+
+ if (!(u->master = output_new(u, master_sink, resample_method))) {
+ pa_log(__FILE__": failed to create master sink input on sink '%s'.\n", u->sink->name);
+ goto fail;
+ }
+
+ split_state = NULL;
+ while ((n = pa_split(slaves, ",", &split_state))) {
+ pa_sink *slave_sink;
+
+ if (!(slave_sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+ pa_log(__FILE__": invalid slave sink '%s'\n", n);
+ goto fail;
+ }
+
+ pa_xfree(n);
+
+ if (!output_new(u, slave_sink, resample_method)) {
+ pa_log(__FILE__": failed to create slave sink input on sink '%s'.\n", slave_sink->name);
+ goto fail;
+ }
+ }
+
+ if (u->n_outputs <= 1)
+ pa_log_warn(__FILE__": WARNING: no slave sinks specified.\n");
+
+ if (u->adjust_time > 0) {
+ pa_gettimeofday(&tv);
+ tv.tv_sec += u->adjust_time;
+ u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u);
+ }
+
+ pa_modargs_free(ma);
+ return 0;
+
+fail:
+ pa_xfree(n);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(c, m);
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ clear_up(u);
+ pa_xfree(u);
+}
+
+
diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4
new file mode 100644
index 00000000..8f10d659
--- /dev/null
+++ b/src/modules/module-defs.h.m4
@@ -0,0 +1,29 @@
+dnl $Id$
+changecom(`/*', `*/')dnl
+define(`module_name', patsubst(patsubst(patsubst(fname, `-symdef.h$'), `^.*/'), `[^0-9a-zA-Z]', `_'))dnl
+define(`c_symbol', patsubst(module_name, `[^0-9a-zA-Z]', `_'))dnl
+define(`c_macro', patsubst(module_name, `[^0-9a-zA-Z]', `'))dnl
+define(`incmacro', `foo'c_macro`symdeffoo')dnl
+define(`gen_symbol', `#define $1 'module_name`_LTX_$1')dnl
+#ifndef incmacro
+#define incmacro
+
+#include <polypcore/core.h>
+#include <polypcore/module.h>
+
+gen_symbol(pa__init)
+gen_symbol(pa__done)
+gen_symbol(pa__get_author)
+gen_symbol(pa__get_description)
+gen_symbol(pa__get_usage)
+gen_symbol(pa__get_version)
+
+int pa__init(struct pa_core *c, struct pa_module*m);
+void pa__done(struct pa_core *c, struct pa_module*m);
+
+const char* pa__get_author(void);
+const char* pa__get_description(void);
+const char* pa__get_usage(void);
+const char* pa__get_version(void);
+
+#endif
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
new file mode 100644
index 00000000..e325b22c
--- /dev/null
+++ b/src/modules/module-detect.c
@@ -0,0 +1,227 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <polypcore/module.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+#include "module-detect-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("just-one=<boolean>")
+
+static const char *endswith(const char *haystack, const char *needle) {
+ size_t l, m;
+ const char *p;
+
+ if ((l = strlen(haystack)) < (m = strlen(needle)))
+ return NULL;
+
+ if (strcmp(p = haystack + l - m, needle))
+ return NULL;
+
+ return p;
+}
+
+#ifdef HAVE_ALSA
+static int detect_alsa(pa_core *c, int just_one) {
+ FILE *f;
+ int n = 0, n_sink = 0, n_source = 0;
+
+ if (!(f = fopen("/proc/asound/devices", "r"))) {
+
+ if (errno != ENOENT)
+ pa_log_error(__FILE__": open(\"/proc/asound/devices\") failed: %s\n", strerror(errno));
+
+ return -1;
+ }
+
+ while (!feof(f)) {
+ char line[64], args[64];
+ unsigned device, subdevice;
+ int is_sink;
+
+ if (!fgets(line, sizeof(line), f))
+ break;
+
+ line[strcspn(line, "\r\n")] = 0;
+
+ if (endswith(line, "digital audio playback"))
+ is_sink = 1;
+ else if (endswith(line, "digital audio capture"))
+ is_sink = 0;
+ else
+ continue;
+
+ if (just_one && is_sink && n_sink >= 1)
+ continue;
+
+ if (just_one && !is_sink && n_source >= 1)
+ continue;
+
+ if (sscanf(line, " %*i: [%u- %u]: ", &device, &subdevice) != 2)
+ continue;
+
+ /* Only one sink per device */
+ if (subdevice != 0)
+ continue;
+
+ snprintf(args, sizeof(args), "device=hw:%u,0", device);
+ if (!pa_module_load(c, is_sink ? "module-alsa-sink" : "module-alsa-source", args))
+ continue;
+
+ n++;
+
+ if (is_sink)
+ n_sink++;
+ else
+ n_source++;
+ }
+
+ fclose(f);
+
+ return n;
+}
+#endif
+
+#ifdef HAVE_OSS
+static int detect_oss(pa_core *c, int just_one) {
+ FILE *f;
+ int n = 0, b = 0;
+
+ if (!(f = fopen("/dev/sndstat", "r")) &&
+ !(f = fopen("/proc/sndstat", "r")) &&
+ !(f = fopen("/proc/asound/oss/sndstat", "r"))) {
+
+ if (errno != ENOENT)
+ pa_log_error(__FILE__": failed to open OSS sndstat device: %s\n", strerror(errno));
+
+ return -1;
+ }
+
+ while (!feof(f)) {
+ char line[64], args[64];
+ unsigned device;
+
+ if (!fgets(line, sizeof(line), f))
+ break;
+
+ line[strcspn(line, "\r\n")] = 0;
+
+ if (!b) {
+ b = strcmp(line, "Audio devices:") == 0;
+ continue;
+ }
+
+ if (line[0] == 0)
+ break;
+
+ if (sscanf(line, "%u: ", &device) != 1)
+ continue;
+
+ if (device == 0)
+ snprintf(args, sizeof(args), "device=/dev/dsp");
+ else
+ snprintf(args, sizeof(args), "device=/dev/dsp%u", device);
+
+ if (!pa_module_load(c, "module-oss", args))
+ continue;
+
+ n++;
+
+ if (just_one)
+ break;
+ }
+
+ fclose(f);
+ return n;
+}
+#endif
+
+int pa__init(pa_core *c, pa_module*m) {
+ int just_one = 0, n = 0;
+ pa_modargs *ma;
+
+ static const char* const valid_modargs[] = {
+ "just-one",
+ NULL
+ };
+
+ assert(c);
+ assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": Failed to parse module arguments\n");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "just-one", &just_one) < 0) {
+ pa_log(__FILE__": just_one= expects a boolean argument.\n");
+ goto fail;
+ }
+
+#if HAVE_ALSA
+ if ((n = detect_alsa(c, just_one)) <= 0)
+#endif
+#if HAVE_OSS
+ if ((n = detect_oss(c, just_one)) <= 0)
+#endif
+ {
+ pa_log_warn(__FILE__": failed to detect any sound hardware.\n");
+ goto fail;
+ }
+
+ pa_log_info(__FILE__": loaded %i modules.\n", n);
+
+ /* We were successful and can unload ourselves now. */
+ pa_module_unload_request(m);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+
+void pa__done(pa_core *c, pa_module*m) {
+ /* NOP */
+}
+
diff --git a/src/modules/module-esound-compat-spawnfd.c b/src/modules/module-esound-compat-spawnfd.c
new file mode 100644
index 00000000..5c656be9
--- /dev/null
+++ b/src/modules/module-esound-compat-spawnfd.c
@@ -0,0 +1,81 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include <polypcore/module.h>
+#include <polypcore/modargs.h>
+#include <polypcore/util.h>
+#include <polypcore/log.h>
+
+#include "module-esound-compat-spawnfd-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnfd emulation")
+PA_MODULE_USAGE("fd=<file descriptor>")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+
+static const char* const valid_modargs[] = {
+ "fd",
+ NULL,
+};
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_modargs *ma = NULL;
+ int ret = -1, fd = -1;
+ char x = 1;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
+ pa_modargs_get_value_s32(ma, "fd", &fd) < 0 ||
+ fd < 0) {
+ pa_log(__FILE__": Failed to parse module arguments\n");
+ goto finish;
+ }
+
+ if (pa_loop_write(fd, &x, sizeof(x)) != sizeof(x))
+ pa_log(__FILE__": WARNING: write(%u, 1, 1) failed: %s\n", fd, strerror(errno));
+
+ close(fd);
+
+ pa_module_unload_request(m);
+
+ ret = 0;
+
+finish:
+ if (ma)
+ pa_modargs_free(ma);
+
+ return ret;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ assert(c && m);
+}
+
+
diff --git a/src/modules/module-esound-compat-spawnpid.c b/src/modules/module-esound-compat-spawnpid.c
new file mode 100644
index 00000000..5daa1297
--- /dev/null
+++ b/src/modules/module-esound-compat-spawnpid.c
@@ -0,0 +1,79 @@
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <polypcore/module.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/log.h>
+
+#include "module-esound-compat-spawnpid-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnpid emulation")
+PA_MODULE_USAGE("pid=<process id>")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+
+static const char* const valid_modargs[] = {
+ "pid",
+ NULL,
+};
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_modargs *ma = NULL;
+ int ret = -1;
+ uint32_t pid = 0;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
+ pa_modargs_get_value_u32(ma, "pid", &pid) < 0 ||
+ !pid) {
+ pa_log(__FILE__": Failed to parse module arguments\n");
+ goto finish;
+ }
+
+ if (kill(pid, SIGUSR1) < 0)
+ pa_log(__FILE__": WARNING: kill(%u) failed: %s\n", pid, strerror(errno));
+
+ pa_module_unload_request(m);
+
+ ret = 0;
+
+finish:
+ if (ma)
+ pa_modargs_free(ma);
+
+ return ret;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ assert(c && m);
+}
+
+
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
new file mode 100644
index 00000000..4f724811
--- /dev/null
+++ b/src/modules/module-esound-sink.c
@@ -0,0 +1,427 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <polypcore/iochannel.h>
+#include <polypcore/sink.h>
+#include <polypcore/module.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+#include <polypcore/socket-client.h>
+#include <polypcore/esound.h>
+#include <polypcore/authkey.h>
+
+#include "module-esound-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("ESOUND Sink")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("sink_name=<name for the sink> server=<address> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate>")
+
+#define DEFAULT_SINK_NAME "esound_output"
+
+struct userdata {
+ pa_core *core;
+
+ pa_sink *sink;
+ pa_iochannel *io;
+ pa_socket_client *client;
+
+ pa_defer_event *defer_event;
+
+ pa_memchunk memchunk;
+ pa_module *module;
+
+ void *write_data;
+ size_t write_length, write_index;
+
+ void *read_data;
+ size_t read_length, read_index;
+
+ enum { STATE_AUTH, STATE_LATENCY, STATE_RUNNING, STATE_DEAD } state;
+
+ pa_usec_t latency;
+
+ esd_format_t format;
+ int32_t rate;
+};
+
+static const char* const valid_modargs[] = {
+ "server",
+ "cookie",
+ "rate",
+ "format",
+ "channels",
+ "sink_name",
+ NULL
+};
+
+static void cancel(struct userdata *u) {
+ assert(u);
+
+ u->state = STATE_DEAD;
+
+ if (u->io) {
+ pa_iochannel_free(u->io);
+ u->io = NULL;
+ }
+
+ if (u->defer_event) {
+ u->core->mainloop->defer_free(u->defer_event);
+ u->defer_event = NULL;
+ }
+
+ if (u->sink) {
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+ u->sink = NULL;
+ }
+
+ if (u->module) {
+ pa_module_unload_request(u->module);
+ u->module = NULL;
+ }
+}
+
+static int do_write(struct userdata *u) {
+ ssize_t r;
+ assert(u);
+
+ if (!pa_iochannel_is_writable(u->io))
+ return 0;
+
+ if (u->write_data) {
+ assert(u->write_index < u->write_length);
+
+ if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) <= 0) {
+ pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ u->write_index += r;
+ assert(u->write_index <= u->write_length);
+
+ if (u->write_index == u->write_length) {
+ free(u->write_data);
+ u->write_data = NULL;
+ u->write_index = u->write_length = 0;
+ }
+ } else if (u->state == STATE_RUNNING) {
+ pa_module_set_used(u->module, pa_idxset_size(u->sink->inputs) + pa_idxset_size(u->sink->monitor_source->outputs));
+
+ if (!u->memchunk.length)
+ if (pa_sink_render(u->sink, 8192, &u->memchunk) < 0)
+ return 0;
+
+ assert(u->memchunk.memblock && u->memchunk.length);
+
+ if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
+ pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ u->memchunk.index += r;
+ u->memchunk.length -= r;
+
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ u->memchunk.memblock = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static int handle_response(struct userdata *u) {
+ assert(u);
+
+ switch (u->state) {
+ case STATE_AUTH:
+ assert(u->read_length == sizeof(int32_t));
+
+ /* Process auth data */
+ if (!*(int32_t*) u->read_data) {
+ pa_log(__FILE__": Authentication failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ /* Request latency data */
+ assert(!u->write_data);
+ *(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY;
+
+ u->write_index = 0;
+ u->state = STATE_LATENCY;
+
+ /* Space for next response */
+ assert(u->read_length >= sizeof(int32_t));
+ u->read_index = 0;
+ u->read_length = sizeof(int32_t);
+
+ break;
+
+ case STATE_LATENCY: {
+ int32_t *p;
+ assert(u->read_length == sizeof(int32_t));
+
+ /* Process latency info */
+ u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100);
+ if (u->latency > 10000000) {
+ pa_log(__FILE__": WARNING! Invalid latency information received from server\n");
+ u->latency = 0;
+ }
+
+ /* Create stream */
+ assert(!u->write_data);
+ p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX);
+ *(p++) = ESD_PROTO_STREAM_PLAY;
+ *(p++) = u->format;
+ *(p++) = u->rate;
+ pa_strlcpy((char*) p, "Polypaudio Tunnel", ESD_NAME_MAX);
+
+ u->write_index = 0;
+ u->state = STATE_RUNNING;
+
+ /* Don't read any further */
+ pa_xfree(u->read_data);
+ u->read_data = NULL;
+ u->read_index = u->read_length = 0;
+
+ break;
+ }
+
+ default:
+ abort();
+ }
+
+ return 0;
+}
+
+static int do_read(struct userdata *u) {
+ assert(u);
+
+ if (!pa_iochannel_is_readable(u->io))
+ return 0;
+
+ if (u->state == STATE_AUTH || u->state == STATE_LATENCY) {
+ ssize_t r;
+
+ if (!u->read_data)
+ return 0;
+
+ assert(u->read_index < u->read_length);
+
+ if ((r = pa_iochannel_read(u->io, (uint8_t*) u->read_data + u->read_index, u->read_length - u->read_index)) <= 0) {
+ pa_log(__FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
+ cancel(u);
+ return -1;
+ }
+
+ u->read_index += r;
+ assert(u->read_index <= u->read_length);
+
+ if (u->read_index == u->read_length)
+ return handle_response(u);
+ }
+
+ return 0;
+}
+
+static void do_work(struct userdata *u) {
+ assert(u);
+
+ u->core->mainloop->defer_enable(u->defer_event, 0);
+
+ if (do_read(u) < 0 || do_write(u) < 0)
+ cancel(u);
+}
+
+static void notify_cb(pa_sink*s) {
+ struct userdata *u = s->userdata;
+ assert(s && u);
+
+ if (pa_iochannel_is_writable(u->io))
+ u->core->mainloop->defer_enable(u->defer_event, 1);
+}
+
+static pa_usec_t get_latency_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ assert(s && u);
+
+ return
+ u->latency +
+ (u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0);
+}
+
+static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_work(u);
+}
+
+static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_work(u);
+}
+
+static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_socket_client_unref(u->client);
+ u->client = NULL;
+
+ if (!io) {
+ pa_log(__FILE__": connection failed: %s\n", strerror(errno));
+ cancel(u);
+ return;
+ }
+
+ u->io = io;
+ pa_iochannel_set_callback(u->io, io_callback, u);
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct userdata *u = NULL;
+ const char *p;
+ pa_sample_spec ss;
+ pa_modargs *ma = NULL;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log(__FILE__": invalid sample format specification\n");
+ goto fail;
+ }
+
+ if ((ss.format != PA_SAMPLE_U8 && ss.format != PA_SAMPLE_S16NE) ||
+ (ss.channels > 2)) {
+ pa_log(__FILE__": esound sample type support is limited to mono/stereo and U8 or S16NE sample data\n");
+ goto fail;
+ }
+
+ u = pa_xmalloc0(sizeof(struct userdata));
+ u->core = c;
+ u->module = m;
+ m->userdata = u;
+ u->format =
+ (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) |
+ (ss.channels == 2 ? ESD_STEREO : ESD_MONO);
+ u->rate = ss.rate;
+ u->sink = NULL;
+ u->client = NULL;
+ u->io = NULL;
+ u->read_data = u->write_data = NULL;
+ u->read_index = u->write_index = u->read_length = u->write_length = 0;
+ u->state = STATE_AUTH;
+ u->latency = 0;
+
+ if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
+ pa_log(__FILE__": failed to create sink.\n");
+ goto fail;
+ }
+
+ if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", ESD_UNIX_SOCKET_NAME), ESD_DEFAULT_PORT))) {
+ pa_log(__FILE__": failed to connect to server.\n");
+ goto fail;
+ }
+ pa_socket_client_set_callback(u->client, on_connection, u);
+
+ /* Prepare the initial request */
+ u->write_data = pa_xmalloc(u->write_length = ESD_KEY_LEN + sizeof(int32_t));
+ if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", ".esd_auth"), u->write_data, ESD_KEY_LEN) < 0) {
+ pa_log(__FILE__": failed to load cookie\n");
+ goto fail;
+ }
+ *(int32_t*) ((uint8_t*) u->write_data + ESD_KEY_LEN) = ESD_ENDIAN_KEY;
+
+ /* Reserve space for the response */
+ u->read_data = pa_xmalloc(u->read_length = sizeof(int32_t));
+
+ u->sink->notify = notify_cb;
+ u->sink->get_latency = get_latency_cb;
+ u->sink->userdata = u;
+ pa_sink_set_owner(u->sink, m);
+ u->sink->description = pa_sprintf_malloc("Esound sink '%s'", p);
+
+ u->memchunk.memblock = NULL;
+ u->memchunk.length = 0;
+
+ u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u);
+ c->mainloop->defer_enable(u->defer_event, 0);
+
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(c, m);
+
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ u->module = NULL;
+ cancel(u);
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+
+ if (u->client)
+ pa_socket_client_unref(u->client);
+
+ pa_xfree(u->read_data);
+ pa_xfree(u->write_data);
+
+ pa_xfree(u);
+}
+
+
+
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
new file mode 100644
index 00000000..ea8a2bd2
--- /dev/null
+++ b/src/modules/module-lirc.c
@@ -0,0 +1,241 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <lirc/lirc_client.h>
+#include <stdlib.h>
+
+#include <polypcore/module.h>
+#include <polypcore/log.h>
+#include <polypcore/namereg.h>
+#include <polypcore/sink.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/modargs.h>
+
+#include "module-lirc-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("LIRC volume control")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("config=<config file> sink=<sink name> appname=<lirc application name>")
+
+static const char* const valid_modargs[] = {
+ "config",
+ "sink",
+ "appname",
+ NULL,
+};
+
+struct userdata {
+ int lirc_fd;
+ pa_io_event *io;
+ struct lirc_config *config;
+ char *sink_name;
+ pa_module *module;
+ float mute_toggle_save;
+};
+
+static int lirc_in_use = 0;
+
+static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) {
+ struct userdata *u = userdata;
+ char *name = NULL, *code = NULL;
+ assert(io);
+ assert(u);
+
+ if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
+ pa_log(__FILE__": lost connection to LIRC daemon.\n");
+ goto fail;
+ }
+
+ if (events & PA_IO_EVENT_INPUT) {
+ char *c;
+
+ if (lirc_nextcode(&code) != 0 || !code) {
+ pa_log(__FILE__": lirc_nextcode() failed.\n");
+ goto fail;
+ }
+
+ c = pa_xstrdup(code);
+ c[strcspn(c, "\n\r")] = 0;
+ pa_log_debug(__FILE__": raw IR code '%s'\n", c);
+ pa_xfree(c);
+
+ while (lirc_code2char(u->config, code, &name) == 0 && name) {
+ enum { INVALID, UP, DOWN, MUTE, RESET, MUTE_TOGGLE } volchange = INVALID;
+
+ pa_log_info(__FILE__": translated IR code '%s'\n", name);
+
+ if (strcasecmp(name, "volume-up") == 0)
+ volchange = UP;
+ else if (strcasecmp(name, "volume-down") == 0)
+ volchange = DOWN;
+ else if (strcasecmp(name, "mute") == 0)
+ volchange = MUTE;
+ else if (strcasecmp(name, "mute-toggle") == 0)
+ volchange = MUTE_TOGGLE;
+ else if (strcasecmp(name, "reset") == 0)
+ volchange = RESET;
+
+ if (volchange == INVALID)
+ pa_log_warn(__FILE__": recieved unknown IR code '%s'\n", name);
+ else {
+ pa_sink *s;
+
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
+ pa_log(__FILE__": failed to get sink '%s'\n", u->sink_name);
+ else {
+ pa_volume_t v = pa_cvolume_avg(pa_sink_get_volume(s, PA_MIXER_HARDWARE));
+ pa_cvolume cv;
+#define DELTA (PA_VOLUME_NORM/20)
+
+ switch (volchange) {
+ case UP:
+ v += PA_VOLUME_NORM/20;
+ break;
+
+ case DOWN:
+ if (v > DELTA)
+ v -= DELTA;
+ else
+ v = PA_VOLUME_MUTED;
+
+ break;
+
+ case MUTE:
+ v = PA_VOLUME_MUTED;
+ break;
+
+ case RESET:
+ v = PA_VOLUME_NORM;
+ break;
+
+ case MUTE_TOGGLE: {
+
+ if (v > 0) {
+ u->mute_toggle_save = v;
+ v = PA_VOLUME_MUTED;
+ } else
+ v = u->mute_toggle_save;
+ }
+ default:
+ ;
+ }
+
+ pa_cvolume_set(&cv, PA_CHANNELS_MAX, v);
+ pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv);
+ }
+ }
+ }
+ }
+
+ free(code);
+
+ return;
+
+fail:
+ u->module->core->mainloop->io_free(u->io);
+ u->io = NULL;
+
+ pa_module_unload_request(u->module);
+
+ free(code);
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ assert(c && m);
+
+ if (lirc_in_use) {
+ pa_log(__FILE__": module-lirc may no be loaded twice.\n");
+ return -1;
+ }
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": Failed to parse module arguments\n");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xmalloc(sizeof(struct userdata));
+ u->module = m;
+ u->io = NULL;
+ u->config = NULL;
+ u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+ u->lirc_fd = -1;
+ u->mute_toggle_save = 0;
+
+ if ((u->lirc_fd = lirc_init((char*) pa_modargs_get_value(ma, "appname", "polypaudio"), 1)) < 0) {
+ pa_log(__FILE__": lirc_init() failed.\n");
+ goto fail;
+ }
+
+ if (lirc_readconfig((char*) pa_modargs_get_value(ma, "config", NULL), &u->config, NULL) < 0) {
+ pa_log(__FILE__": lirc_readconfig() failed.\n");
+ goto fail;
+ }
+
+ u->io = c->mainloop->io_new(c->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
+
+ lirc_in_use = 1;
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(c, m);
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c);
+ assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->io)
+ m->core->mainloop->io_free(u->io);
+
+ if (u->config)
+ lirc_freeconfig(u->config);
+
+ if (u->lirc_fd >= 0)
+ lirc_deinit();
+
+ pa_xfree(u->sink_name);
+ pa_xfree(u);
+
+ lirc_in_use = 0;
+}
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
new file mode 100644
index 00000000..10ceb75e
--- /dev/null
+++ b/src/modules/module-match.c
@@ -0,0 +1,235 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <polypcore/module.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/log.h>
+#include <polypcore/subscribe.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/sink-input.h>
+
+#include "module-match-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Sink input matching module")
+PA_MODULE_USAGE("table=<filename>")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+
+#define WHITESPACE "\n\r \t"
+
+#ifndef DEFAULT_CONFIG_DIR
+#define DEFAULT_CONFIG_DIR "/etc/polypaudio"
+#endif
+
+#define DEFAULT_MATCH_TABLE_FILE DEFAULT_CONFIG_DIR"/match.table"
+#define DEFAULT_MATCH_TABLE_FILE_USER ".polypaudio/match.table"
+
+static const char* const valid_modargs[] = {
+ "table",
+ NULL,
+};
+
+struct rule {
+ regex_t regex;
+ pa_volume_t volume;
+ struct rule *next;
+};
+
+struct userdata {
+ struct rule *rules;
+ pa_subscription *subscription;
+};
+
+static int load_rules(struct userdata *u, const char *filename) {
+ FILE *f;
+ int n = 0;
+ int ret = -1;
+ struct rule *end = NULL;
+ char *fn = NULL;
+
+ f = filename ?
+ fopen(fn = pa_xstrdup(filename), "r") :
+ pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
+
+ if (!f) {
+ pa_log(__FILE__": failed to open file '%s': %s\n", fn, strerror(errno));
+ goto finish;
+ }
+
+ while (!feof(f)) {
+ char *d, *v;
+ pa_volume_t volume;
+ uint32_t k;
+ regex_t regex;
+ char ln[256];
+ struct rule *rule;
+
+ if (!fgets(ln, sizeof(ln), f))
+ break;
+
+ n++;
+
+ pa_strip_nl(ln);
+
+ if (ln[0] == '#' || !*ln )
+ continue;
+
+ d = ln+strcspn(ln, WHITESPACE);
+ v = d+strspn(d, WHITESPACE);
+
+
+ if (!*v) {
+ pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words\n", filename, n);
+ goto finish;
+ }
+
+ *d = 0;
+ if (pa_atou(v, &k) < 0) {
+ pa_log(__FILE__": [%s:%u] failed to parse volume\n", filename, n);
+ goto finish;
+ }
+
+ volume = (pa_volume_t) k;
+
+
+ if (regcomp(&regex, ln, REG_EXTENDED|REG_NOSUB) != 0) {
+ pa_log(__FILE__": [%s:%u] invalid regular expression\n", filename, n);
+ goto finish;
+ }
+
+ rule = pa_xmalloc(sizeof(struct rule));
+ rule->regex = regex;
+ rule->volume = volume;
+ rule->next = NULL;
+
+ if (end)
+ end->next = rule;
+ else
+ u->rules = rule;
+ end = rule;
+
+ *d = 0;
+ }
+
+ ret = 0;
+
+finish:
+ if (f)
+ fclose(f);
+
+ if (fn)
+ pa_xfree(fn);
+
+ return ret;
+}
+
+static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ struct userdata *u = userdata;
+ pa_sink_input *si;
+ struct rule *r;
+ assert(c && u);
+
+ if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW))
+ return;
+
+ if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
+ return;
+
+ if (!si->name)
+ return;
+
+ for (r = u->rules; r; r = r->next) {
+ if (!regexec(&r->regex, si->name, 0, NULL, 0)) {
+ pa_cvolume cv;
+ pa_log_debug(__FILE__": changing volume of sink input '%s' to 0x%03x\n", si->name, r->volume);
+ pa_cvolume_set(&cv, r->volume, si->sample_spec.channels);
+ pa_sink_input_set_volume(si, &cv);
+ }
+ }
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": Failed to parse module arguments\n");
+ goto fail;
+ }
+
+ u = pa_xmalloc(sizeof(struct userdata));
+ u->rules = NULL;
+ u->subscription = NULL;
+ m->userdata = u;
+
+ if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0)
+ goto fail;
+
+ u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u);
+
+ pa_modargs_free(ma);
+ return 0;
+
+fail:
+ pa__done(c, m);
+
+ if (ma)
+ pa_modargs_free(ma);
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata* u;
+ struct rule *r, *n;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->subscription)
+ pa_subscription_free(u->subscription);
+
+ for (r = u->rules; r; r = n) {
+ n = r->next;
+
+ regfree(&r->regex);
+ pa_xfree(r);
+ }
+
+ pa_xfree(u);
+}
+
+
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
new file mode 100644
index 00000000..b60f786d
--- /dev/null
+++ b/src/modules/module-mmkbd-evdev.c
@@ -0,0 +1,250 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <linux/input.h>
+
+#include <polypcore/module.h>
+#include <polypcore/log.h>
+#include <polypcore/namereg.h>
+#include <polypcore/sink.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/modargs.h>
+#include <polypcore/util.h>
+
+#include "module-mmkbd-evdev-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Multimedia keyboard support via Linux evdev")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("device=<evdev device> sink=<sink name>")
+
+#define DEFAULT_DEVICE "/dev/input/event0"
+
+/*
+ * This isn't defined in older kernel headers and there is no way of
+ * detecting it.
+ */
+struct _input_id {
+ __u16 bustype;
+ __u16 vendor;
+ __u16 product;
+ __u16 version;
+};
+
+static const char* const valid_modargs[] = {
+ "device",
+ "sink",
+ NULL,
+};
+
+struct userdata {
+ int fd;
+ pa_io_event *io;
+ char *sink_name;
+ pa_module *module;
+ float mute_toggle_save;
+};
+
+static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) {
+ struct userdata *u = userdata;
+ assert(io);
+ assert(u);
+
+ if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
+ pa_log(__FILE__": lost connection to evdev device.\n");
+ goto fail;
+ }
+
+ if (events & PA_IO_EVENT_INPUT) {
+ struct input_event ev;
+
+ if (pa_loop_read(u->fd, &ev, sizeof(ev)) <= 0) {
+ pa_log(__FILE__": failed to read from event device: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) {
+ enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID;
+
+ pa_log_debug(__FILE__": key code=%u, value=%u\n", ev.code, ev.value);
+
+ switch (ev.code) {
+ case KEY_VOLUMEDOWN: volchange = DOWN; break;
+ case KEY_VOLUMEUP: volchange = UP; break;
+ case KEY_MUTE: volchange = MUTE_TOGGLE; break;
+ }
+
+ if (volchange != INVALID) {
+ pa_sink *s;
+
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
+ pa_log(__FILE__": failed to get sink '%s'\n", u->sink_name);
+ else {
+ pa_volume_t v = pa_cvolume_avg(pa_sink_get_volume(s, PA_MIXER_HARDWARE));
+ pa_cvolume cv;
+#define DELTA (PA_VOLUME_NORM/20)
+
+ switch (volchange) {
+ case UP:
+ v += DELTA;
+ break;
+
+ case DOWN:
+ if (v > DELTA)
+ v -= DELTA;
+ else
+ v = PA_VOLUME_MUTED;
+
+ break;
+
+ case MUTE_TOGGLE: {
+
+ if (v > 0) {
+ u->mute_toggle_save = v;
+ v = PA_VOLUME_MUTED;
+ } else
+ v = u->mute_toggle_save;
+ }
+ default:
+ ;
+ }
+
+ pa_cvolume_set(&cv, PA_CHANNELS_MAX, v);
+ pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv);
+ }
+ }
+ }
+ }
+
+ return;
+
+fail:
+ u->module->core->mainloop->io_free(u->io);
+ u->io = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
+#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ int version;
+ struct _input_id input_id;
+ char name[256];
+ uint8_t evtype_bitmask[EV_MAX/8 + 1];
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": Failed to parse module arguments\n");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xmalloc(sizeof(struct userdata));
+ u->module = m;
+ u->io = NULL;
+ u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+ u->fd = -1;
+ u->mute_toggle_save = 0;
+
+ if ((u->fd = open(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), O_RDONLY)) < 0) {
+ pa_log(__FILE__": failed to open evdev device: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (ioctl(u->fd, EVIOCGVERSION, &version) < 0) {
+ pa_log(__FILE__": EVIOCGVERSION failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ pa_log_info(__FILE__": evdev driver version %i.%i.%i\n", version >> 16, (version >> 8) & 0xff, version & 0xff);
+
+ if(ioctl(u->fd, EVIOCGID, &input_id)) {
+ pa_log(__FILE__": EVIOCGID failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ pa_log_info(__FILE__": evdev vendor 0x%04hx product 0x%04hx version 0x%04hx bustype %u\n",
+ input_id.vendor, input_id.product, input_id.version, input_id.bustype);
+
+ if(ioctl(u->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
+ pa_log(__FILE__": EVIOCGNAME failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ pa_log_info(__FILE__": evdev device name: %s\n", name);
+
+ memset(evtype_bitmask, 0, sizeof(evtype_bitmask));
+ if (ioctl(u->fd, EVIOCGBIT(0, EV_MAX), evtype_bitmask) < 0) {
+ pa_log(__FILE__": EVIOCGBIT failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (!test_bit(EV_KEY, evtype_bitmask)) {
+ pa_log(__FILE__": device has no keys.\n");
+ goto fail;
+ }
+
+ u->io = c->mainloop->io_new(c->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(c, m);
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c);
+ assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->io)
+ m->core->mainloop->io_free(u->io);
+
+ if (u->fd >= 0)
+ close(u->fd);
+
+ pa_xfree(u->sink_name);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-native-protocol-fd.c b/src/modules/module-native-protocol-fd.c
new file mode 100644
index 00000000..abc531b3
--- /dev/null
+++ b/src/modules/module-native-protocol-fd.c
@@ -0,0 +1,85 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include <polypcore/module.h>
+#include <polypcore/iochannel.h>
+#include <polypcore/modargs.h>
+#include <polypcore/protocol-native.h>
+#include <polypcore/log.h>
+
+#include "module-native-protocol-fd-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Native protocol autospawn helper")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+
+static const char* const valid_modargs[] = {
+ "fd",
+ "public",
+ "cookie",
+ NULL,
+};
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_iochannel *io;
+ pa_modargs *ma;
+ int fd, r = -1;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments.\n");
+ goto finish;
+ }
+
+ if (pa_modargs_get_value_s32(ma, "fd", &fd) < 0) {
+ pa_log(__FILE__": invalid file descriptor.\n");
+ goto finish;
+ }
+
+ io = pa_iochannel_new(c->mainloop, fd, fd);
+
+ if (!(m->userdata = pa_protocol_native_new_iochannel(c, io, m, ma))) {
+ pa_iochannel_free(io);
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (ma)
+ pa_modargs_free(ma);
+
+ return r;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ assert(c && m);
+
+ pa_protocol_native_free(m->userdata);
+}
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
new file mode 100644
index 00000000..5731a403
--- /dev/null
+++ b/src/modules/module-null-sink.c
@@ -0,0 +1,150 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <polypcore/iochannel.h>
+#include <polypcore/sink.h>
+#include <polypcore/module.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+#include "module-null-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Clocked NULL sink")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("format=<sample format> channels=<number of channels> rate=<sample rate> sink_name=<name of sink>")
+
+#define DEFAULT_SINK_NAME "null"
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_sink *sink;
+ pa_time_event *time_event;
+ size_t block_size;
+};
+
+static const char* const valid_modargs[] = {
+ "rate",
+ "format",
+ "channels",
+ "sink_name",
+ NULL
+};
+
+static void time_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+ pa_memchunk chunk;
+ struct timeval ntv = *tv;
+ size_t l;
+
+ assert(u);
+
+ if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) {
+ l = chunk.length;
+ pa_memblock_unref(chunk.memblock);
+ } else
+ l = u->block_size;
+
+ pa_timeval_add(&ntv, pa_bytes_to_usec(l, &u->sink->sample_spec));
+ m->time_restart(e, &ntv);
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct userdata *u = NULL;
+ pa_sample_spec ss;
+ pa_modargs *ma = NULL;
+ struct timeval tv;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments.\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log(__FILE__": invalid sample format specification.\n");
+ goto fail;
+ }
+
+ u = pa_xmalloc0(sizeof(struct userdata));
+ u->core = c;
+ u->module = m;
+ m->userdata = u;
+
+ if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
+ pa_log(__FILE__": failed to create sink.\n");
+ goto fail;
+ }
+
+ u->sink->userdata = u;
+ pa_sink_set_owner(u->sink, m);
+ u->sink->description = pa_sprintf_malloc("NULL sink");
+
+ pa_gettimeofday(&tv);
+ u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u);
+
+ u->block_size = pa_bytes_per_second(&ss) / 10;
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(c, m);
+
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+
+ u->core->mainloop->time_free(u->time_event);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-oss-mmap.c b/src/modules/module-oss-mmap.c
new file mode 100644
index 00000000..6986b03c
--- /dev/null
+++ b/src/modules/module-oss-mmap.c
@@ -0,0 +1,426 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/mman.h>
+
+#include <polypcore/iochannel.h>
+#include <polypcore/sink.h>
+#include <polypcore/source.h>
+#include <polypcore/module.h>
+#include <polypcore/oss-util.h>
+#include <polypcore/sample-util.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+#include "module-oss-mmap-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("OSS Sink/Source (mmap)")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("sink_name=<name for the sink> source_name=<name for the source> device=<OSS device> record=<enable source?> playback=<enable sink?> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size>")
+
+struct userdata {
+ pa_sink *sink;
+ pa_source *source;
+ pa_core *core;
+ pa_sample_spec sample_spec;
+
+ size_t in_fragment_size, out_fragment_size, in_fragments, out_fragments, out_fill;
+
+ int fd;
+
+ void *in_mmap, *out_mmap;
+ size_t in_mmap_length, out_mmap_length;
+
+ pa_io_event *io_event;
+
+ pa_memblock **in_memblocks, **out_memblocks;
+ unsigned out_current, in_current;
+ pa_module *module;
+};
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ "source_name",
+ "device",
+ "record",
+ "playback",
+ "fragments",
+ "fragment_size",
+ "format",
+ "rate",
+ "channels",
+ NULL
+};
+
+#define DEFAULT_SINK_NAME "oss_output"
+#define DEFAULT_SOURCE_NAME "oss_input"
+#define DEFAULT_DEVICE "/dev/dsp"
+
+static void update_usage(struct userdata *u) {
+ pa_module_set_used(u->module,
+ (u->sink ? pa_idxset_size(u->sink->inputs) : 0) +
+ (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) +
+ (u->source ? pa_idxset_size(u->source->outputs) : 0));
+}
+
+static void out_fill_memblocks(struct userdata *u, unsigned n) {
+ assert(u && u->out_memblocks);
+
+ while (n > 0) {
+ pa_memchunk chunk;
+
+ if (u->out_memblocks[u->out_current])
+ pa_memblock_unref_fixed(u->out_memblocks[u->out_current]);
+
+ chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed((uint8_t*)u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size, 1, u->core->memblock_stat);
+ assert(chunk.memblock);
+ chunk.length = chunk.memblock->length;
+ chunk.index = 0;
+
+ pa_sink_render_into_full(u->sink, &chunk);
+
+ u->out_current++;
+ while (u->out_current >= u->out_fragments)
+ u->out_current -= u->out_fragments;
+
+ n--;
+ }
+}
+
+static void do_write(struct userdata *u) {
+ struct count_info info;
+ assert(u && u->sink);
+
+ update_usage(u);
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_GETOPTR: %s\n", strerror(errno));
+ return;
+ }
+
+ u->out_fill = (u->out_fragment_size * u->out_fragments) - (info.ptr % u->out_fragment_size);
+
+ if (!info.blocks)
+ return;
+
+ out_fill_memblocks(u, info.blocks);
+}
+
+static void in_post_memblocks(struct userdata *u, unsigned n) {
+ assert(u && u->in_memblocks);
+
+ while (n > 0) {
+ pa_memchunk chunk;
+
+ if (!u->in_memblocks[u->in_current]) {
+ chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed((uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1, u->core->memblock_stat);
+ chunk.length = chunk.memblock->length;
+ chunk.index = 0;
+
+ pa_source_post(u->source, &chunk);
+ }
+
+ u->in_current++;
+ while (u->in_current >= u->in_fragments)
+ u->in_current -= u->in_fragments;
+
+ n--;
+ }
+}
+
+static void in_clear_memblocks(struct userdata*u, unsigned n) {
+ unsigned i = u->in_current;
+ assert(u && u->in_memblocks);
+
+ if (n > u->in_fragments)
+ n = u->in_fragments;
+
+ while (n > 0) {
+ if (u->in_memblocks[i]) {
+ pa_memblock_unref_fixed(u->in_memblocks[i]);
+ u->in_memblocks[i] = NULL;
+ }
+
+ i++;
+ while (i >= u->in_fragments)
+ i -= u->in_fragments;
+
+ n--;
+ }
+}
+
+static void do_read(struct userdata *u) {
+ struct count_info info;
+ assert(u && u->source);
+
+ update_usage(u);
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_GETIPTR: %s\n", strerror(errno));
+ return;
+ }
+
+ if (!info.blocks)
+ return;
+
+ in_post_memblocks(u, info.blocks);
+ in_clear_memblocks(u, u->in_fragments/2);
+}
+
+static void io_callback(pa_mainloop_api *m, pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t f, void *userdata) {
+ struct userdata *u = userdata;
+ assert (u && u->core->mainloop == m && u->io_event == e);
+
+ if (f & PA_IO_EVENT_INPUT)
+ do_read(u);
+ if (f & PA_IO_EVENT_OUTPUT)
+ do_write(u);
+}
+
+static pa_usec_t sink_get_latency_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ assert(s && u);
+
+ do_write(u);
+ return pa_bytes_to_usec(u->out_fill, &s->sample_spec);
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct audio_buf_info info;
+ struct userdata *u = NULL;
+ const char *p;
+ int nfrags, frag_size;
+ int mode, caps;
+ int enable_bits = 0, zero = 0;
+ int playback = 1, record = 1;
+ pa_modargs *ma = NULL;
+ assert(c && m);
+
+ m->userdata = u = pa_xmalloc0(sizeof(struct userdata));
+ u->module = m;
+ u->fd = -1;
+ u->core = c;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments.\n");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
+ pa_log(__FILE__": record= and playback= expect numeric arguments.\n");
+ goto fail;
+ }
+
+ if (!playback && !record) {
+ pa_log(__FILE__": neither playback nor record enabled for device.\n");
+ goto fail;
+ }
+
+ mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+
+ nfrags = 12;
+ frag_size = 1024;
+ if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
+ pa_log(__FILE__": failed to parse fragments arguments\n");
+ goto fail;
+ }
+
+ u->sample_spec = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &u->sample_spec) < 0) {
+ pa_log(__FILE__": failed to parse sample specification\n");
+ goto fail;
+ }
+
+ if ((u->fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
+ goto fail;
+
+ if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_REALTIME) || !(caps & DSP_CAP_TRIGGER)) {
+ pa_log(__FILE__": OSS device not mmap capable.\n");
+ goto fail;
+ }
+
+ pa_log(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+
+ if (nfrags >= 2 && frag_size >= 1)
+ if (pa_oss_set_fragments(u->fd, nfrags, frag_size) < 0)
+ goto fail;
+
+ if (pa_oss_auto_format(u->fd, &u->sample_spec) < 0)
+ goto fail;
+
+ if (mode != O_WRONLY) {
+ if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_GETISPACE: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ pa_log_info(__FILE__": input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
+ u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal);
+
+ if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
+ if (mode == O_RDWR) {
+ pa_log(__FILE__": mmap failed for input. Changing to O_WRONLY mode.\n");
+ mode = O_WRONLY;
+ } else {
+ pa_log(__FILE__": mmap(): %s\n", strerror(errno));
+ goto fail;
+ }
+ } else {
+
+ u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &u->sample_spec, NULL);
+ assert(u->source);
+ u->source->userdata = u;
+ pa_source_set_owner(u->source, m);
+ u->source->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p);
+
+ u->in_memblocks = pa_xmalloc0(sizeof(pa_memblock *)*u->in_fragments);
+
+ enable_bits |= PCM_ENABLE_INPUT;
+ }
+ }
+
+ if (mode != O_RDONLY) {
+ if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ pa_log_info(__FILE__": output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
+ u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal);
+
+ if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
+ if (mode == O_RDWR) {
+ pa_log(__FILE__": mmap filed for output. Changing to O_RDONLY mode.\n");
+ mode = O_RDONLY;
+ } else {
+ pa_log(__FILE__": mmap(): %s\n", strerror(errno));
+ goto fail;
+ }
+ } else {
+ pa_silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec);
+
+ u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &u->sample_spec, NULL);
+ assert(u->sink);
+ u->sink->get_latency = sink_get_latency_cb;
+ u->sink->userdata = u;
+ pa_sink_set_owner(u->sink, m);
+ u->sink->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p);
+
+ u->out_memblocks = pa_xmalloc0(sizeof(struct memblock *)*u->out_fragments);
+
+ enable_bits |= PCM_ENABLE_OUTPUT;
+ }
+ }
+
+ zero = 0;
+ if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ assert(u->source || u->sink);
+
+ u->io_event = c->mainloop->io_new(c->mainloop, u->fd, (u->source ? PA_IO_EVENT_INPUT : 0) | (u->sink ? PA_IO_EVENT_OUTPUT : 0), io_callback, u);
+ assert(u->io_event);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ pa__done(c, m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->out_memblocks) {
+ unsigned i;
+ for (i = 0; i < u->out_fragments; i++)
+ if (u->out_memblocks[i])
+ pa_memblock_unref_fixed(u->out_memblocks[i]);
+ pa_xfree(u->out_memblocks);
+ }
+
+ if (u->in_memblocks) {
+ unsigned i;
+ for (i = 0; i < u->in_fragments; i++)
+ if (u->in_memblocks[i])
+ pa_memblock_unref_fixed(u->in_memblocks[i]);
+ pa_xfree(u->in_memblocks);
+ }
+
+ if (u->in_mmap && u->in_mmap != MAP_FAILED)
+ munmap(u->in_mmap, u->in_mmap_length);
+
+ if (u->out_mmap && u->out_mmap != MAP_FAILED)
+ munmap(u->out_mmap, u->out_mmap_length);
+
+ if (u->sink) {
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+ }
+
+ if (u->source) {
+ pa_source_disconnect(u->source);
+ pa_source_unref(u->source);
+ }
+
+ if (u->io_event)
+ u->core->mainloop->io_free(u->io_event);
+
+ if (u->fd >= 0)
+ close(u->fd);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c
new file mode 100644
index 00000000..04458419
--- /dev/null
+++ b/src/modules/module-oss.c
@@ -0,0 +1,468 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <polypcore/iochannel.h>
+#include <polypcore/sink.h>
+#include <polypcore/source.h>
+#include <polypcore/module.h>
+#include <polypcore/oss-util.h>
+#include <polypcore/sample-util.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+#include "module-oss-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("OSS Sink/Source")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("sink_name=<name for the sink> source_name=<name for the source> device=<OSS device> record=<enable source?> playback=<enable sink?> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size>")
+
+struct userdata {
+ pa_sink *sink;
+ pa_source *source;
+ pa_iochannel *io;
+ pa_core *core;
+
+ pa_memchunk memchunk, silence;
+
+ uint32_t in_fragment_size, out_fragment_size, sample_size;
+ int use_getospace, use_getispace;
+
+ int fd;
+ pa_module *module;
+};
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ "source_name",
+ "device",
+ "record",
+ "playback",
+ "fragments",
+ "fragment_size",
+ "format",
+ "rate",
+ "channels",
+ NULL
+};
+
+#define DEFAULT_SINK_NAME "oss_output"
+#define DEFAULT_SOURCE_NAME "oss_input"
+#define DEFAULT_DEVICE "/dev/dsp"
+
+static void update_usage(struct userdata *u) {
+ pa_module_set_used(u->module,
+ (u->sink ? pa_idxset_size(u->sink->inputs) : 0) +
+ (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) +
+ (u->source ? pa_idxset_size(u->source->outputs) : 0));
+}
+
+static void do_write(struct userdata *u) {
+ pa_memchunk *memchunk;
+ ssize_t r;
+ size_t l;
+ int loop = 0;
+
+ assert(u);
+
+ if (!u->sink || !pa_iochannel_is_writable(u->io))
+ return;
+
+ update_usage(u);
+
+ l = u->out_fragment_size;
+
+ if (u->use_getospace) {
+ audio_buf_info info;
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
+ u->use_getospace = 0;
+ else {
+ if (info.bytes/l > 0) {
+ l = (info.bytes/l)*l;
+ loop = 1;
+ }
+ }
+ }
+
+ do {
+ memchunk = &u->memchunk;
+
+ if (!memchunk->length)
+ if (pa_sink_render(u->sink, l, memchunk) < 0)
+ memchunk = &u->silence;
+
+ assert(memchunk->memblock);
+ assert(memchunk->memblock->data);
+ assert(memchunk->length);
+
+ if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) {
+ pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+ break;
+ }
+
+ if (memchunk == &u->silence)
+ assert(r % u->sample_size == 0);
+ else {
+ u->memchunk.index += r;
+ u->memchunk.length -= r;
+
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ u->memchunk.memblock = NULL;
+ }
+ }
+
+ l = l > (size_t) r ? l - r : 0;
+ } while (loop && l > 0);
+}
+
+static void do_read(struct userdata *u) {
+ pa_memchunk memchunk;
+ ssize_t r;
+ size_t l;
+ int loop = 0;
+ assert(u);
+
+ if (!u->source || !pa_iochannel_is_readable(u->io) || !pa_idxset_size(u->source->outputs))
+ return;
+
+ update_usage(u);
+
+ l = u->in_fragment_size;
+
+ if (u->use_getispace) {
+ audio_buf_info info;
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0)
+ u->use_getispace = 0;
+ else {
+ if (info.bytes/l > 0) {
+ l = (info.bytes/l)*l;
+ loop = 1;
+ }
+ }
+ }
+
+ do {
+ memchunk.memblock = pa_memblock_new(l, u->core->memblock_stat);
+ assert(memchunk.memblock);
+ if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
+ pa_memblock_unref(memchunk.memblock);
+ if (errno != EAGAIN)
+ pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+ break;
+ }
+
+ assert(r <= (ssize_t) memchunk.memblock->length);
+ memchunk.length = memchunk.memblock->length = r;
+ memchunk.index = 0;
+
+ pa_source_post(u->source, &memchunk);
+ pa_memblock_unref(memchunk.memblock);
+
+ l = l > (size_t) r ? l - r : 0;
+ } while (loop && l > 0);
+}
+
+static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_write(u);
+ do_read(u);
+}
+
+static void source_notify_cb(pa_source *s) {
+ struct userdata *u = s->userdata;
+ assert(u);
+ do_read(u);
+}
+
+static pa_usec_t sink_get_latency_cb(pa_sink *s) {
+ pa_usec_t r = 0;
+ int arg;
+ struct userdata *u = s->userdata;
+ assert(s && u && u->sink);
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
+ pa_log_info(__FILE__": device doesn't support SNDCTL_DSP_GETODELAY: %s\n", strerror(errno));
+ s->get_latency = NULL;
+ return 0;
+ }
+
+ r += pa_bytes_to_usec(arg, &s->sample_spec);
+
+ if (u->memchunk.memblock)
+ r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
+
+ return r;
+}
+
+static pa_usec_t source_get_latency_cb(pa_source *s) {
+ struct userdata *u = s->userdata;
+ audio_buf_info info;
+ assert(s && u && u->source);
+
+ if (!u->use_getispace)
+ return 0;
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
+ u->use_getispace = 0;
+ return 0;
+ }
+
+ if (info.bytes <= 0)
+ return 0;
+
+ return pa_bytes_to_usec(info.bytes, &s->sample_spec);
+}
+
+static int sink_get_hw_volume(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ char cv[PA_CVOLUME_SNPRINT_MAX];
+ unsigned vol;
+
+ if (ioctl(u->fd, SOUND_MIXER_READ_PCM, &vol) < 0) {
+ pa_log_info(__FILE__": device doesn't support reading mixer settings: %s\n", strerror(errno));
+ s->get_hw_volume = NULL;
+ return -1;
+ }
+
+ s->hw_volume.values[0] = ((vol & 0xFF) * PA_VOLUME_NORM) / 100;
+
+ if ((s->hw_volume.channels = s->sample_spec.channels) >= 2)
+ s->hw_volume.values[1] = (((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100;
+
+ pa_log_info(__FILE__": Read mixer settings: %s\n", pa_cvolume_snprint(cv, sizeof(cv), &s->hw_volume));
+ return 0;
+}
+
+static int sink_set_hw_volume(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ char cv[PA_CVOLUME_SNPRINT_MAX];
+ unsigned vol;
+
+ vol = (s->hw_volume.values[0]*100)/PA_VOLUME_NORM;
+
+ if (s->sample_spec.channels >= 2)
+ vol |= ((s->hw_volume.values[1]*100)/PA_VOLUME_NORM) << 8;
+
+ if (ioctl(u->fd, SOUND_MIXER_WRITE_PCM, &vol) < 0) {
+ pa_log_info(__FILE__": device doesn't support writing mixer settings: %s\n", strerror(errno));
+ s->set_hw_volume = NULL;
+ return -1;
+ }
+
+ pa_log_info(__FILE__": Wrote mixer settings: %s\n", pa_cvolume_snprint(cv, sizeof(cv), &s->hw_volume));
+ return 0;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct audio_buf_info info;
+ struct userdata *u = NULL;
+ const char *p;
+ int fd = -1;
+ int nfrags, frag_size, in_frag_size, out_frag_size;
+ int mode;
+ int record = 1, playback = 1;
+ pa_sample_spec ss;
+ pa_modargs *ma = NULL;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments.\n");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
+ pa_log(__FILE__": record= and playback= expect numeric argument.\n");
+ goto fail;
+ }
+
+ if (!playback && !record) {
+ pa_log(__FILE__": neither playback nor record enabled for device.\n");
+ goto fail;
+ }
+
+ mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+
+ nfrags = 12;
+ frag_size = 1024;
+ if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
+ pa_log(__FILE__": failed to parse fragments arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log(__FILE__": failed to parse sample specification\n");
+ goto fail;
+ }
+
+ if ((fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, NULL)) < 0)
+ goto fail;
+
+ pa_log_info(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+
+ if (nfrags >= 2 && frag_size >= 1)
+ if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
+ goto fail;
+
+ if (pa_oss_auto_format(fd, &ss) < 0)
+ goto fail;
+
+ if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno));
+ goto fail;
+ }
+ assert(frag_size);
+ in_frag_size = out_frag_size = frag_size;
+
+ u = pa_xmalloc(sizeof(struct userdata));
+ u->core = c;
+ u->use_getospace = u->use_getispace = 0;
+
+ if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
+ pa_log_info(__FILE__": input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
+ in_frag_size = info.fragsize;
+ u->use_getispace = 1;
+ }
+
+ if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
+ pa_log_info(__FILE__": output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
+ out_frag_size = info.fragsize;
+ u->use_getospace = 1;
+ }
+
+ if (mode != O_WRONLY) {
+ u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL);
+ assert(u->source);
+ u->source->userdata = u;
+ u->source->notify = source_notify_cb;
+ u->source->get_latency = source_get_latency_cb;
+ pa_source_set_owner(u->source, m);
+ u->source->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p);
+ } else
+ u->source = NULL;
+
+ if (mode != O_RDONLY) {
+ u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL);
+ assert(u->sink);
+ u->sink->get_latency = sink_get_latency_cb;
+ u->sink->get_hw_volume = sink_get_hw_volume;
+ u->sink->set_hw_volume = sink_set_hw_volume;
+ u->sink->userdata = u;
+ pa_sink_set_owner(u->sink, m);
+ u->sink->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p);
+ } else
+ u->sink = NULL;
+
+ assert(u->source || u->sink);
+
+ u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : -1);
+ assert(u->io);
+ pa_iochannel_set_callback(u->io, io_callback, u);
+ u->fd = fd;
+
+ u->memchunk.memblock = NULL;
+ u->memchunk.length = 0;
+ u->sample_size = pa_frame_size(&ss);
+
+ u->out_fragment_size = out_frag_size;
+ u->in_fragment_size = in_frag_size;
+ u->silence.memblock = pa_memblock_new(u->silence.length = u->out_fragment_size, u->core->memblock_stat);
+ assert(u->silence.memblock);
+ pa_silence_memblock(u->silence.memblock, &ss);
+ u->silence.index = 0;
+
+ u->module = m;
+ m->userdata = u;
+
+ pa_modargs_free(ma);
+
+ /*
+ * Some crappy drivers do not start the recording until we read something.
+ * Without this snippet, poll will never register the fd as ready.
+ */
+ if (u->source) {
+ char buf[u->sample_size];
+ read(u->fd, buf, u->sample_size);
+ }
+
+ /* Read mixer settings */
+ if (u->sink)
+ sink_get_hw_volume(u->sink);
+
+ return 0;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+ if (u->silence.memblock)
+ pa_memblock_unref(u->silence.memblock);
+
+ if (u->sink) {
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+ }
+
+ if (u->source) {
+ pa_source_disconnect(u->source);
+ pa_source_unref(u->source);
+ }
+
+ pa_iochannel_free(u->io);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
new file mode 100644
index 00000000..6ace377f
--- /dev/null
+++ b/src/modules/module-pipe-sink.c
@@ -0,0 +1,237 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <polypcore/iochannel.h>
+#include <polypcore/sink.h>
+#include <polypcore/module.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+#include "module-pipe-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("UNIX pipe sink")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("sink_name=<name for the sink> file=<path of the FIFO> format=<sample format> channels=<number of channels> rate=<sample rate>")
+
+#define DEFAULT_FIFO_NAME "/tmp/music.output"
+#define DEFAULT_SINK_NAME "fifo_output"
+
+struct userdata {
+ pa_core *core;
+
+ char *filename;
+
+ pa_sink *sink;
+ pa_iochannel *io;
+ pa_defer_event *defer_event;
+
+ pa_memchunk memchunk;
+ pa_module *module;
+};
+
+static const char* const valid_modargs[] = {
+ "file",
+ "rate",
+ "format",
+ "channels",
+ "sink_name",
+ NULL
+};
+
+static void do_write(struct userdata *u) {
+ ssize_t r;
+ assert(u);
+
+ u->core->mainloop->defer_enable(u->defer_event, 0);
+
+ if (!pa_iochannel_is_writable(u->io))
+ return;
+
+ pa_module_set_used(u->module, pa_idxset_size(u->sink->inputs) + pa_idxset_size(u->sink->monitor_source->outputs));
+
+ if (!u->memchunk.length)
+ if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0)
+ return;
+
+ assert(u->memchunk.memblock && u->memchunk.length);
+
+ if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
+ pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ u->memchunk.index += r;
+ u->memchunk.length -= r;
+
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ u->memchunk.memblock = NULL;
+ }
+}
+
+static void notify_cb(pa_sink*s) {
+ struct userdata *u = s->userdata;
+ assert(s && u);
+
+ if (pa_iochannel_is_writable(u->io))
+ u->core->mainloop->defer_enable(u->defer_event, 1);
+}
+
+static pa_usec_t get_latency_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ assert(s && u);
+
+ return u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0;
+}
+
+static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_write(u);
+}
+
+static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_write(u);
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct userdata *u = NULL;
+ struct stat st;
+ const char *p;
+ int fd = -1;
+ pa_sample_spec ss;
+ pa_modargs *ma = NULL;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log(__FILE__": invalid sample format specification\n");
+ goto fail;
+ }
+
+ mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
+
+ if ((fd = open(p, O_RDWR)) < 0) {
+ pa_log(__FILE__": open('%s'): %s\n", p, strerror(errno));
+ goto fail;
+ }
+
+ pa_fd_set_cloexec(fd, 1);
+
+ if (fstat(fd, &st) < 0) {
+ pa_log(__FILE__": fstat('%s'): %s\n", p, strerror(errno));
+ goto fail;
+ }
+
+ if (!S_ISFIFO(st.st_mode)) {
+ pa_log(__FILE__": '%s' is not a FIFO.\n", p);
+ goto fail;
+ }
+
+ u = pa_xmalloc0(sizeof(struct userdata));
+ u->filename = pa_xstrdup(p);
+ u->core = c;
+ u->module = m;
+ m->userdata = u;
+
+ if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
+ pa_log(__FILE__": failed to create sink.\n");
+ goto fail;
+ }
+ u->sink->notify = notify_cb;
+ u->sink->get_latency = get_latency_cb;
+ u->sink->userdata = u;
+ pa_sink_set_owner(u->sink, m);
+ u->sink->description = pa_sprintf_malloc("Unix FIFO sink '%s'", p);
+ assert(u->sink->description);
+
+ u->io = pa_iochannel_new(c->mainloop, -1, fd);
+ assert(u->io);
+ pa_iochannel_set_callback(u->io, io_callback, u);
+
+ u->memchunk.memblock = NULL;
+ u->memchunk.length = 0;
+
+ u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u);
+ assert(u->defer_event);
+ c->mainloop->defer_enable(u->defer_event, 0);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ if (fd >= 0)
+ close(fd);
+
+ pa__done(c, m);
+
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+ pa_iochannel_free(u->io);
+ u->core->mainloop->defer_free(u->defer_event);
+
+ assert(u->filename);
+ unlink(u->filename);
+ pa_xfree(u->filename);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
new file mode 100644
index 00000000..a7bb0ce7
--- /dev/null
+++ b/src/modules/module-pipe-source.c
@@ -0,0 +1,210 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <polypcore/iochannel.h>
+#include <polypcore/source.h>
+#include <polypcore/module.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+#include "module-pipe-source-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("UNIX pipe source")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("source_name=<name for the source> file=<path of the FIFO> format=<sample format> channels=<number of channels> rate=<sample rate>")
+
+#define DEFAULT_FIFO_NAME "/tmp/music.input"
+#define DEFAULT_SOURCE_NAME "fifo_input"
+
+struct userdata {
+ pa_core *core;
+
+ char *filename;
+
+ pa_source *source;
+ pa_iochannel *io;
+ pa_module *module;
+ pa_memchunk chunk;
+};
+
+static const char* const valid_modargs[] = {
+ "file",
+ "rate",
+ "channels",
+ "format",
+ "source_name",
+ NULL
+};
+
+static void do_read(struct userdata *u) {
+ ssize_t r;
+ pa_memchunk chunk;
+ assert(u);
+
+ if (!pa_iochannel_is_readable(u->io))
+ return;
+
+ pa_module_set_used(u->module, pa_idxset_size(u->source->outputs));
+
+ if (!u->chunk.memblock) {
+ u->chunk.memblock = pa_memblock_new(1024, u->core->memblock_stat);
+ u->chunk.index = chunk.length = 0;
+ }
+
+ assert(u->chunk.memblock && u->chunk.memblock->length > u->chunk.index);
+ if ((r = pa_iochannel_read(u->io, (uint8_t*) u->chunk.memblock->data + u->chunk.index, u->chunk.memblock->length - u->chunk.index)) <= 0) {
+ pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ u->chunk.length = r;
+ pa_source_post(u->source, &u->chunk);
+ u->chunk.index += r;
+
+ if (u->chunk.index >= u->chunk.memblock->length) {
+ u->chunk.index = u->chunk.length = 0;
+ pa_memblock_unref(u->chunk.memblock);
+ u->chunk.memblock = NULL;
+ }
+}
+
+static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_read(u);
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct userdata *u = NULL;
+ struct stat st;
+ const char *p;
+ int fd = -1;
+ pa_sample_spec ss;
+ pa_modargs *ma = NULL;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log(__FILE__": invalid sample format specification\n");
+ goto fail;
+ }
+
+ mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
+
+ if ((fd = open(p, O_RDWR)) < 0) {
+ pa_log(__FILE__": open('%s'): %s\n", p, strerror(errno));
+ goto fail;
+ }
+
+ pa_fd_set_cloexec(fd, 1);
+
+ if (fstat(fd, &st) < 0) {
+ pa_log(__FILE__": fstat('%s'): %s\n", p, strerror(errno));
+ goto fail;
+ }
+
+ if (!S_ISFIFO(st.st_mode)) {
+ pa_log(__FILE__": '%s' is not a FIFO.\n", p);
+ goto fail;
+ }
+
+ u = pa_xmalloc0(sizeof(struct userdata));
+
+ u->filename = pa_xstrdup(p);
+ u->core = c;
+
+ if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL))) {
+ pa_log(__FILE__": failed to create source.\n");
+ goto fail;
+ }
+ u->source->userdata = u;
+ pa_source_set_owner(u->source, m);
+ u->source->description = pa_sprintf_malloc("Unix FIFO source '%s'", p);
+ assert(u->source->description);
+
+ u->io = pa_iochannel_new(c->mainloop, fd, -1);
+ assert(u->io);
+ pa_iochannel_set_callback(u->io, io_callback, u);
+
+ u->chunk.memblock = NULL;
+ u->chunk.index = u->chunk.length = 0;
+
+ u->module = m;
+ m->userdata = u;
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ if (fd >= 0)
+ close(fd);
+
+ pa__done(c, m);
+
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->chunk.memblock)
+ pa_memblock_unref(u->chunk.memblock);
+
+ pa_source_disconnect(u->source);
+ pa_source_unref(u->source);
+ pa_iochannel_free(u->io);
+
+ assert(u->filename);
+ unlink(u->filename);
+ pa_xfree(u->filename);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c
new file mode 100644
index 00000000..469ddcee
--- /dev/null
+++ b/src/modules/module-protocol-stub.c
@@ -0,0 +1,261 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <errno.h>
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <limits.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <polypcore/winsock.h>
+
+#include <polypcore/module.h>
+#include <polypcore/socket-server.h>
+#include <polypcore/socket-util.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/log.h>
+#include <polypcore/native-common.h>
+
+#ifdef USE_TCP_SOCKETS
+#define SOCKET_DESCRIPTION "(TCP sockets)"
+#define SOCKET_USAGE "port=<TCP port number> loopback=<listen on loopback device only?>"
+#elif defined(USE_TCP6_SOCKETS)
+#define SOCKET_DESCRIPTION "(TCP/IPv6 sockets)"
+#define SOCKET_USAGE "port=<TCP port number> loopback=<listen on loopback device only?>"
+#else
+#define SOCKET_DESCRIPTION "(UNIX sockets)"
+#define SOCKET_USAGE "socket=<path to UNIX socket>"
+#endif
+
+#if defined(USE_PROTOCOL_SIMPLE)
+ #include <polypcore/protocol-simple.h>
+ #define protocol_new pa_protocol_simple_new
+ #define protocol_free pa_protocol_simple_free
+ #define TCPWRAP_SERVICE "polypaudio-simple"
+ #define IPV4_PORT 4711
+ #define UNIX_SOCKET "simple"
+ #define MODULE_ARGUMENTS "rate", "format", "channels", "sink", "source", "playback", "record",
+ #if defined(USE_TCP_SOCKETS)
+ #include "module-simple-protocol-tcp-symdef.h"
+ #elif defined(USE_TCP6_SOCKETS)
+ #include "module-simple-protocol-tcp6-symdef.h"
+ #else
+ #include "module-simple-protocol-unix-symdef.h"
+ #endif
+ PA_MODULE_DESCRIPTION("Simple protocol "SOCKET_DESCRIPTION)
+ PA_MODULE_USAGE("rate=<sample rate> format=<sample format> channels=<number of channels> sink=<sink to connect to> source=<source to connect to> playback=<enable playback?> record=<enable record?> "SOCKET_USAGE)
+#elif defined(USE_PROTOCOL_CLI)
+ #include <polypcore/protocol-cli.h>
+ #define protocol_new pa_protocol_cli_new
+ #define protocol_free pa_protocol_cli_free
+ #define TCPWRAP_SERVICE "polypaudio-cli"
+ #define IPV4_PORT 4712
+ #define UNIX_SOCKET "cli"
+ #define MODULE_ARGUMENTS
+ #ifdef USE_TCP_SOCKETS
+ #include "module-cli-protocol-tcp-symdef.h"
+ #elif defined(USE_TCP6_SOCKETS)
+ #include "module-cli-protocol-tcp6-symdef.h"
+ #else
+ #include "module-cli-protocol-unix-symdef.h"
+ #endif
+ PA_MODULE_DESCRIPTION("Command line interface protocol "SOCKET_DESCRIPTION)
+ PA_MODULE_USAGE(SOCKET_USAGE)
+#elif defined(USE_PROTOCOL_HTTP)
+ #include <polypcore/protocol-http.h>
+ #define protocol_new pa_protocol_http_new
+ #define protocol_free pa_protocol_http_free
+ #define TCPWRAP_SERVICE "polypaudio-http"
+ #define IPV4_PORT 4714
+ #define UNIX_SOCKET "http"
+ #define MODULE_ARGUMENTS
+ #ifdef USE_TCP_SOCKETS
+ #include "module-http-protocol-tcp-symdef.h"
+ #elif defined(USE_TCP6_SOCKETS)
+ #include "module-http-protocol-tcp6-symdef.h"
+ #else
+ #include "module-http-protocol-unix-symdef.h"
+ #endif
+ PA_MODULE_DESCRIPTION("HTTP "SOCKET_DESCRIPTION)
+ PA_MODULE_USAGE(SOCKET_USAGE)
+#elif defined(USE_PROTOCOL_NATIVE)
+ #include <polypcore/protocol-native.h>
+ #define protocol_new pa_protocol_native_new
+ #define protocol_free pa_protocol_native_free
+ #define TCPWRAP_SERVICE "polypaudio-native"
+ #define IPV4_PORT PA_NATIVE_DEFAULT_PORT
+ #define UNIX_SOCKET PA_NATIVE_DEFAULT_UNIX_SOCKET
+ #define MODULE_ARGUMENTS "public", "cookie",
+ #ifdef USE_TCP_SOCKETS
+ #include "module-native-protocol-tcp-symdef.h"
+ #elif defined(USE_TCP6_SOCKETS)
+ #include "module-native-protocol-tcp6-symdef.h"
+ #else
+ #include "module-native-protocol-unix-symdef.h"
+ #endif
+ PA_MODULE_DESCRIPTION("Native protocol "SOCKET_DESCRIPTION)
+ PA_MODULE_USAGE("public=<don't check for cookies?> cookie=<path to cookie file> "SOCKET_USAGE)
+#elif defined(USE_PROTOCOL_ESOUND)
+ #include <polypcore/protocol-esound.h>
+ #include <polypcore/esound.h>
+ #define protocol_new pa_protocol_esound_new
+ #define protocol_free pa_protocol_esound_free
+ #define TCPWRAP_SERVICE "esound"
+ #define IPV4_PORT ESD_DEFAULT_PORT
+ #define UNIX_SOCKET ESD_UNIX_SOCKET_NAME
+ #define MODULE_ARGUMENTS "sink", "source", "public", "cookie",
+ #ifdef USE_TCP_SOCKETS
+ #include "module-esound-protocol-tcp-symdef.h"
+ #elif defined(USE_TCP6_SOCKETS)
+ #include "module-esound-protocol-tcp6-symdef.h"
+ #else
+ #include "module-esound-protocol-unix-symdef.h"
+ #endif
+ PA_MODULE_DESCRIPTION("ESOUND protocol "SOCKET_DESCRIPTION)
+ PA_MODULE_USAGE("sink=<sink to connect to> source=<source to connect to> public=<don't check for cookies?> cookie=<path to cookie file> "SOCKET_USAGE)
+#else
+ #error "Broken build system"
+#endif
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+
+static const char* const valid_modargs[] = {
+ MODULE_ARGUMENTS
+#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS)
+ "port",
+ "loopback",
+#else
+ "socket",
+#endif
+ NULL
+};
+
+static pa_socket_server *create_socket_server(pa_core *c, pa_modargs *ma) {
+ pa_socket_server *s;
+#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS)
+ int loopback = 1;
+ uint32_t port = IPV4_PORT;
+
+ if (pa_modargs_get_value_boolean(ma, "loopback", &loopback) < 0) {
+ pa_log(__FILE__": loopback= expects a boolean argument.\n");
+ return NULL;
+ }
+
+ if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {
+ pa_log(__FILE__": port= expects a numerical argument between 1 and 65535.\n");
+ return NULL;
+ }
+
+#ifdef USE_TCP6_SOCKETS
+ if (!(s = pa_socket_server_new_ipv6(c->mainloop, loopback ? (const uint8_t*) &in6addr_loopback : (const uint8_t*) &in6addr_any, port)))
+ return NULL;
+#else
+ if (!(s = pa_socket_server_new_ipv4(c->mainloop, loopback ? INADDR_LOOPBACK : INADDR_ANY, port, TCPWRAP_SERVICE)))
+ return NULL;
+#endif
+
+#else
+ int r;
+ const char *v;
+ char tmp[PATH_MAX];
+
+ v = pa_modargs_get_value(ma, "socket", UNIX_SOCKET);
+ assert(v);
+
+ pa_runtime_path(v, tmp, sizeof(tmp));
+
+ if (pa_make_secure_parent_dir(tmp) < 0) {
+ pa_log(__FILE__": Failed to create secure socket directory.\n");
+ return NULL;
+ }
+
+ if ((r = pa_unix_socket_remove_stale(tmp)) < 0) {
+ pa_log(__FILE__": Failed to remove stale UNIX socket '%s': %s\n", tmp, strerror(errno));
+ return NULL;
+ }
+
+ if (r)
+ pa_log(__FILE__": Removed stale UNIX socket '%s'.", tmp);
+
+ if (!(s = pa_socket_server_new_unix(c->mainloop, tmp)))
+ return NULL;
+
+#endif
+ return s;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_socket_server *s;
+ pa_modargs *ma = NULL;
+ int ret = -1;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": Failed to parse module arguments\n");
+ goto finish;
+ }
+
+ if (!(s = create_socket_server(c, ma)))
+ goto finish;
+
+ if (!(m->userdata = protocol_new(c, s, m, ma))) {
+ pa_socket_server_unref(s);
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+ if (ma)
+ pa_modargs_free(ma);
+
+ return ret;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ assert(c && m);
+
+#if defined(USE_PROTOCOL_ESOUND)
+ if (remove (ESD_UNIX_SOCKET_NAME) != 0)
+ pa_log("%s: Failed to remove %s : %s.\n", __FILE__, ESD_UNIX_SOCKET_NAME, strerror (errno));
+ if (remove (ESD_UNIX_SOCKET_DIR) != 0)
+ pa_log("%s: Failed to remove %s : %s.\n", __FILE__, ESD_UNIX_SOCKET_DIR, strerror (errno));
+#endif
+
+ protocol_free(m->userdata);
+}
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
new file mode 100644
index 00000000..446e3974
--- /dev/null
+++ b/src/modules/module-sine.c
@@ -0,0 +1,182 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <math.h>
+
+#include <polypcore/sink-input.h>
+#include <polypcore/module.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/namereg.h>
+#include <polypcore/log.h>
+
+#include "module-sine-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("Sine wave generator")
+PA_MODULE_USAGE("sink=<sink to connect to> frequency=<frequency in Hz>")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_sink_input *sink_input;
+ pa_memblock *memblock;
+ size_t peek_index;
+};
+
+static const char* const valid_modargs[] = {
+ "sink",
+ "frequency",
+ NULL,
+};
+
+static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
+ struct userdata *u;
+ assert(i && chunk && i->userdata);
+ u = i->userdata;
+
+ chunk->memblock = pa_memblock_ref(u->memblock);
+ chunk->index = u->peek_index;
+ chunk->length = u->memblock->length - u->peek_index;
+ return 0;
+}
+
+static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
+ struct userdata *u;
+ assert(i && chunk && length && i->userdata);
+ u = i->userdata;
+
+ assert(chunk->memblock == u->memblock && length <= u->memblock->length-u->peek_index);
+
+ u->peek_index += length;
+
+ if (u->peek_index >= u->memblock->length)
+ u->peek_index = 0;
+}
+
+static void sink_input_kill(pa_sink_input *i) {
+ struct userdata *u;
+ assert(i && i->userdata);
+ u = i->userdata;
+
+ pa_sink_input_disconnect(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ u->sink_input = NULL;
+
+ pa_module_unload_request(u->module);
+}
+
+static void calc_sine(float *f, size_t l, float freq) {
+ size_t i;
+
+ l /= sizeof(float);
+
+ for (i = 0; i < l; i++)
+ f[i] = (float) sin((double) i/l*M_PI*2*freq)/2;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ pa_sink *sink;
+ const char *sink_name;
+ pa_sample_spec ss;
+ uint32_t frequency;
+ char t[256];
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": Failed to parse module arguments\n");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xmalloc(sizeof(struct userdata));
+ u->core = c;
+ u->module = m;
+ u->sink_input = NULL;
+ u->memblock = NULL;
+
+ sink_name = pa_modargs_get_value(ma, "sink", NULL);
+
+ if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
+ pa_log(__FILE__": No such sink.\n");
+ goto fail;
+ }
+
+ ss.format = PA_SAMPLE_FLOAT32;
+ ss.rate = sink->sample_spec.rate;
+ ss.channels = 1;
+
+ frequency = 440;
+ if (pa_modargs_get_value_u32(ma, "frequency", &frequency) < 0 || frequency < 1 || frequency > ss.rate/2) {
+ pa_log(__FILE__": Invalid frequency specification\n");
+ goto fail;
+ }
+
+ u->memblock = pa_memblock_new(pa_bytes_per_second(&ss), c->memblock_stat);
+ calc_sine(u->memblock->data, u->memblock->length, frequency);
+
+ snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency);
+ if (!(u->sink_input = pa_sink_input_new(sink, __FILE__, t, &ss, NULL, 0, -1)))
+ goto fail;
+
+ u->sink_input->peek = sink_input_peek;
+ u->sink_input->drop = sink_input_drop;
+ u->sink_input->kill = sink_input_kill;
+ u->sink_input->userdata = u;
+ u->sink_input->owner = m;
+
+ u->peek_index = 0;
+
+ pa_modargs_free(ma);
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(c, m);
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u = m->userdata;
+ assert(c && m);
+
+ if (!u)
+ return;
+
+ if (u->sink_input) {
+ pa_sink_input_disconnect(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ }
+
+ if (u->memblock)
+ pa_memblock_unref(u->memblock);
+ pa_xfree(u);
+}
+
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
new file mode 100644
index 00000000..e0745fc0
--- /dev/null
+++ b/src/modules/module-solaris.c
@@ -0,0 +1,488 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <signal.h>
+#include <stropts.h>
+#include <sys/conf.h>
+#include <sys/audio.h>
+
+#include <polyp/mainloop-signal.h>
+
+#include <polypcore/iochannel.h>
+#include <polypcore/sink.h>
+#include <polypcore/source.h>
+#include <polypcore/module.h>
+#include <polypcore/sample-util.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+#include "module-solaris-symdef.h"
+
+PA_MODULE_AUTHOR("Pierre Ossman")
+PA_MODULE_DESCRIPTION("Solaris Sink/Source")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("sink_name=<name for the sink> source_name=<name for the source> device=<OSS device> record=<enable source?> playback=<enable sink?> format=<sample format> channels=<number of channels> rate=<sample rate> buffer_size=<record buffer size>")
+
+struct userdata {
+ pa_sink *sink;
+ pa_source *source;
+ pa_iochannel *io;
+ pa_core *core;
+ pa_signal_event *sig;
+
+ pa_memchunk memchunk, silence;
+
+ uint32_t sample_size;
+ uint32_t buffer_size;
+ unsigned int written_bytes, read_bytes;
+
+ int fd;
+ pa_module *module;
+};
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ "source_name",
+ "device",
+ "record",
+ "playback",
+ "buffer_size",
+ "format",
+ "rate",
+ "channels",
+ NULL
+};
+
+#define DEFAULT_SINK_NAME "solaris_output"
+#define DEFAULT_SOURCE_NAME "solaris_input"
+#define DEFAULT_DEVICE "/dev/audio"
+
+#define CHUNK_SIZE 2048
+
+static void update_usage(struct userdata *u) {
+ pa_module_set_used(u->module,
+ (u->sink ? pa_idxset_size(u->sink->inputs) : 0) +
+ (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) +
+ (u->source ? pa_idxset_size(u->source->outputs) : 0));
+}
+
+static void do_write(struct userdata *u) {
+ audio_info_t info;
+ int err;
+ pa_memchunk *memchunk;
+ size_t len;
+ ssize_t r;
+
+ assert(u);
+
+ if (!u->sink || !pa_iochannel_is_writable(u->io))
+ return;
+
+ update_usage(u);
+
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ assert(err >= 0);
+
+ /*
+ * Since we cannot modify the size of the output buffer we fake it
+ * by not filling it more than u->buffer_size.
+ */
+ len = u->buffer_size;
+ len -= u->written_bytes - (info.play.samples * u->sample_size);
+
+ /*
+ * Do not fill more than half the buffer in one chunk since we only
+ * get notifications upon completion of entire chunks.
+ */
+ if (len > (u->buffer_size / 2))
+ len = u->buffer_size / 2;
+
+ if (len < u->sample_size)
+ return;
+
+ memchunk = &u->memchunk;
+
+ if (!memchunk->length)
+ if (pa_sink_render(u->sink, len, memchunk) < 0)
+ memchunk = &u->silence;
+
+ assert(memchunk->memblock);
+ assert(memchunk->memblock->data);
+ assert(memchunk->length);
+
+ if (memchunk->length < len)
+ len = memchunk->length;
+
+ if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, len)) < 0) {
+ pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ if (memchunk == &u->silence)
+ assert(r % u->sample_size == 0);
+ else {
+ u->memchunk.index += r;
+ u->memchunk.length -= r;
+
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ u->memchunk.memblock = NULL;
+ }
+ }
+
+ u->written_bytes += r;
+
+ /*
+ * Write 0 bytes which will generate a SIGPOLL when "played".
+ */
+ if (write(u->fd, NULL, 0) < 0) {
+ pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+ return;
+ }
+}
+
+static void do_read(struct userdata *u) {
+ pa_memchunk memchunk;
+ int err, l;
+ ssize_t r;
+ assert(u);
+
+ if (!u->source || !pa_iochannel_is_readable(u->io))
+ return;
+
+ update_usage(u);
+
+ err = ioctl(u->fd, I_NREAD, &l);
+ assert(err >= 0);
+
+ memchunk.memblock = pa_memblock_new(l, u->core->memblock_stat);
+ assert(memchunk.memblock);
+ if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
+ pa_memblock_unref(memchunk.memblock);
+ if (errno != EAGAIN)
+ pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ assert(r <= (ssize_t) memchunk.memblock->length);
+ memchunk.length = memchunk.memblock->length = r;
+ memchunk.index = 0;
+
+ pa_source_post(u->source, &memchunk);
+ pa_memblock_unref(memchunk.memblock);
+
+ u->read_bytes += r;
+}
+
+static void io_callback(pa_iochannel *io, void*userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_write(u);
+ do_read(u);
+}
+
+void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_write(u);
+}
+
+static pa_usec_t sink_get_latency_cb(pa_sink *s) {
+ pa_usec_t r = 0;
+ audio_info_t info;
+ int err;
+ struct userdata *u = s->userdata;
+ assert(s && u && u->sink);
+
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ assert(err >= 0);
+
+ r += pa_bytes_to_usec(u->written_bytes, &s->sample_spec);
+ r -= pa_bytes_to_usec(info.play.samples * u->sample_size, &s->sample_spec);
+
+ if (u->memchunk.memblock)
+ r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
+
+ return r;
+}
+
+static pa_usec_t source_get_latency_cb(pa_source *s) {
+ pa_usec_t r = 0;
+ struct userdata *u = s->userdata;
+ audio_info_t info;
+ int err;
+ assert(s && u && u->source);
+
+ err = ioctl(u->fd, AUDIO_GETINFO, &info);
+ assert(err >= 0);
+
+ r += pa_bytes_to_usec(info.record.samples * u->sample_size, &s->sample_spec);
+ r -= pa_bytes_to_usec(u->read_bytes, &s->sample_spec);
+
+ return r;
+}
+
+static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+
+ if (mode != O_RDONLY) {
+ info.play.sample_rate = ss->rate;
+ info.play.channels = ss->channels;
+ switch (ss->format) {
+ case PA_SAMPLE_U8:
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ break;
+ case PA_SAMPLE_ALAW:
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ALAW;
+ break;
+ case PA_SAMPLE_ULAW:
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ break;
+ case PA_SAMPLE_S16NE:
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ if (mode != O_WRONLY) {
+ info.record.sample_rate = ss->rate;
+ info.record.channels = ss->channels;
+ switch (ss->format) {
+ case PA_SAMPLE_U8:
+ info.record.precision = 8;
+ info.record.encoding = AUDIO_ENCODING_LINEAR;
+ break;
+ case PA_SAMPLE_ALAW:
+ info.record.precision = 8;
+ info.record.encoding = AUDIO_ENCODING_ALAW;
+ break;
+ case PA_SAMPLE_ULAW:
+ info.record.precision = 8;
+ info.record.encoding = AUDIO_ENCODING_ULAW;
+ break;
+ case PA_SAMPLE_S16NE:
+ info.record.precision = 16;
+ info.record.encoding = AUDIO_ENCODING_LINEAR;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
+ if (errno == EINVAL)
+ pa_log(__FILE__": AUDIO_SETINFO: Unsupported sample format.\n");
+ else
+ pa_log(__FILE__": AUDIO_SETINFO: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pa_solaris_set_buffer(int fd, int buffer_size) {
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+
+ info.record.buffer_size = buffer_size;
+
+ if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
+ if (errno == EINVAL)
+ pa_log(__FILE__": AUDIO_SETINFO: Unsupported buffer size.\n");
+ else
+ pa_log(__FILE__": AUDIO_SETINFO: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct userdata *u = NULL;
+ const char *p;
+ int fd = -1;
+ int buffer_size;
+ int mode;
+ int record = 1, playback = 1;
+ pa_sample_spec ss;
+ pa_modargs *ma = NULL;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments.\n");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
+ pa_log(__FILE__": record= and playback= expect numeric argument.\n");
+ goto fail;
+ }
+
+ if (!playback && !record) {
+ pa_log(__FILE__": neither playback nor record enabled for device.\n");
+ goto fail;
+ }
+
+ mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+
+ buffer_size = 16384;
+ if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
+ pa_log(__FILE__": failed to parse buffer size argument\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log(__FILE__": failed to parse sample specification\n");
+ goto fail;
+ }
+
+ if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
+ goto fail;
+
+ pa_log_info(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+
+ if (pa_solaris_auto_format(fd, mode, &ss) < 0)
+ goto fail;
+
+ if ((mode != O_WRONLY) && (buffer_size >= 1))
+ if (pa_solaris_set_buffer(fd, buffer_size) < 0)
+ goto fail;
+
+ u = pa_xmalloc(sizeof(struct userdata));
+ u->core = c;
+
+ if (mode != O_WRONLY) {
+ u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL);
+ assert(u->source);
+ u->source->userdata = u;
+ u->source->get_latency = source_get_latency_cb;
+ pa_source_set_owner(u->source, m);
+ u->source->description = pa_sprintf_malloc("Solaris PCM on '%s'", p);
+ } else
+ u->source = NULL;
+
+ if (mode != O_RDONLY) {
+ u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL);
+ assert(u->sink);
+ u->sink->get_latency = sink_get_latency_cb;
+ u->sink->userdata = u;
+ pa_sink_set_owner(u->sink, m);
+ u->sink->description = pa_sprintf_malloc("Solaris PCM on '%s'", p);
+ } else
+ u->sink = NULL;
+
+ assert(u->source || u->sink);
+
+ u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
+ assert(u->io);
+ pa_iochannel_set_callback(u->io, io_callback, u);
+ u->fd = fd;
+
+ u->memchunk.memblock = NULL;
+ u->memchunk.length = 0;
+ u->sample_size = pa_frame_size(&ss);
+ u->buffer_size = buffer_size;
+
+ u->silence.memblock = pa_memblock_new(u->silence.length = CHUNK_SIZE, u->core->memblock_stat);
+ assert(u->silence.memblock);
+ pa_silence_memblock(u->silence.memblock, &ss);
+ u->silence.index = 0;
+
+ u->written_bytes = 0;
+ u->read_bytes = 0;
+
+ u->module = m;
+ m->userdata = u;
+
+ u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
+ assert(u->sig);
+ ioctl(u->fd, I_SETSIG, S_MSG);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ ioctl(u->fd, I_SETSIG, 0);
+ pa_signal_free(u->sig);
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+ if (u->silence.memblock)
+ pa_memblock_unref(u->silence.memblock);
+
+ if (u->sink) {
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+ }
+
+ if (u->source) {
+ pa_source_disconnect(u->source);
+ pa_source_unref(u->source);
+ }
+
+ pa_iochannel_free(u->io);
+ pa_xfree(u);
+}
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
new file mode 100644
index 00000000..5ee10fda
--- /dev/null
+++ b/src/modules/module-tunnel.c
@@ -0,0 +1,688 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <polypcore/module.h>
+#include <polypcore/util.h>
+#include <polypcore/modargs.h>
+#include <polypcore/log.h>
+#include <polypcore/subscribe.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/sink-input.h>
+#include <polypcore/pdispatch.h>
+#include <polypcore/pstream.h>
+#include <polypcore/pstream-util.h>
+#include <polypcore/authkey.h>
+#include <polypcore/socket-client.h>
+#include <polypcore/socket-util.h>
+#include <polypcore/authkey-prop.h>
+
+#ifdef TUNNEL_SINK
+#include "module-tunnel-sink-symdef.h"
+PA_MODULE_DESCRIPTION("Tunnel module for sinks")
+PA_MODULE_USAGE("server=<address> sink=<remote sink name> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> sink_name=<name for the local sink>")
+#else
+#include "module-tunnel-source-symdef.h"
+PA_MODULE_DESCRIPTION("Tunnel module for sources")
+PA_MODULE_USAGE("server=<address> source=<remote source name> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> source_name=<name for the local source>")
+#endif
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+
+#define DEFAULT_SINK_NAME "tunnel"
+#define DEFAULT_SOURCE_NAME "tunnel"
+
+#define DEFAULT_TLENGTH (44100*2*2/10) //(10240*8)
+#define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2)
+#define DEFAULT_MINREQ 512
+#define DEFAULT_PREBUF (DEFAULT_TLENGTH-DEFAULT_MINREQ)
+#define DEFAULT_FRAGSIZE 1024
+
+#define DEFAULT_TIMEOUT 5
+
+#define LATENCY_INTERVAL 10
+
+static const char* const valid_modargs[] = {
+ "server",
+ "cookie",
+ "format",
+ "channels",
+ "rate",
+#ifdef TUNNEL_SINK
+ "sink_name",
+ "sink",
+#else
+ "source_name",
+ "source",
+#endif
+ NULL,
+};
+
+static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+#ifdef TUNNEL_SINK
+static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+#endif
+
+static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = {
+#ifdef TUNNEL_SINK
+ [PA_COMMAND_REQUEST] = command_request,
+#endif
+ [PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed,
+ [PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed
+};
+
+struct userdata {
+ pa_socket_client *client;
+ pa_pstream *pstream;
+ pa_pdispatch *pdispatch;
+
+ char *server_name;
+#ifdef TUNNEL_SINK
+ char *sink_name;
+ pa_sink *sink;
+ uint32_t requested_bytes;
+#else
+ char *source_name;
+ pa_source *source;
+#endif
+
+ pa_module *module;
+ pa_core *core;
+
+ uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
+
+ uint32_t ctag;
+ uint32_t device_index;
+ uint32_t channel;
+
+ pa_usec_t host_latency;
+
+ pa_time_event *time_event;
+
+ int auth_cookie_in_property;
+};
+
+static void close_stuff(struct userdata *u) {
+ assert(u);
+
+ if (u->pstream) {
+ pa_pstream_close(u->pstream);
+ pa_pstream_unref(u->pstream);
+ u->pstream = NULL;
+ }
+
+ if (u->pdispatch) {
+ pa_pdispatch_unref(u->pdispatch);
+ u->pdispatch = NULL;
+ }
+
+ if (u->client) {
+ pa_socket_client_unref(u->client);
+ u->client = NULL;
+ }
+
+#ifdef TUNNEL_SINK
+ if (u->sink) {
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+ u->sink = NULL;
+ }
+#else
+ if (u->source) {
+ pa_source_disconnect(u->source);
+ pa_source_unref(u->source);
+ u->source = NULL;
+ }
+#endif
+
+ if (u->time_event) {
+ u->core->mainloop->time_free(u->time_event);
+ u->time_event = NULL;
+ }
+}
+
+static void die(struct userdata *u) {
+ assert(u);
+ close_stuff(u);
+ pa_module_unload_request(u->module);
+}
+
+static void command_stream_killed(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ assert(pd && t && u && u->pdispatch == pd);
+
+ pa_log(__FILE__": stream killed\n");
+ die(u);
+}
+
+#ifdef TUNNEL_SINK
+static void send_prebuf_request(struct userdata *u) {
+ pa_tagstruct *t;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PREBUF_PLAYBACK_STREAM);
+ pa_tagstruct_putu32(t, u->ctag++);
+ pa_tagstruct_putu32(t, u->channel);
+ pa_pstream_send_tagstruct(u->pstream, t);
+}
+
+static void send_bytes(struct userdata *u) {
+ assert(u);
+
+ if (!u->pstream)
+ return;
+
+ while (u->requested_bytes > 0) {
+ pa_memchunk chunk;
+ if (pa_sink_render(u->sink, u->requested_bytes, &chunk) < 0) {
+
+
+ if (u->requested_bytes >= DEFAULT_TLENGTH-DEFAULT_PREBUF)
+ send_prebuf_request(u);
+
+ return;
+ }
+
+ pa_pstream_send_memblock(u->pstream, u->channel, 0, &chunk);
+ pa_memblock_unref(chunk.memblock);
+
+ if (chunk.length > u->requested_bytes)
+ u->requested_bytes = 0;
+ else
+ u->requested_bytes -= chunk.length;
+ }
+}
+
+static void command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ uint32_t bytes, channel;
+ assert(pd && command == PA_COMMAND_REQUEST && t && u && u->pdispatch == pd);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_getu32(t, &bytes) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_log(__FILE__": invalid protocol reply\n");
+ die(u);
+ return;
+ }
+
+ if (channel != u->channel) {
+ pa_log(__FILE__": recieved data for invalid channel\n");
+ die(u);
+ return;
+ }
+
+ u->requested_bytes += bytes;
+ send_bytes(u);
+}
+
+#endif
+
+static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ pa_usec_t buffer_usec, sink_usec, source_usec, transport_usec;
+ int playing;
+ uint32_t queue_length;
+ uint64_t counter;
+ struct timeval local, remote, now;
+ assert(pd && u);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (command == PA_COMMAND_ERROR)
+ pa_log(__FILE__": failed to get latency.\n");
+ else
+ pa_log(__FILE__": protocol error.\n");
+ die(u);
+ return;
+ }
+
+ if (pa_tagstruct_get_usec(t, &buffer_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &source_usec) < 0 ||
+ pa_tagstruct_get_boolean(t, &playing) < 0 ||
+ pa_tagstruct_getu32(t, &queue_length) < 0 ||
+ pa_tagstruct_get_timeval(t, &local) < 0 ||
+ pa_tagstruct_get_timeval(t, &remote) < 0 ||
+ pa_tagstruct_getu64(t, &counter) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_log(__FILE__": invalid reply.\n");
+ die(u);
+ return;
+ }
+
+ pa_gettimeofday(&now);
+
+ if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
+ /* local and remote seem to have synchronized clocks */
+#ifdef TUNNEL_SINK
+ transport_usec = pa_timeval_diff(&remote, &local);
+#else
+ transport_usec = pa_timeval_diff(&now, &remote);
+#endif
+ } else
+ transport_usec = pa_timeval_diff(&now, &local)/2;
+
+#ifdef TUNNEL_SINK
+ u->host_latency = sink_usec + transport_usec;
+#else
+ u->host_latency = source_usec + transport_usec;
+ if (u->host_latency > sink_usec)
+ u->host_latency -= sink_usec;
+ else
+ u->host_latency = 0;
+#endif
+
+/* pa_log(__FILE__": estimated host latency: %0.0f usec\n", (double) u->host_latency); */
+}
+
+static void request_latency(struct userdata *u) {
+ pa_tagstruct *t;
+ struct timeval now;
+ uint32_t tag;
+ assert(u);
+
+ t = pa_tagstruct_new(NULL, 0);
+#ifdef TUNNEL_SINK
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
+#else
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY);
+#endif
+ pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_putu32(t, u->channel);
+
+ pa_gettimeofday(&now);
+ pa_tagstruct_put_timeval(t, &now);
+ pa_tagstruct_putu64(t, 0);
+
+ pa_pstream_send_tagstruct(u->pstream, t);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u);
+}
+
+static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ assert(pd && u && u->pdispatch == pd);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (command == PA_COMMAND_ERROR)
+ pa_log(__FILE__": failed to create stream.\n");
+ else
+ pa_log(__FILE__": protocol error.\n");
+ die(u);
+ return;
+ }
+
+ if (pa_tagstruct_getu32(t, &u->channel) < 0 ||
+ pa_tagstruct_getu32(t, &u->device_index) < 0 ||
+#ifdef TUNNEL_SINK
+ pa_tagstruct_getu32(t, &u->requested_bytes) < 0 ||
+#endif
+ !pa_tagstruct_eof(t)) {
+ pa_log(__FILE__": invalid reply.\n");
+ die(u);
+ return;
+ }
+
+ request_latency(u);
+#ifdef TUNNEL_SINK
+ send_bytes(u);
+#endif
+}
+
+static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct userdata *u = userdata;
+ pa_tagstruct *reply;
+ char name[256], un[128], hn[128];
+ assert(pd && u && u->pdispatch == pd);
+
+ if (command != PA_COMMAND_REPLY || !pa_tagstruct_eof(t)) {
+ if (command == PA_COMMAND_ERROR)
+ pa_log(__FILE__": failed to authenticate\n");
+ else
+ pa_log(__FILE__": protocol error.\n");
+ die(u);
+ return;
+ }
+#ifdef TUNNEL_SINK
+ snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', sink '%s'",
+ pa_get_host_name(hn, sizeof(hn)),
+ pa_get_user_name(un, sizeof(un)),
+ u->sink->name);
+#else
+ snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', source '%s'",
+ pa_get_host_name(hn, sizeof(hn)),
+ pa_get_user_name(un, sizeof(un)),
+ u->source->name);
+#endif
+
+ reply = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
+ pa_tagstruct_putu32(reply, tag = u->ctag++);
+ pa_tagstruct_puts(reply, name);
+ pa_pstream_send_tagstruct(u->pstream, reply);
+ /* We ignore the server's reply here */
+
+ reply = pa_tagstruct_new(NULL, 0);
+#ifdef TUNNEL_SINK
+ pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM);
+ pa_tagstruct_putu32(reply, tag = u->ctag++);
+ pa_tagstruct_puts(reply, name);
+ pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec);
+ pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
+ pa_tagstruct_puts(reply, u->sink_name);
+ pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
+ pa_tagstruct_put_boolean(reply, 0);
+ pa_tagstruct_putu32(reply, DEFAULT_TLENGTH);
+ pa_tagstruct_putu32(reply, DEFAULT_PREBUF);
+ pa_tagstruct_putu32(reply, DEFAULT_MINREQ);
+ pa_tagstruct_putu32(reply, PA_VOLUME_NORM);
+#else
+ pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM);
+ pa_tagstruct_putu32(reply, tag = u->ctag++);
+ pa_tagstruct_puts(reply, name);
+ pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec);
+ pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
+ pa_tagstruct_puts(reply, u->source_name);
+ pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
+ pa_tagstruct_put_boolean(reply, 0);
+ pa_tagstruct_putu32(reply, DEFAULT_FRAGSIZE);
+#endif
+
+ pa_pstream_send_tagstruct(u->pstream, reply);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u);
+}
+
+static void pstream_die_callback(pa_pstream *p, void *userdata) {
+ struct userdata *u = userdata;
+ assert(p && u);
+
+ pa_log(__FILE__": stream died.\n");
+ die(u);
+}
+
+
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) {
+ struct userdata *u = userdata;
+ assert(p && packet && u);
+
+ if (pa_pdispatch_run(u->pdispatch, packet, u) < 0) {
+ pa_log(__FILE__": invalid packet\n");
+ die(u);
+ }
+}
+
+#ifndef TUNNEL_SINK
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata) {
+ struct userdata *u = userdata;
+ assert(p && chunk && u);
+
+ if (channel != u->channel) {
+ pa_log(__FILE__": recieved memory block on bad channel.\n");
+ die(u);
+ return;
+ }
+
+ pa_source_post(u->source, chunk);
+}
+#endif
+
+static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
+ struct userdata *u = userdata;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(sc && u && u->client == sc);
+
+ pa_socket_client_unref(u->client);
+ u->client = NULL;
+
+ if (!io) {
+ pa_log(__FILE__": connection failed.\n");
+ pa_module_unload_request(u->module);
+ return;
+ }
+
+ u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->memblock_stat);
+ u->pdispatch = pa_pdispatch_new(u->core->mainloop, command_table, PA_COMMAND_MAX);
+
+ pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
+ pa_pstream_set_recieve_packet_callback(u->pstream, pstream_packet_callback, u);
+#ifndef TUNNEL_SINK
+ pa_pstream_set_recieve_memblock_callback(u->pstream, pstream_memblock_callback, u);
+#endif
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
+ pa_tagstruct_putu32(t, tag = u->ctag++);
+ pa_tagstruct_put_arbitrary(t, u->auth_cookie, sizeof(u->auth_cookie));
+ pa_pstream_send_tagstruct(u->pstream, t);
+ pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u);
+
+}
+
+#ifdef TUNNEL_SINK
+static void sink_notify(pa_sink*sink) {
+ struct userdata *u;
+ assert(sink && sink->userdata);
+ u = sink->userdata;
+
+ send_bytes(u);
+}
+
+static pa_usec_t sink_get_latency(pa_sink *sink) {
+ struct userdata *u;
+ uint32_t l;
+ pa_usec_t usec = 0;
+ assert(sink && sink->userdata);
+ u = sink->userdata;
+
+ l = DEFAULT_TLENGTH;
+
+ if (l > u->requested_bytes) {
+ l -= u->requested_bytes;
+ usec += pa_bytes_to_usec(l, &u->sink->sample_spec);
+ }
+
+ usec += u->host_latency;
+
+ return usec;
+}
+#else
+static pa_usec_t source_get_latency(pa_source *source) {
+ struct userdata *u;
+ assert(source && source->userdata);
+ u = source->userdata;
+
+ return u->host_latency;
+}
+#endif
+
+static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+ struct timeval ntv;
+ assert(m && e && u);
+
+ request_latency(u);
+
+ pa_gettimeofday(&ntv);
+ ntv.tv_sec += LATENCY_INTERVAL;
+ m->time_restart(e, &ntv);
+}
+
+static int load_key(struct userdata *u, const char*fn) {
+ assert(u);
+
+ u->auth_cookie_in_property = 0;
+
+ if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
+ pa_log_debug(__FILE__": using already loaded auth cookie.\n");
+ pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+ u->auth_cookie_in_property = 1;
+ return 0;
+ }
+
+ if (!fn)
+ fn = PA_NATIVE_COOKIE_FILE;
+
+ if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
+ return -1;
+
+ pa_log_debug(__FILE__": loading cookie from disk.\n");
+
+ if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
+ u->auth_cookie_in_property = 1;
+
+ return 0;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u = NULL;
+ pa_sample_spec ss;
+ struct timeval ntv;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ u = pa_xmalloc(sizeof(struct userdata));
+ m->userdata = u;
+ u->module = m;
+ u->core = c;
+ u->client = NULL;
+ u->pdispatch = NULL;
+ u->pstream = NULL;
+ u->server_name = NULL;
+#ifdef TUNNEL_SINK
+ u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));;
+ u->sink = NULL;
+ u->requested_bytes = 0;
+#else
+ u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
+ u->source = NULL;
+#endif
+ u->ctag = 1;
+ u->device_index = u->channel = PA_INVALID_INDEX;
+ u->host_latency = 0;
+ u->auth_cookie_in_property = 0;
+ u->time_event = NULL;
+
+ if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
+ goto fail;
+
+ if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) {
+ pa_log(__FILE__": no server specified.\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log(__FILE__": invalid sample format specification\n");
+ goto fail;
+ }
+
+ if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
+ pa_log(__FILE__": failed to connect to server '%s'\n", u->server_name);
+ goto fail;
+ }
+
+ if (!u->client)
+ goto fail;
+
+ pa_socket_client_set_callback(u->client, on_connection, u);
+
+#ifdef TUNNEL_SINK
+ if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) {
+ pa_log(__FILE__": failed to create sink.\n");
+ goto fail;
+ }
+
+ u->sink->notify = sink_notify;
+ u->sink->get_latency = sink_get_latency;
+ u->sink->userdata = u;
+ u->sink->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->sink_name ? u->sink_name : "", u->sink_name ? "@" : "", u->server_name);
+
+ pa_sink_set_owner(u->sink, m);
+#else
+ if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL))) {
+ pa_log(__FILE__": failed to create source.\n");
+ goto fail;
+ }
+
+ u->source->get_latency = source_get_latency;
+ u->source->userdata = u;
+ u->source->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->source_name ? u->source_name : "", u->source_name ? "@" : "", u->server_name);
+
+ pa_source_set_owner(u->source, m);
+#endif
+
+ pa_gettimeofday(&ntv);
+ ntv.tv_sec += LATENCY_INTERVAL;
+ u->time_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, u);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ pa__done(c, m);
+
+ if (ma)
+ pa_modargs_free(ma);
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata* u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ close_stuff(u);
+
+ if (u->auth_cookie_in_property)
+ pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME);
+
+#ifdef TUNNEL_SINK
+ pa_xfree(u->sink_name);
+#else
+ pa_xfree(u->source_name);
+#endif
+ pa_xfree(u->server_name);
+
+ pa_xfree(u);
+}
+
+
diff --git a/src/modules/module-waveout.c b/src/modules/module-waveout.c
new file mode 100644
index 00000000..8809c31d
--- /dev/null
+++ b/src/modules/module-waveout.c
@@ -0,0 +1,583 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <windows.h>
+#include <mmsystem.h>
+#include <assert.h>
+
+#include <polyp/mainloop-api.h>
+
+#include <polypcore/sink.h>
+#include <polypcore/source.h>
+#include <polypcore/module.h>
+#include <polypcore/modargs.h>
+#include <polypcore/sample-util.h>
+#include <polypcore/util.h>
+#include <polypcore/log.h>
+#include <polypcore/xmalloc.h>
+
+#include "module-waveout-symdef.h"
+
+PA_MODULE_AUTHOR("Pierre Ossman")
+PA_MODULE_DESCRIPTION("Windows waveOut Sink/Source")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("sink_name=<name for the sink> source_name=<name for the source> record=<enable source?> playback=<enable sink?> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size>")
+
+#define DEFAULT_SINK_NAME "wave_output"
+#define DEFAULT_SOURCE_NAME "wave_input"
+
+struct userdata {
+ pa_sink *sink;
+ pa_source *source;
+ pa_core *core;
+ pa_time_event *event;
+ pa_defer_event *defer;
+ pa_usec_t poll_timeout;
+
+ uint32_t fragments, fragment_size;
+
+ uint32_t free_ofrags, free_ifrags;
+
+ DWORD written_bytes;
+
+ int cur_ohdr, cur_ihdr;
+ unsigned int oremain;
+ WAVEHDR *ohdrs, *ihdrs;
+ pa_memchunk silence;
+
+ HWAVEOUT hwo;
+ HWAVEIN hwi;
+ pa_module *module;
+
+ CRITICAL_SECTION crit;
+};
+
+static const char* const valid_modargs[] = {
+ "sink_name",
+ "source_name",
+ "record",
+ "playback",
+ "fragments",
+ "fragment_size",
+ "format",
+ "rate",
+ "channels",
+ NULL
+};
+
+static void update_usage(struct userdata *u) {
+ pa_module_set_used(u->module,
+ (u->sink ? pa_idxset_size(u->sink->inputs) : 0) +
+ (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) +
+ (u->source ? pa_idxset_size(u->source->outputs) : 0));
+}
+
+static void do_write(struct userdata *u)
+{
+ uint32_t free_frags, remain;
+ pa_memchunk memchunk, *cur_chunk;
+ WAVEHDR *hdr;
+ MMRESULT res;
+
+ if (!u->sink)
+ return;
+
+ EnterCriticalSection(&u->crit);
+
+ free_frags = u->free_ofrags;
+ u->free_ofrags = 0;
+
+ LeaveCriticalSection(&u->crit);
+
+ while (free_frags) {
+ hdr = &u->ohdrs[u->cur_ohdr];
+ if (hdr->dwFlags & WHDR_PREPARED)
+ waveOutUnprepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
+
+ remain = u->oremain;
+ while (remain) {
+ cur_chunk = &memchunk;
+
+ if (pa_sink_render(u->sink, remain, cur_chunk) < 0) {
+ /*
+ * Don't fill with silence unless we're getting close to
+ * underflowing.
+ */
+ if (free_frags > u->fragments/2)
+ cur_chunk = &u->silence;
+ else {
+ EnterCriticalSection(&u->crit);
+
+ u->free_ofrags += free_frags;
+
+ LeaveCriticalSection(&u->crit);
+
+ u->oremain = remain;
+ return;
+ }
+ }
+
+ assert(cur_chunk->memblock);
+ assert(cur_chunk->memblock->data);
+ assert(cur_chunk->length);
+
+ memcpy(hdr->lpData + u->fragment_size - remain,
+ (char*)cur_chunk->memblock->data + cur_chunk->index,
+ (cur_chunk->length < remain)?cur_chunk->length:remain);
+
+ remain -= (cur_chunk->length < remain)?cur_chunk->length:remain;
+
+ if (cur_chunk != &u->silence) {
+ pa_memblock_unref(cur_chunk->memblock);
+ cur_chunk->memblock = NULL;
+ }
+ }
+
+ res = waveOutPrepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
+ if (res != MMSYSERR_NOERROR) {
+ pa_log_error(__FILE__ ": ERROR: Unable to prepare waveOut block: %d\n",
+ res);
+ }
+ res = waveOutWrite(u->hwo, hdr, sizeof(WAVEHDR));
+ if (res != MMSYSERR_NOERROR) {
+ pa_log_error(__FILE__ ": ERROR: Unable to write waveOut block: %d\n",
+ res);
+ }
+
+ u->written_bytes += u->fragment_size;
+
+ free_frags--;
+ u->cur_ohdr++;
+ u->cur_ohdr %= u->fragments;
+ u->oremain = u->fragment_size;
+ }
+}
+
+static void do_read(struct userdata *u)
+{
+ uint32_t free_frags;
+ pa_memchunk memchunk;
+ WAVEHDR *hdr;
+ MMRESULT res;
+
+ if (!u->source)
+ return;
+
+ EnterCriticalSection(&u->crit);
+
+ free_frags = u->free_ifrags;
+ u->free_ifrags = 0;
+
+ LeaveCriticalSection(&u->crit);
+
+ while (free_frags) {
+ hdr = &u->ihdrs[u->cur_ihdr];
+ if (hdr->dwFlags & WHDR_PREPARED)
+ waveInUnprepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
+
+ if (hdr->dwBytesRecorded) {
+ memchunk.memblock = pa_memblock_new(hdr->dwBytesRecorded, u->core->memblock_stat);
+ assert(memchunk.memblock);
+
+ memcpy((char*)memchunk.memblock->data, hdr->lpData, hdr->dwBytesRecorded);
+
+ memchunk.length = memchunk.memblock->length = hdr->dwBytesRecorded;
+ memchunk.index = 0;
+
+ pa_source_post(u->source, &memchunk);
+ pa_memblock_unref(memchunk.memblock);
+ }
+
+ res = waveInPrepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
+ if (res != MMSYSERR_NOERROR) {
+ pa_log_error(__FILE__ ": ERROR: Unable to prepare waveIn block: %d\n",
+ res);
+ }
+ res = waveInAddBuffer(u->hwi, hdr, sizeof(WAVEHDR));
+ if (res != MMSYSERR_NOERROR) {
+ pa_log_error(__FILE__ ": ERROR: Unable to add waveIn block: %d\n",
+ res);
+ }
+
+ free_frags--;
+ u->cur_ihdr++;
+ u->cur_ihdr %= u->fragments;
+ }
+}
+
+static void poll_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+ struct timeval ntv;
+
+ assert(u);
+
+ update_usage(u);
+
+ do_write(u);
+ do_read(u);
+
+ pa_gettimeofday(&ntv);
+ pa_timeval_add(&ntv, u->poll_timeout);
+
+ a->time_restart(e, &ntv);
+}
+
+static void defer_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
+ struct userdata *u = userdata;
+
+ assert(u);
+
+ a->defer_enable(e, 0);
+
+ do_write(u);
+ do_read(u);
+}
+
+static void CALLBACK chunk_done_cb(HWAVEOUT hwo, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) {
+ struct userdata *u = (struct userdata *)inst;
+
+ if (msg != WOM_DONE)
+ return;
+
+ EnterCriticalSection(&u->crit);
+
+ u->free_ofrags++;
+ assert(u->free_ofrags <= u->fragments);
+
+ LeaveCriticalSection(&u->crit);
+}
+
+static void CALLBACK chunk_ready_cb(HWAVEIN hwi, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) {
+ struct userdata *u = (struct userdata *)inst;
+
+ if (msg != WIM_DATA)
+ return;
+
+ EnterCriticalSection(&u->crit);
+
+ u->free_ifrags++;
+ assert(u->free_ifrags <= u->fragments);
+
+ LeaveCriticalSection(&u->crit);
+}
+
+static pa_usec_t sink_get_latency_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ uint32_t free_frags;
+ MMTIME mmt;
+ assert(s && u && u->sink);
+
+ memset(&mmt, 0, sizeof(mmt));
+ mmt.wType = TIME_BYTES;
+ if (waveOutGetPosition(u->hwo, &mmt, sizeof(mmt)) == MMSYSERR_NOERROR)
+ return pa_bytes_to_usec(u->written_bytes - mmt.u.cb, &s->sample_spec);
+ else {
+ EnterCriticalSection(&u->crit);
+
+ free_frags = u->free_ofrags;
+
+ LeaveCriticalSection(&u->crit);
+
+ return pa_bytes_to_usec((u->fragments - free_frags) * u->fragment_size,
+ &s->sample_spec);
+ }
+}
+
+static pa_usec_t source_get_latency_cb(pa_source *s) {
+ pa_usec_t r = 0;
+ struct userdata *u = s->userdata;
+ uint32_t free_frags;
+ assert(s && u && u->sink);
+
+ EnterCriticalSection(&u->crit);
+
+ free_frags = u->free_ifrags;
+
+ LeaveCriticalSection(&u->crit);
+
+ r += pa_bytes_to_usec((free_frags + 1) * u->fragment_size, &s->sample_spec);
+
+ fprintf(stderr, "Latency: %d us\n", (int)r);
+
+ return r;
+}
+
+static void notify_sink_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ assert(u);
+
+ u->core->mainloop->defer_enable(u->defer, 1);
+}
+
+static void notify_source_cb(pa_source *s) {
+ struct userdata *u = s->userdata;
+ assert(u);
+
+ u->core->mainloop->defer_enable(u->defer, 1);
+}
+
+static int ss_to_waveformat(pa_sample_spec *ss, LPWAVEFORMATEX wf) {
+ wf->wFormatTag = WAVE_FORMAT_PCM;
+
+ if (ss->channels > 2) {
+ pa_log_error(__FILE__": ERROR: More than two channels not supported.\n");
+ return -1;
+ }
+
+ wf->nChannels = ss->channels;
+
+ switch (ss->rate) {
+ case 8000:
+ case 11025:
+ case 22005:
+ case 44100:
+ break;
+ default:
+ pa_log_error(__FILE__": ERROR: Unsupported sample rate.\n");
+ return -1;
+ }
+
+ wf->nSamplesPerSec = ss->rate;
+
+ if (ss->format == PA_SAMPLE_U8)
+ wf->wBitsPerSample = 8;
+ else if (ss->format == PA_SAMPLE_S16NE)
+ wf->wBitsPerSample = 16;
+ else {
+ pa_log_error(__FILE__": ERROR: Unsupported sample format.\n");
+ return -1;
+ }
+
+ wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample/8;
+ wf->nAvgBytesPerSec = wf->nSamplesPerSec * wf->nBlockAlign;
+
+ wf->cbSize = 0;
+
+ return 0;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct userdata *u = NULL;
+ HWAVEOUT hwo = INVALID_HANDLE_VALUE;
+ HWAVEIN hwi = INVALID_HANDLE_VALUE;
+ WAVEFORMATEX wf;
+ int nfrags, frag_size;
+ int record = 1, playback = 1;
+ pa_sample_spec ss;
+ pa_modargs *ma = NULL;
+ unsigned int i;
+ struct timeval tv;
+
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments.\n");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
+ pa_log(__FILE__": record= and playback= expect boolean argument.\n");
+ goto fail;
+ }
+
+ if (!playback && !record) {
+ pa_log(__FILE__": neither playback nor record enabled for device.\n");
+ goto fail;
+ }
+
+ nfrags = 20;
+ frag_size = 1024;
+ if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
+ pa_log(__FILE__": failed to parse fragments arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log(__FILE__": failed to parse sample specification\n");
+ goto fail;
+ }
+
+ if (ss_to_waveformat(&ss, &wf) < 0)
+ goto fail;
+
+ u = pa_xmalloc(sizeof(struct userdata));
+
+ if (record) {
+ if (waveInOpen(&hwi, WAVE_MAPPER, &wf, (DWORD_PTR)chunk_ready_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
+ goto fail;
+ if (waveInStart(hwi) != MMSYSERR_NOERROR)
+ goto fail;
+ pa_log_debug(__FILE__": Opened waveIn subsystem.\n");
+ }
+
+ if (playback) {
+ if (waveOutOpen(&hwo, WAVE_MAPPER, &wf, (DWORD_PTR)chunk_done_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
+ goto fail;
+ pa_log_debug(__FILE__": Opened waveOut subsystem.\n");
+ }
+
+ InitializeCriticalSection(&u->crit);
+
+ if (hwi != INVALID_HANDLE_VALUE) {
+ u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL);
+ assert(u->source);
+ u->source->userdata = u;
+ u->source->notify = notify_source_cb;
+ u->source->get_latency = source_get_latency_cb;
+ pa_source_set_owner(u->source, m);
+ u->source->description = pa_sprintf_malloc("Windows waveIn PCM");
+ } else
+ u->source = NULL;
+
+ if (hwo != INVALID_HANDLE_VALUE) {
+ u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL);
+ assert(u->sink);
+ u->sink->notify = notify_sink_cb;
+ u->sink->get_latency = sink_get_latency_cb;
+ u->sink->userdata = u;
+ pa_sink_set_owner(u->sink, m);
+ u->sink->description = pa_sprintf_malloc("Windows waveOut PCM");
+ } else
+ u->sink = NULL;
+
+ assert(u->source || u->sink);
+
+ u->core = c;
+ u->hwi = hwi;
+ u->hwo = hwo;
+
+ u->fragments = nfrags;
+ u->free_ifrags = u->fragments;
+ u->free_ofrags = u->fragments;
+ u->fragment_size = frag_size - (frag_size % pa_frame_size(&ss));
+
+ u->written_bytes = 0;
+
+ u->oremain = u->fragment_size;
+
+ u->poll_timeout = pa_bytes_to_usec(u->fragments * u->fragment_size / 3, &ss);
+
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, u->poll_timeout);
+
+ u->event = c->mainloop->time_new(c->mainloop, &tv, poll_cb, u);
+ assert(u->event);
+
+ u->defer = c->mainloop->defer_new(c->mainloop, defer_cb, u);
+ assert(u->defer);
+ c->mainloop->defer_enable(u->defer, 0);
+
+ u->cur_ihdr = 0;
+ u->cur_ohdr = 0;
+ u->ihdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
+ assert(u->ihdrs);
+ u->ohdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
+ assert(u->ohdrs);
+ for (i = 0;i < u->fragments;i++) {
+ u->ihdrs[i].dwBufferLength = u->fragment_size;
+ u->ohdrs[i].dwBufferLength = u->fragment_size;
+ u->ihdrs[i].lpData = pa_xmalloc(u->fragment_size);
+ assert(u->ihdrs);
+ u->ohdrs[i].lpData = pa_xmalloc(u->fragment_size);
+ assert(u->ohdrs);
+ }
+
+ u->silence.length = u->fragment_size;
+ u->silence.memblock = pa_memblock_new(u->silence.length, u->core->memblock_stat);
+ assert(u->silence.memblock);
+ pa_silence_memblock(u->silence.memblock, &ss);
+ u->silence.index = 0;
+
+ u->module = m;
+ m->userdata = u;
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (hwi != INVALID_HANDLE_VALUE)
+ waveInClose(hwi);
+
+ if (hwo != INVALID_HANDLE_VALUE)
+ waveOutClose(hwo);
+
+ if (u)
+ pa_xfree(u);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ unsigned int i;
+
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->event)
+ c->mainloop->time_free(u->event);
+
+ if (u->defer)
+ c->mainloop->defer_free(u->defer);
+
+ if (u->sink) {
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+ }
+
+ if (u->source) {
+ pa_source_disconnect(u->source);
+ pa_source_unref(u->source);
+ }
+
+ if (u->hwi != INVALID_HANDLE_VALUE) {
+ waveInReset(u->hwi);
+ waveInClose(u->hwi);
+ }
+
+ if (u->hwo != INVALID_HANDLE_VALUE) {
+ waveOutReset(u->hwo);
+ waveOutClose(u->hwo);
+ }
+
+ for (i = 0;i < u->fragments;i++) {
+ pa_xfree(u->ihdrs[i].lpData);
+ pa_xfree(u->ohdrs[i].lpData);
+ }
+
+ pa_xfree(u->ihdrs);
+ pa_xfree(u->ohdrs);
+
+ DeleteCriticalSection(&u->crit);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c
new file mode 100644
index 00000000..d722b732
--- /dev/null
+++ b/src/modules/module-x11-bell.c
@@ -0,0 +1,173 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
+
+#include <polypcore/iochannel.h>
+#include <polypcore/sink.h>
+#include <polypcore/scache.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/namereg.h>
+#include <polypcore/log.h>
+#include <polypcore/x11wrap.h>
+
+#include "module-x11-bell-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("X11 Bell interceptor")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("sink=<sink to connect to> sample=<sample name> display=<X11 display>")
+
+struct userdata {
+ pa_core *core;
+ int xkb_event_base;
+ char *sink_name;
+ char *scache_item;
+ Display *display;
+
+ pa_x11_wrapper *x11_wrapper;
+ pa_x11_client *x11_client;
+};
+
+static const char* const valid_modargs[] = {
+ "sink",
+ "sample",
+ "display",
+ NULL
+};
+
+static int ring_bell(struct userdata *u, int percent) {
+ pa_sink *s;
+ pa_cvolume cv;
+ assert(u);
+
+ if (!(s = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
+ pa_log(__FILE__": Invalid sink: %s\n", u->sink_name);
+ return -1;
+ }
+
+ pa_scache_play_item(u->core, u->scache_item, s, pa_cvolume_set(&cv, PA_CHANNELS_MAX, percent*PA_VOLUME_NORM/100));
+ return 0;
+}
+
+static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) {
+ XkbBellNotifyEvent *bne;
+ struct userdata *u = userdata;
+ assert(w && e && u && u->x11_wrapper == w);
+
+ if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify)
+ return 0;
+
+ bne = (XkbBellNotifyEvent*) e;
+
+ if (ring_bell(u, bne->percent) < 0) {
+ pa_log_info(__FILE__": Ringing bell failed, reverting to X11 device bell.\n");
+ XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);
+ }
+
+ return 1;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct userdata *u = NULL;
+ pa_modargs *ma = NULL;
+ int major, minor;
+ unsigned int auto_ctrls, auto_values;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xmalloc(sizeof(struct userdata));
+ u->core = c;
+ u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "x11-bell"));
+ u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+ u->x11_client = NULL;
+
+ if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL))))
+ goto fail;
+
+ u->display = pa_x11_wrapper_get_display(u->x11_wrapper);
+
+ major = XkbMajorVersion;
+ minor = XkbMinorVersion;
+
+ if (!XkbLibraryVersion(&major, &minor)) {
+ pa_log(__FILE__": XkbLibraryVersion() failed\n");
+ goto fail;
+ }
+
+ major = XkbMajorVersion;
+ minor = XkbMinorVersion;
+
+
+ if (!XkbQueryExtension(u->display, NULL, &u->xkb_event_base, NULL, &major, &minor)) {
+ pa_log(__FILE__": XkbQueryExtension() failed\n");
+ goto fail;
+ }
+
+ XkbSelectEvents(u->display, XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask);
+ auto_ctrls = auto_values = XkbAudibleBellMask;
+ XkbSetAutoResetControls(u->display, XkbAudibleBellMask, &auto_ctrls, &auto_values);
+ XkbChangeEnabledControls(u->display, XkbUseCoreKbd, XkbAudibleBellMask, 0);
+
+ u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_callback, u);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+ if (m->userdata)
+ pa__done(c, m);
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata *u = m->userdata;
+ assert(c && m && u);
+
+ pa_xfree(u->scache_item);
+ pa_xfree(u->sink_name);
+
+ if (u->x11_client)
+ pa_x11_client_free(u->x11_client);
+
+ if (u->x11_wrapper)
+ pa_x11_wrapper_unref(u->x11_wrapper);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-x11-publish.c b/src/modules/module-x11-publish.c
new file mode 100644
index 00000000..dca5d049
--- /dev/null
+++ b/src/modules/module-x11-publish.c
@@ -0,0 +1,192 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include <polypcore/module.h>
+#include <polypcore/sink.h>
+#include <polypcore/scache.h>
+#include <polypcore/modargs.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/namereg.h>
+#include <polypcore/log.h>
+#include <polypcore/x11wrap.h>
+#include <polypcore/util.h>
+#include <polypcore/native-common.h>
+#include <polypcore/authkey-prop.h>
+#include <polypcore/authkey.h>
+#include <polypcore/x11prop.h>
+#include <polypcore/strlist.h>
+#include <polypcore/props.h>
+
+#include "module-x11-publish-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("X11 Credential Publisher")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("display=<X11 display>")
+
+static const char* const valid_modargs[] = {
+ "display",
+ "sink",
+ "source",
+ "cookie",
+ NULL
+};
+
+struct userdata {
+ pa_core *core;
+ pa_x11_wrapper *x11_wrapper;
+ Display *display;
+ char *id;
+ uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
+ int auth_cookie_in_property;
+};
+
+static int load_key(struct userdata *u, const char*fn) {
+ assert(u);
+
+ u->auth_cookie_in_property = 0;
+
+ if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
+ pa_log_debug(__FILE__": using already loaded auth cookie.\n");
+ pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+ u->auth_cookie_in_property = 1;
+ return 0;
+ }
+
+ if (!fn)
+ fn = PA_NATIVE_COOKIE_FILE;
+
+ if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
+ return -1;
+
+ pa_log_debug(__FILE__": loading cookie from disk.\n");
+
+ if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
+ u->auth_cookie_in_property = 1;
+
+ return 0;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ pa_modargs *ma = NULL;
+ char hn[256], un[128];
+ char hx[PA_NATIVE_COOKIE_LENGTH*2+1];
+ const char *t;
+ char *s;
+ pa_strlist *l;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xmalloc(sizeof(struct userdata));
+ u->core = c;
+ u->id = NULL;
+ u->auth_cookie_in_property = 0;
+
+ if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
+ goto fail;
+
+ if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL))))
+ goto fail;
+
+ u->display = pa_x11_wrapper_get_display(u->x11_wrapper);
+
+ if (!(l = pa_property_get(c, PA_NATIVE_SERVER_PROPERTY_NAME)))
+ goto fail;
+
+ s = pa_strlist_tostring(l);
+ pa_x11_set_prop(u->display, "POLYP_SERVER", s);
+ pa_xfree(s);
+
+ if (!pa_get_fqdn(hn, sizeof(hn)) || !pa_get_user_name(un, sizeof(un)))
+ goto fail;
+
+ u->id = pa_sprintf_malloc("%s@%s/%u", un, hn, (unsigned) getpid());
+ pa_x11_set_prop(u->display, "POLYP_ID", u->id);
+
+ if ((t = pa_modargs_get_value(ma, "source", NULL)))
+ pa_x11_set_prop(u->display, "POLYP_SOURCE", t);
+
+ if ((t = pa_modargs_get_value(ma, "sink", NULL)))
+ pa_x11_set_prop(u->display, "POLYP_SINK", t);
+
+ pa_x11_set_prop(u->display, "POLYP_COOKIE", pa_hexstr(u->auth_cookie, sizeof(u->auth_cookie), hx, sizeof(hx)));
+
+ pa_modargs_free(ma);
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(c, m);
+ return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata*u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->x11_wrapper) {
+ char t[256];
+
+ /* Yes, here is a race condition */
+ if (!pa_x11_get_prop(u->display, "POLYP_ID", t, sizeof(t)) || strcmp(t, u->id))
+ pa_log("WARNING: Polypaudio information vanished from X11!\n");
+ else {
+ pa_x11_del_prop(u->display, "POLYP_ID");
+ pa_x11_del_prop(u->display, "POLYP_SERVER");
+ pa_x11_del_prop(u->display, "POLYP_SINK");
+ pa_x11_del_prop(u->display, "POLYP_SOURCE");
+ pa_x11_del_prop(u->display, "POLYP_COOKIE");
+ XSync(u->display, False);
+ }
+ }
+
+ if (u->x11_wrapper)
+ pa_x11_wrapper_unref(u->x11_wrapper);
+
+ if (u->auth_cookie_in_property)
+ pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME);
+
+ pa_xfree(u->id);
+ pa_xfree(u);
+}
+
diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c
new file mode 100644
index 00000000..45d566ae
--- /dev/null
+++ b/src/modules/module-zeroconf-publish.c
@@ -0,0 +1,508 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <polypcore/howl-wrap.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/autoload.h>
+#include <polypcore/sink.h>
+#include <polypcore/source.h>
+#include <polypcore/native-common.h>
+#include <polypcore/util.h>
+#include <polypcore/log.h>
+#include <polypcore/subscribe.h>
+#include <polypcore/dynarray.h>
+#include <polypcore/endianmacros.h>
+#include <polypcore/modargs.h>
+
+#include "module-zeroconf-publish-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering")
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE("port=<IP port number>")
+
+#define SERVICE_NAME_SINK "_polypaudio-sink._tcp"
+#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp"
+#define SERVICE_NAME_SERVER "_polypaudio-server._tcp"
+
+static const char* const valid_modargs[] = {
+ "port",
+ NULL
+};
+
+struct service {
+ sw_discovery_oid oid;
+ char *name;
+ int published; /* 0 -> not yet registered, 1 -> registered with data from real device, 2 -> registered with data from autoload device */
+
+ struct {
+ int valid;
+ pa_namereg_type type;
+ uint32_t index;
+ } loaded;
+
+ struct {
+ int valid;
+ pa_namereg_type type;
+ uint32_t index;
+ } autoload;
+};
+
+struct userdata {
+ pa_core *core;
+ pa_howl_wrapper *howl_wrapper;
+ pa_hashmap *services;
+ pa_dynarray *sink_dynarray, *source_dynarray, *autoload_dynarray;
+ pa_subscription *subscription;
+
+ uint16_t port;
+ sw_discovery_oid server_oid;
+};
+
+static sw_result publish_reply(sw_discovery discovery, sw_discovery_publish_status status, sw_discovery_oid oid, sw_opaque extra) {
+ return SW_OKAY;
+}
+
+static void get_service_data(struct userdata *u, struct service *s, pa_sample_spec *ret_ss, char **ret_description, pa_typeid_t *ret_typeid) {
+ assert(u && s && s->loaded.valid && ret_ss && ret_description && ret_typeid);
+
+ if (s->loaded.type == PA_NAMEREG_SINK) {
+ pa_sink *sink = pa_idxset_get_by_index(u->core->sinks, s->loaded.index);
+ assert(sink);
+ *ret_ss = sink->sample_spec;
+ *ret_description = sink->description;
+ *ret_typeid = sink->typeid;
+ } else if (s->loaded.type == PA_NAMEREG_SOURCE) {
+ pa_source *source = pa_idxset_get_by_index(u->core->sources, s->loaded.index);
+ assert(source);
+ *ret_ss = source->sample_spec;
+ *ret_description = source->description;
+ *ret_typeid = source->typeid;
+ } else
+ assert(0);
+}
+
+static void txt_record_server_data(pa_core *c, sw_text_record t) {
+ char s[256];
+ assert(c);
+
+ sw_text_record_add_key_and_string_value(t, "server-version", PACKAGE_NAME" "PACKAGE_VERSION);
+ sw_text_record_add_key_and_string_value(t, "user-name", pa_get_user_name(s, sizeof(s)));
+ sw_text_record_add_key_and_string_value(t, "fqdn", pa_get_fqdn(s, sizeof(s)));
+ snprintf(s, sizeof(s), "0x%08x", c->cookie);
+ sw_text_record_add_key_and_string_value(t, "cookie", s);
+}
+
+static int publish_service(struct userdata *u, struct service *s) {
+ char t[256];
+ char hn[256];
+ int r = -1;
+ sw_text_record txt;
+ int free_txt = 0;
+ assert(u && s);
+
+ if ((s->published == 1 && s->loaded.valid) ||
+ (s->published == 2 && s->autoload.valid && !s->loaded.valid))
+ return 0;
+
+ if (s->published) {
+ sw_discovery_cancel(pa_howl_wrapper_get_discovery(u->howl_wrapper), s->oid);
+ s->published = 0;
+ }
+
+ snprintf(t, sizeof(t), "Networked Audio Device %s on %s", s->name, pa_get_host_name(hn, sizeof(hn)));
+
+ if (sw_text_record_init(&txt) != SW_OKAY) {
+ pa_log(__FILE__": sw_text_record_init() failed\n");
+ goto finish;
+ }
+ free_txt = 1;
+
+ sw_text_record_add_key_and_string_value(txt, "device", s->name);
+
+ txt_record_server_data(u->core, txt);
+
+ if (s->loaded.valid) {
+ char z[64], *description;
+ pa_typeid_t typeid;
+ pa_sample_spec ss;
+
+ get_service_data(u, s, &ss, &description, &typeid);
+
+ snprintf(z, sizeof(z), "%u", ss.rate);
+ sw_text_record_add_key_and_string_value(txt, "rate", z);
+ snprintf(z, sizeof(z), "%u", ss.channels);
+ sw_text_record_add_key_and_string_value(txt, "channels", z);
+ sw_text_record_add_key_and_string_value(txt, "format", pa_sample_format_to_string(ss.format));
+
+ sw_text_record_add_key_and_string_value(txt, "description", description);
+
+ snprintf(z, sizeof(z), "0x%8x", typeid);
+ sw_text_record_add_key_and_string_value(txt, "typeid", z);
+
+
+ if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t,
+ s->loaded.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE,
+ NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt),
+ publish_reply, s, &s->oid) != SW_OKAY) {
+ pa_log(__FILE__": failed to register sink on zeroconf.\n");
+ goto finish;
+ }
+
+ s->published = 1;
+ } else if (s->autoload.valid) {
+
+ if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t,
+ s->autoload.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE,
+ NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt),
+ publish_reply, s, &s->oid) != SW_OKAY) {
+ pa_log(__FILE__": failed to register sink on zeroconf.\n");
+ goto finish;
+ }
+
+ s->published = 2;
+ }
+
+ r = 0;
+
+finish:
+
+ if (!s->published) {
+ /* Remove this service */
+ pa_hashmap_remove(u->services, s->name);
+ pa_xfree(s->name);
+ pa_xfree(s);
+ }
+
+ if (free_txt)
+ sw_text_record_fina(txt);
+
+ return r;
+}
+
+struct service *get_service(struct userdata *u, const char *name) {
+ struct service *s;
+
+ if ((s = pa_hashmap_get(u->services, name)))
+ return s;
+
+ s = pa_xmalloc(sizeof(struct service));
+ s->published = 0;
+ s->name = pa_xstrdup(name);
+ s->loaded.valid = s->autoload.valid = 0;
+
+ pa_hashmap_put(u->services, s->name, s);
+
+ return s;
+}
+
+static int publish_sink(struct userdata *u, pa_sink *s) {
+ struct service *svc;
+ assert(u && s);
+
+ svc = get_service(u, s->name);
+ if (svc->loaded.valid)
+ return 0;
+
+ svc->loaded.valid = 1;
+ svc->loaded.type = PA_NAMEREG_SINK;
+ svc->loaded.index = s->index;
+
+ pa_dynarray_put(u->sink_dynarray, s->index, svc);
+
+ return publish_service(u, svc);
+}
+
+static int publish_source(struct userdata *u, pa_source *s) {
+ struct service *svc;
+ assert(u && s);
+
+ svc = get_service(u, s->name);
+ if (svc->loaded.valid)
+ return 0;
+
+ svc->loaded.valid = 1;
+ svc->loaded.type = PA_NAMEREG_SOURCE;
+ svc->loaded.index = s->index;
+
+ pa_dynarray_put(u->source_dynarray, s->index, svc);
+
+ return publish_service(u, svc);
+}
+
+static int publish_autoload(struct userdata *u, pa_autoload_entry *s) {
+ struct service *svc;
+ assert(u && s);
+
+ svc = get_service(u, s->name);
+ if (svc->autoload.valid)
+ return 0;
+
+ svc->autoload.valid = 1;
+ svc->autoload.type = s->type;
+ svc->autoload.index = s->index;
+
+ pa_dynarray_put(u->autoload_dynarray, s->index, svc);
+
+ return publish_service(u, svc);
+}
+
+static int remove_sink(struct userdata *u, uint32_t index) {
+ struct service *svc;
+ assert(u && index != PA_INVALID_INDEX);
+
+ if (!(svc = pa_dynarray_get(u->sink_dynarray, index)))
+ return 0;
+
+ if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SINK)
+ return 0;
+
+ svc->loaded.valid = 0;
+ pa_dynarray_put(u->sink_dynarray, index, NULL);
+
+ return publish_service(u, svc);
+}
+
+static int remove_source(struct userdata *u, uint32_t index) {
+ struct service *svc;
+ assert(u && index != PA_INVALID_INDEX);
+
+ if (!(svc = pa_dynarray_get(u->source_dynarray, index)))
+ return 0;
+
+ if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SOURCE)
+ return 0;
+
+ svc->loaded.valid = 0;
+ pa_dynarray_put(u->source_dynarray, index, NULL);
+
+ return publish_service(u, svc);
+}
+
+static int remove_autoload(struct userdata *u, uint32_t index) {
+ struct service *svc;
+ assert(u && index != PA_INVALID_INDEX);
+
+ if (!(svc = pa_dynarray_get(u->autoload_dynarray, index)))
+ return 0;
+
+ if (!svc->autoload.valid)
+ return 0;
+
+ svc->autoload.valid = 0;
+ pa_dynarray_put(u->autoload_dynarray, index, NULL);
+
+ return publish_service(u, svc);
+}
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type t, uint32_t index, void *userdata) {
+ struct userdata *u = userdata;
+ assert(u && c);
+
+ switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
+ case PA_SUBSCRIPTION_EVENT_SINK: {
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+ pa_sink *sink;
+
+ if ((sink = pa_idxset_get_by_index(c->sinks, index))) {
+ if (publish_sink(u, sink) < 0)
+ goto fail;
+ }
+ } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ if (remove_sink(u, index) < 0)
+ goto fail;
+ }
+
+ break;
+
+ case PA_SUBSCRIPTION_EVENT_SOURCE:
+
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+ pa_source *source;
+
+ if ((source = pa_idxset_get_by_index(c->sources, index))) {
+ if (publish_source(u, source) < 0)
+ goto fail;
+ }
+ } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ if (remove_source(u, index) < 0)
+ goto fail;
+ }
+
+ break;
+
+ case PA_SUBSCRIPTION_EVENT_AUTOLOAD:
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+ pa_autoload_entry *autoload;
+
+ if ((autoload = pa_idxset_get_by_index(c->autoload_idxset, index))) {
+ if (publish_autoload(u, autoload) < 0)
+ goto fail;
+ }
+ } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ if (remove_autoload(u, index) < 0)
+ goto fail;
+ }
+
+ break;
+ }
+
+ return;
+
+fail:
+ if (u->subscription) {
+ pa_subscription_free(u->subscription);
+ u->subscription = NULL;
+ }
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+ struct userdata *u;
+ uint32_t index, port = PA_NATIVE_DEFAULT_PORT;
+ pa_sink *sink;
+ pa_source *source;
+ pa_autoload_entry *autoload;
+ pa_modargs *ma = NULL;
+ char t[256], hn[256];
+ int free_txt = 0;
+ sw_text_record txt;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log(__FILE__": failed to parse module arguments.\n");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port == 0 || port >= 0xFFFF) {
+ pa_log(__FILE__": invalid port specified.\n");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xmalloc(sizeof(struct userdata));
+ u->core = c;
+ u->port = (uint16_t) port;
+
+ if (!(u->howl_wrapper = pa_howl_wrapper_get(c)))
+ goto fail;
+
+ u->services = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ u->sink_dynarray = pa_dynarray_new();
+ u->source_dynarray = pa_dynarray_new();
+ u->autoload_dynarray = pa_dynarray_new();
+
+ u->subscription = pa_subscription_new(c,
+ PA_SUBSCRIPTION_MASK_SINK|
+ PA_SUBSCRIPTION_MASK_SOURCE|
+ PA_SUBSCRIPTION_MASK_AUTOLOAD, subscribe_callback, u);
+
+ for (sink = pa_idxset_first(c->sinks, &index); sink; sink = pa_idxset_next(c->sinks, &index))
+ if (publish_sink(u, sink) < 0)
+ goto fail;
+
+ for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index))
+ if (publish_source(u, source) < 0)
+ goto fail;
+
+ if (c->autoload_idxset)
+ for (autoload = pa_idxset_first(c->autoload_idxset, &index); autoload; autoload = pa_idxset_next(c->autoload_idxset, &index))
+ if (publish_autoload(u, autoload) < 0)
+ goto fail;
+
+ snprintf(t, sizeof(t), "Networked Audio Server on %s", pa_get_host_name(hn, sizeof(hn)));
+
+ if (sw_text_record_init(&txt) != SW_OKAY) {
+ pa_log(__FILE__": sw_text_record_init() failed\n");
+ goto fail;
+ }
+ free_txt = 1;
+
+ txt_record_server_data(u->core, txt);
+
+ if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t,
+ SERVICE_NAME_SERVER,
+ NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt),
+ publish_reply, u, &u->server_oid) != SW_OKAY) {
+ pa_log(__FILE__": failed to register server on zeroconf.\n");
+ goto fail;
+ }
+
+ sw_text_record_fina(txt);
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ pa__done(c, m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ if (free_txt)
+ sw_text_record_fina(txt);
+
+ return -1;
+}
+
+static void service_free(void *p, void *userdata) {
+ struct service *s = p;
+ struct userdata *u = userdata;
+ assert(s && u);
+ sw_discovery_cancel(pa_howl_wrapper_get_discovery(u->howl_wrapper), s->oid);
+ pa_xfree(s->name);
+ pa_xfree(s);
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+ struct userdata*u;
+ assert(c && m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->services)
+ pa_hashmap_free(u->services, service_free, u);
+
+ if (u->sink_dynarray)
+ pa_dynarray_free(u->sink_dynarray, NULL, NULL);
+ if (u->source_dynarray)
+ pa_dynarray_free(u->source_dynarray, NULL, NULL);
+ if (u->autoload_dynarray)
+ pa_dynarray_free(u->autoload_dynarray, NULL, NULL);
+
+ if (u->subscription)
+ pa_subscription_free(u->subscription);
+
+ if (u->howl_wrapper)
+ pa_howl_wrapper_unref(u->howl_wrapper);
+
+
+ pa_xfree(u);
+}
+
diff --git a/src/polyp/cdecl.h b/src/polyp/cdecl.h
new file mode 100644
index 00000000..d51ae026
--- /dev/null
+++ b/src/polyp/cdecl.h
@@ -0,0 +1,42 @@
+#ifndef foocdeclhfoo
+#define foocdeclhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/** \file
+ * C++ compatibility support */
+
+#ifdef __cplusplus
+/** If using C++ this macro enables C mode, otherwise does nothing */
+#define PA_C_DECL_BEGIN extern "C" {
+/** If using C++ this macros switches back to C++ mode, otherwise does nothing */
+#define PA_C_DECL_END }
+
+#else
+/** If using C++ this macro enables C mode, otherwise does nothing */
+#define PA_C_DECL_BEGIN
+/** If using C++ this macros switches back to C++ mode, otherwise does nothing */
+#define PA_C_DECL_END
+
+#endif
+
+#endif
diff --git a/src/polyp/channelmap.c b/src/polyp/channelmap.c
new file mode 100644
index 00000000..7bfd21e6
--- /dev/null
+++ b/src/polyp/channelmap.c
@@ -0,0 +1,202 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "channelmap.h"
+
+pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
+ unsigned c;
+ assert(m);
+
+ m->channels = 0;
+
+ for (c = 0; c < PA_CHANNELS_MAX; c++)
+ m->map[c] = PA_CHANNEL_POSITION_INVALID;
+
+ return m;
+}
+
+pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {
+ assert(m);
+
+ pa_channel_map_init(m);
+
+ m->channels = 1;
+ m->map[0] = PA_CHANNEL_POSITION_MONO;
+ return m;
+}
+
+pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
+ assert(m);
+
+ pa_channel_map_init(m);
+
+ m->channels = 2;
+ m->map[0] = PA_CHANNEL_POSITION_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_RIGHT;
+ return m;
+}
+
+pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels) {
+ assert(m);
+ assert(channels > 0);
+ assert(channels <= PA_CHANNELS_MAX);
+
+ pa_channel_map_init(m);
+
+ m->channels = channels;
+
+ switch (channels) {
+ case 1:
+ m->map[0] = PA_CHANNEL_POSITION_MONO;
+ return m;
+
+ case 8:
+ m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+ m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+ /* Fall through */
+
+ case 6:
+ m->map[5] = PA_CHANNEL_POSITION_LFE;
+ /* Fall through */
+
+ case 5:
+ m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ /* Fall through */
+
+ case 4:
+ m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+ m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ /* Fall through */
+
+ case 2:
+ m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ return m;
+
+ default:
+ return NULL;
+ }
+}
+
+const char* pa_channel_position_to_string(pa_channel_position_t pos) {
+
+ const char *const table[] = {
+ [PA_CHANNEL_POSITION_MONO] = "mono",
+
+ [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
+ [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
+ [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
+
+ [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
+ [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
+ [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
+
+ [PA_CHANNEL_POSITION_LFE] = "lfe",
+
+ [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
+ [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
+
+ [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
+ [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right",
+
+ [PA_CHANNEL_POSITION_AUX1] = "aux1",
+ [PA_CHANNEL_POSITION_AUX2] = "aux2",
+ [PA_CHANNEL_POSITION_AUX3] = "aux3",
+ [PA_CHANNEL_POSITION_AUX4] = "aux4",
+ [PA_CHANNEL_POSITION_AUX5] = "aux5",
+ [PA_CHANNEL_POSITION_AUX6] = "aux6",
+ [PA_CHANNEL_POSITION_AUX7] = "aux7",
+ [PA_CHANNEL_POSITION_AUX8] = "aux8",
+ [PA_CHANNEL_POSITION_AUX9] = "aux9",
+ [PA_CHANNEL_POSITION_AUX10] = "aux10",
+ [PA_CHANNEL_POSITION_AUX11] = "aux11",
+ [PA_CHANNEL_POSITION_AUX12] = "aux12"
+ };
+
+ if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
+ return NULL;
+
+ return table[pos];
+}
+
+int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
+ unsigned c;
+
+ assert(a);
+ assert(b);
+
+ if (a->channels != b->channels)
+ return 0;
+
+ for (c = 0; c < a->channels; c++)
+ if (a->map[c] != b->map[c])
+ return 0;
+
+ return 1;
+}
+
+char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
+ unsigned channel;
+ int first = 1;
+ char *e;
+
+ assert(s);
+ assert(l > 0);
+ assert(map);
+
+ *(e = s) = 0;
+
+ for (channel = 0; channel < map->channels && l > 1; channel++) {
+ l -= snprintf(e, l, "%s%u:%s",
+ first ? "" : " ",
+ channel,
+ pa_channel_position_to_string(map->map[channel]));
+
+ e = strchr(e, 0);
+ first = 0;
+ }
+
+ return s;
+}
+
+int pa_channel_map_valid(const pa_channel_map *map) {
+ unsigned c;
+
+ assert(map);
+
+ if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX)
+ return 0;
+
+ for (c = 0; c < map->channels; c++)
+ if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX)
+ return 0;
+
+ return 1;
+}
diff --git a/src/polyp/channelmap.h b/src/polyp/channelmap.h
new file mode 100644
index 00000000..0b9f6e26
--- /dev/null
+++ b/src/polyp/channelmap.h
@@ -0,0 +1,98 @@
+#ifndef foochannelmaphfoo
+#define foochannelmaphfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/sample.h>
+#include <polyp/cdecl.h>
+
+/** \file
+ * Constants and routines for channel mapping handling */
+
+PA_C_DECL_BEGIN
+
+typedef enum {
+ PA_CHANNEL_POSITION_INVALID = -1,
+ PA_CHANNEL_POSITION_MONO = 0,
+
+ PA_CHANNEL_POSITION_LEFT,
+ PA_CHANNEL_POSITION_RIGHT,
+
+ PA_CHANNEL_POSITION_FRONT_CENTER,
+ PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT,
+ PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT,
+
+ PA_CHANNEL_POSITION_REAR_CENTER,
+ PA_CHANNEL_POSITION_REAR_LEFT,
+ PA_CHANNEL_POSITION_REAR_RIGHT,
+
+ PA_CHANNEL_POSITION_LFE,
+ PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE,
+
+ PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+ PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+
+ PA_CHANNEL_POSITION_SIDE_LEFT,
+ PA_CHANNEL_POSITION_SIDE_RIGHT,
+
+ PA_CHANNEL_POSITION_AUX0,
+ PA_CHANNEL_POSITION_AUX1,
+ PA_CHANNEL_POSITION_AUX2,
+ PA_CHANNEL_POSITION_AUX3,
+ PA_CHANNEL_POSITION_AUX4,
+ PA_CHANNEL_POSITION_AUX5,
+ PA_CHANNEL_POSITION_AUX6,
+ PA_CHANNEL_POSITION_AUX7,
+ PA_CHANNEL_POSITION_AUX8,
+ PA_CHANNEL_POSITION_AUX9,
+ PA_CHANNEL_POSITION_AUX10,
+ PA_CHANNEL_POSITION_AUX11,
+ PA_CHANNEL_POSITION_AUX12,
+ PA_CHANNEL_POSITION_AUX13,
+ PA_CHANNEL_POSITION_AUX14,
+ PA_CHANNEL_POSITION_AUX15,
+
+ PA_CHANNEL_POSITION_MAX
+} pa_channel_position_t;
+
+typedef struct pa_channel_map {
+ uint8_t channels;
+ pa_channel_position_t map[PA_CHANNELS_MAX];
+} pa_channel_map;
+
+pa_channel_map* pa_channel_map_init(pa_channel_map *m);
+pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m);
+pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m);
+pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels);
+
+const char* pa_channel_position_to_string(pa_channel_position_t pos);
+
+#define PA_CHANNEL_MAP_SNPRINT_MAX 64
+char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);
+
+int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b);
+
+int pa_channel_map_valid(const pa_channel_map *map);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/client-conf-x11.c b/src/polyp/client-conf-x11.c
new file mode 100644
index 00000000..83d0bd2e
--- /dev/null
+++ b/src/polyp/client-conf-x11.c
@@ -0,0 +1,91 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-13071
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include "client-conf-x11.h"
+#include <polypcore/x11prop.h>
+#include <polypcore/log.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/util.h>
+
+int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {
+ Display *d = NULL;
+ int ret = -1;
+ char t[1024];
+
+ if (!dname && !getenv("DISPLAY"))
+ goto finish;
+
+ if (!(d = XOpenDisplay(dname))) {
+ pa_log(__FILE__": XOpenDisplay() failed\n");
+ goto finish;
+ }
+
+ if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) {
+ pa_xfree(c->default_server);
+ c->default_server = pa_xstrdup(t);
+ }
+
+ if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) {
+ pa_xfree(c->default_sink);
+ c->default_sink = pa_xstrdup(t);
+ }
+
+ if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) {
+ pa_xfree(c->default_source);
+ c->default_source = pa_xstrdup(t);
+ }
+
+ if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) {
+ uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+
+ if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie)) {
+ pa_log(__FILE__": failed to parse cookie data\n");
+ goto finish;
+ }
+
+ assert(sizeof(cookie) == sizeof(c->cookie));
+ memcpy(c->cookie, cookie, sizeof(cookie));
+
+ c->cookie_valid = 1;
+
+ pa_xfree(c->cookie_file);
+ c->cookie_file = NULL;
+ }
+
+ ret = 0;
+
+finish:
+ if (d)
+ XCloseDisplay(d);
+
+ return ret;
+
+}
diff --git a/src/polyp/client-conf-x11.h b/src/polyp/client-conf-x11.h
new file mode 100644
index 00000000..80841171
--- /dev/null
+++ b/src/polyp/client-conf-x11.h
@@ -0,0 +1,31 @@
+#ifndef fooclientconfx11hfoo
+#define fooclientconfx11hfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/client-conf.h>
+
+/* Load client configuration data from the specified X11 display,
+ * overwriting the current settings in *c */
+int pa_client_conf_from_x11(pa_client_conf *c, const char *display);
+
+#endif
diff --git a/src/polyp/client-conf.c b/src/polyp/client-conf.c
new file mode 100644
index 00000000..2df201ce
--- /dev/null
+++ b/src/polyp/client-conf.c
@@ -0,0 +1,191 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+#include <polypcore/conf-parser.h>
+#include <polypcore/util.h>
+#include <polypcore/authkey.h>
+
+#include <polyp/client-conf.h>
+
+#ifndef DEFAULT_CONFIG_DIR
+# ifndef OS_IS_WIN32
+# define DEFAULT_CONFIG_DIR "/etc/polypaudio"
+# else
+# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%"
+# endif
+#endif
+
+#ifndef OS_IS_WIN32
+# define PATH_SEP "/"
+#else
+# define PATH_SEP "\\"
+#endif
+
+#define DEFAULT_CLIENT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "client.conf"
+#define DEFAULT_CLIENT_CONFIG_FILE_USER ".polypaudio" PATH_SEP "client.conf"
+
+#define ENV_CLIENT_CONFIG_FILE "POLYP_CLIENTCONFIG"
+#define ENV_DEFAULT_SINK "POLYP_SINK"
+#define ENV_DEFAULT_SOURCE "POLYP_SOURCE"
+#define ENV_DEFAULT_SERVER "POLYP_SERVER"
+#define ENV_DAEMON_BINARY "POLYP_BINARY"
+#define ENV_COOKIE_FILE "POLYP_COOKIE"
+
+static const pa_client_conf default_conf = {
+ .daemon_binary = NULL,
+ .extra_arguments = NULL,
+ .default_sink = NULL,
+ .default_source = NULL,
+ .default_server = NULL,
+ .autospawn = 0,
+ .cookie_file = NULL,
+ .cookie_valid = 0
+};
+
+pa_client_conf *pa_client_conf_new(void) {
+ pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
+
+ c->daemon_binary = pa_xstrdup(POLYPAUDIO_BINARY);
+ c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5");
+ c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE);
+
+ return c;
+}
+
+void pa_client_conf_free(pa_client_conf *c) {
+ assert(c);
+ pa_xfree(c->daemon_binary);
+ pa_xfree(c->extra_arguments);
+ pa_xfree(c->default_sink);
+ pa_xfree(c->default_source);
+ pa_xfree(c->default_server);
+ pa_xfree(c->cookie_file);
+ pa_xfree(c);
+}
+int pa_client_conf_load(pa_client_conf *c, const char *filename) {
+ FILE *f = NULL;
+ char *fn = NULL;
+ int r = -1;
+
+ /* Prepare the configuration parse table */
+ pa_config_item table[] = {
+ { "daemon-binary", pa_config_parse_string, NULL },
+ { "extra-arguments", pa_config_parse_string, NULL },
+ { "default-sink", pa_config_parse_string, NULL },
+ { "default-source", pa_config_parse_string, NULL },
+ { "default-server", pa_config_parse_string, NULL },
+ { "autospawn", pa_config_parse_bool, NULL },
+ { "cookie-file", pa_config_parse_string, NULL },
+ { NULL, NULL, NULL },
+ };
+
+ table[0].data = &c->daemon_binary;
+ table[1].data = &c->extra_arguments;
+ table[2].data = &c->default_sink;
+ table[3].data = &c->default_source;
+ table[4].data = &c->default_server;
+ table[5].data = &c->autospawn;
+ table[6].data = &c->cookie_file;
+
+ f = filename ?
+ fopen((fn = pa_xstrdup(filename)), "r") :
+ pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn);
+
+ if (!f && errno != EINTR) {
+ pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno));
+ goto finish;
+ }
+
+ r = f ? pa_config_parse(fn, f, table, NULL) : 0;
+
+ if (!r)
+ r = pa_client_conf_load_cookie(c);
+
+
+finish:
+ pa_xfree(fn);
+
+ if (f)
+ fclose(f);
+
+ return r;
+}
+
+int pa_client_conf_env(pa_client_conf *c) {
+ char *e;
+
+ if ((e = getenv(ENV_DEFAULT_SINK))) {
+ pa_xfree(c->default_sink);
+ c->default_sink = pa_xstrdup(e);
+ }
+
+ if ((e = getenv(ENV_DEFAULT_SOURCE))) {
+ pa_xfree(c->default_source);
+ c->default_source = pa_xstrdup(e);
+ }
+
+ if ((e = getenv(ENV_DEFAULT_SERVER))) {
+ pa_xfree(c->default_server);
+ c->default_server = pa_xstrdup(e);
+ }
+
+ if ((e = getenv(ENV_DAEMON_BINARY))) {
+ pa_xfree(c->daemon_binary);
+ c->daemon_binary = pa_xstrdup(e);
+ }
+
+ if ((e = getenv(ENV_COOKIE_FILE))) {
+ pa_xfree(c->cookie_file);
+ c->cookie_file = pa_xstrdup(e);
+
+ return pa_client_conf_load_cookie(c);
+ }
+
+ return 0;
+}
+
+int pa_client_conf_load_cookie(pa_client_conf* c) {
+ assert(c);
+
+ c->cookie_valid = 0;
+
+ if (!c->cookie_file)
+ return -1;
+
+ if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0)
+ return -1;
+
+ c->cookie_valid = 1;
+ return 0;
+}
+
diff --git a/src/polyp/client-conf.h b/src/polyp/client-conf.h
new file mode 100644
index 00000000..2d8a019f
--- /dev/null
+++ b/src/polyp/client-conf.h
@@ -0,0 +1,52 @@
+#ifndef fooclientconfhfoo
+#define fooclientconfhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "../polypcore/native-common.h"
+
+/* A structure containing configuration data for polypaudio clients. */
+
+typedef struct pa_client_conf {
+ char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file;
+ int autospawn;
+ uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+ int cookie_valid; /* non-zero, when cookie is valid */
+} pa_client_conf;
+
+/* Create a new configuration data object and reset it to defaults */
+pa_client_conf *pa_client_conf_new(void);
+void pa_client_conf_free(pa_client_conf *c);
+
+/* Load the configuration data from the speicified file, overwriting
+ * the current settings in *c. When the filename is NULL, the
+ * default client configuration file name is used. */
+int pa_client_conf_load(pa_client_conf *c, const char *filename);
+
+/* Load the configuration data from the environment of the current
+ process, overwriting the current settings in *c. */
+int pa_client_conf_env(pa_client_conf *c);
+
+/* Load cookie data from c->cookie_file into c->cookie */
+int pa_client_conf_load_cookie(pa_client_conf* c);
+
+#endif
diff --git a/src/polyp/glib-mainloop.c b/src/polyp/glib-mainloop.c
new file mode 100644
index 00000000..962eb574
--- /dev/null
+++ b/src/polyp/glib-mainloop.c
@@ -0,0 +1,538 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+
+#include <polyp/glib-mainloop.h>
+#include <polypcore/idxset.h>
+#include <polypcore/xmalloc.h>
+#include "glib.h"
+#include <polypcore/util.h>
+
+struct pa_io_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ GIOChannel *io_channel;
+ GSource *source;
+ GIOCondition io_condition;
+ int fd;
+ void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_io_event *e, void *userdata);
+ pa_io_event *next, *prev;
+};
+
+struct pa_time_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ GSource *source;
+ struct timeval timeval;
+ void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata);
+ pa_time_event *next, *prev;
+};
+
+struct pa_defer_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ GSource *source;
+ void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata);
+ pa_defer_event *next, *prev;
+};
+
+struct pa_glib_mainloop {
+ GMainContext *glib_main_context;
+ pa_mainloop_api api;
+ GSource *cleanup_source;
+ pa_io_event *io_events, *dead_io_events;
+ pa_time_event *time_events, *dead_time_events;
+ pa_defer_event *defer_events, *dead_defer_events;
+};
+
+static void schedule_free_dead_events(pa_glib_mainloop *g);
+
+static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f);
+
+static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) {
+ pa_io_event *e;
+ pa_glib_mainloop *g;
+
+ assert(m && m->userdata && fd >= 0 && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_io_event));
+ e->mainloop = m->userdata;
+ e->dead = 0;
+ e->fd = fd;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+
+ e->io_channel = g_io_channel_unix_new(e->fd);
+ assert(e->io_channel);
+ e->source = NULL;
+ e->io_condition = 0;
+
+ glib_io_enable(e, f);
+
+ e->next = g->io_events;
+ if (e->next) e->next->prev = e;
+ g->io_events = e;
+ e->prev = NULL;
+
+ return e;
+}
+
+/* The callback GLIB calls whenever an IO condition is met */
+static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
+ pa_io_event *e = data;
+ pa_io_event_flags_t f;
+ assert(source && e && e->io_channel == source);
+
+ f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
+ (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
+ (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
+ (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
+
+ e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
+ return TRUE;
+}
+
+static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
+ GIOCondition c;
+ assert(e && !e->dead);
+
+ c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
+
+ if (c == e->io_condition)
+ return;
+
+ if (e->source) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ }
+
+ e->source = g_io_create_watch(e->io_channel, c | G_IO_ERR | G_IO_HUP);
+ assert(e->source);
+
+ g_source_set_callback(e->source, (GSourceFunc) io_cb, e, NULL);
+ g_source_attach(e->source, e->mainloop->glib_main_context);
+ g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
+
+ e->io_condition = c;
+}
+
+static void glib_io_free(pa_io_event*e) {
+ assert(e && !e->dead);
+
+ if (e->source) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ e->source = NULL;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->io_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_io_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_io_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Time sources */
+
+static void glib_time_restart(pa_time_event*e, const struct timeval *tv);
+
+static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
+ pa_glib_mainloop *g;
+ pa_time_event *e;
+
+ assert(m && m->userdata && tv && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_time_event));
+ e->mainloop = g;
+ e->dead = 0;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+ e->source = NULL;
+
+ glib_time_restart(e, tv);
+
+ e->next = g->time_events;
+ if (e->next) e->next->prev = e;
+ g->time_events = e;
+ e->prev = NULL;
+
+ return e;
+}
+
+static guint msec_diff(const struct timeval *a, const struct timeval *b) {
+ guint r;
+ assert(a && b);
+
+ if (a->tv_sec < b->tv_sec)
+ return 0;
+
+ if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
+ return 0;
+
+ r = (a->tv_sec-b->tv_sec)*1000;
+
+ if (a->tv_usec >= b->tv_usec)
+ r += (a->tv_usec - b->tv_usec) / 1000;
+ else
+ r -= (b->tv_usec - a->tv_usec) / 1000;
+
+ return r;
+}
+
+static gboolean time_cb(gpointer data) {
+ pa_time_event* e = data;
+ assert(e && e->mainloop && e->source);
+
+ g_source_unref(e->source);
+ e->source = NULL;
+
+ e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
+ return FALSE;
+}
+
+static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
+ struct timeval now;
+ assert(e && e->mainloop && !e->dead);
+
+ pa_gettimeofday(&now);
+ if (e->source) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ }
+
+ if (tv) {
+ e->timeval = *tv;
+ e->source = g_timeout_source_new(msec_diff(tv, &now));
+ assert(e->source);
+ g_source_set_callback(e->source, time_cb, e, NULL);
+ g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
+ g_source_attach(e->source, e->mainloop->glib_main_context);
+ } else
+ e->source = NULL;
+ }
+
+static void glib_time_free(pa_time_event *e) {
+ assert(e && e->mainloop && !e->dead);
+
+ if (e->source) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ e->source = NULL;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->time_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_time_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_time_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Deferred sources */
+
+static void glib_defer_enable(pa_defer_event *e, int b);
+
+static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) {
+ pa_defer_event *e;
+ pa_glib_mainloop *g;
+
+ assert(m && m->userdata && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_defer_event));
+ e->mainloop = g;
+ e->dead = 0;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+ e->source = NULL;
+
+ glib_defer_enable(e, 1);
+
+ e->next = g->defer_events;
+ if (e->next) e->next->prev = e;
+ g->defer_events = e;
+ e->prev = NULL;
+ return e;
+}
+
+static gboolean idle_cb(gpointer data) {
+ pa_defer_event* e = data;
+ assert(e && e->mainloop && e->source);
+
+ e->callback(&e->mainloop->api, e, e->userdata);
+ return TRUE;
+}
+
+static void glib_defer_enable(pa_defer_event *e, int b) {
+ assert(e && e->mainloop);
+
+ if (e->source && !b) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ e->source = NULL;
+ } else if (!e->source && b) {
+ e->source = g_idle_source_new();
+ assert(e->source);
+ g_source_set_callback(e->source, idle_cb, e, NULL);
+ g_source_attach(e->source, e->mainloop->glib_main_context);
+ g_source_set_priority(e->source, G_PRIORITY_HIGH);
+ }
+}
+
+static void glib_defer_free(pa_defer_event *e) {
+ assert(e && e->mainloop && !e->dead);
+
+ if (e->source) {
+ g_source_destroy(e->source);
+ g_source_unref(e->source);
+ e->source = NULL;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->defer_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_defer_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_defer_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* quit() */
+
+static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) {
+ pa_glib_mainloop *g;
+ assert(a && a->userdata);
+ g = a->userdata;
+
+ /* NOOP */
+}
+
+static const pa_mainloop_api vtable = {
+ .userdata = NULL,
+
+ .io_new = glib_io_new,
+ .io_enable = glib_io_enable,
+ .io_free = glib_io_free,
+ .io_set_destroy= glib_io_set_destroy,
+
+ .time_new = glib_time_new,
+ .time_restart = glib_time_restart,
+ .time_free = glib_time_free,
+ .time_set_destroy = glib_time_set_destroy,
+
+ .defer_new = glib_defer_new,
+ .defer_enable = glib_defer_enable,
+ .defer_free = glib_defer_free,
+ .defer_set_destroy = glib_defer_set_destroy,
+
+ .quit = glib_quit,
+};
+
+pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
+ pa_glib_mainloop *g;
+
+ g = pa_xmalloc(sizeof(pa_glib_mainloop));
+ if (c) {
+ g->glib_main_context = c;
+ g_main_context_ref(c);
+ } else
+ g->glib_main_context = g_main_context_default();
+
+ g->api = vtable;
+ g->api.userdata = g;
+
+ g->io_events = g->dead_io_events = NULL;
+ g->time_events = g->dead_time_events = NULL;
+ g->defer_events = g->dead_defer_events = NULL;
+
+ g->cleanup_source = NULL;
+ return g;
+}
+
+static void free_io_events(pa_io_event *e) {
+ while (e) {
+ pa_io_event *r = e;
+ e = r->next;
+
+ if (r->source) {
+ g_source_destroy(r->source);
+ g_source_unref(r->source);
+ }
+
+ if (r->io_channel)
+ g_io_channel_unref(r->io_channel);
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+static void free_time_events(pa_time_event *e) {
+ while (e) {
+ pa_time_event *r = e;
+ e = r->next;
+
+ if (r->source) {
+ g_source_destroy(r->source);
+ g_source_unref(r->source);
+ }
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+static void free_defer_events(pa_defer_event *e) {
+ while (e) {
+ pa_defer_event *r = e;
+ e = r->next;
+
+ if (r->source) {
+ g_source_destroy(r->source);
+ g_source_unref(r->source);
+ }
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+void pa_glib_mainloop_free(pa_glib_mainloop* g) {
+ assert(g);
+
+ free_io_events(g->io_events);
+ free_io_events(g->dead_io_events);
+ free_defer_events(g->defer_events);
+ free_defer_events(g->dead_defer_events);
+ free_time_events(g->time_events);
+ free_time_events(g->dead_time_events);
+
+ if (g->cleanup_source) {
+ g_source_destroy(g->cleanup_source);
+ g_source_unref(g->cleanup_source);
+ }
+
+ g_main_context_unref(g->glib_main_context);
+ pa_xfree(g);
+}
+
+pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
+ assert(g);
+ return &g->api;
+}
+
+static gboolean free_dead_events(gpointer p) {
+ pa_glib_mainloop *g = p;
+ assert(g);
+
+ free_io_events(g->dead_io_events);
+ free_defer_events(g->dead_defer_events);
+ free_time_events(g->dead_time_events);
+
+ g->dead_io_events = NULL;
+ g->dead_defer_events = NULL;
+ g->dead_time_events = NULL;
+
+ g_source_destroy(g->cleanup_source);
+ g_source_unref(g->cleanup_source);
+ g->cleanup_source = NULL;
+
+ return FALSE;
+}
+
+static void schedule_free_dead_events(pa_glib_mainloop *g) {
+ assert(g && g->glib_main_context);
+
+ if (g->cleanup_source)
+ return;
+
+ g->cleanup_source = g_idle_source_new();
+ assert(g->cleanup_source);
+ g_source_set_callback(g->cleanup_source, free_dead_events, g, NULL);
+ g_source_attach(g->cleanup_source, g->glib_main_context);
+}
diff --git a/src/polyp/glib-mainloop.h b/src/polyp/glib-mainloop.h
new file mode 100644
index 00000000..b4815ed9
--- /dev/null
+++ b/src/polyp/glib-mainloop.h
@@ -0,0 +1,55 @@
+#ifndef fooglibmainloophfoo
+#define fooglibmainloophfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <glib.h>
+
+#include <polyp/mainloop-api.h>
+#include <polyp/cdecl.h>
+
+/** \file
+ * GLIB main loop support */
+
+PA_C_DECL_BEGIN
+
+/** \pa_glib_mainloop
+ * An opaque GLIB main loop object */
+typedef struct pa_glib_mainloop pa_glib_mainloop;
+
+/** Create a new GLIB main loop object for the specified GLIB main loop context. If c is NULL the default context is used. */
+#if GLIB_MAJOR_VERSION >= 2
+pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c);
+#else
+pa_glib_mainloop *pa_glib_mainloop_new(void);
+#endif
+
+
+/** Free the GLIB main loop object */
+void pa_glib_mainloop_free(pa_glib_mainloop* g);
+
+/** Return the abstract main loop API vtable for the GLIB main loop object */
+pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/glib12-mainloop.c b/src/polyp/glib12-mainloop.c
new file mode 100644
index 00000000..80a02b1c
--- /dev/null
+++ b/src/polyp/glib12-mainloop.c
@@ -0,0 +1,500 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+
+#include <polyp/glib-mainloop.h>
+#include <polypcore/idxset.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/util.h>
+
+/* A mainloop implementation based on GLIB 1.2 */
+
+struct pa_io_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ GIOChannel *io_channel;
+ guint source;
+ GIOCondition io_condition;
+ int fd;
+ void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_io_event*e, void *userdata);
+ pa_io_event *next, *prev;
+};
+
+struct pa_time_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ guint source;
+ struct timeval timeval;
+ void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata);
+ pa_time_event *next, *prev;
+};
+
+struct pa_defer_event {
+ pa_glib_mainloop *mainloop;
+ int dead;
+ guint source;
+ void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata);
+ pa_defer_event *next, *prev;
+};
+
+struct pa_glib_mainloop {
+ pa_mainloop_api api;
+ guint cleanup_source;
+ pa_io_event *io_events, *dead_io_events;
+ pa_time_event *time_events, *dead_time_events;
+ pa_defer_event *defer_events, *dead_defer_events;
+};
+
+static void schedule_free_dead_events(pa_glib_mainloop *g);
+
+static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f);
+
+static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) {
+ pa_io_event *e;
+ pa_glib_mainloop *g;
+
+ assert(m && m->userdata && fd >= 0 && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_io_event));
+ e->mainloop = m->userdata;
+ e->dead = 0;
+ e->fd = fd;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+
+ e->io_channel = g_io_channel_unix_new(e->fd);
+ assert(e->io_channel);
+ e->source = (guint) -1;
+ e->io_condition = 0;
+
+ glib_io_enable(e, f);
+
+ e->next = g->io_events;
+ if (e->next) e->next->prev = e;
+ g->io_events = e;
+ e->prev = NULL;
+
+ return e;
+}
+
+static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
+ pa_io_event *e = data;
+ pa_io_event_flags_t f;
+ assert(source && e && e->io_channel == source);
+
+ f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
+ (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
+ (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
+ (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
+
+ e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
+ return TRUE;
+}
+
+static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
+ GIOCondition c;
+ assert(e && !e->dead);
+
+ c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
+
+ if (c == e->io_condition)
+ return;
+
+ if (e->source != (guint) -1)
+ g_source_remove(e->source);
+
+ e->source = g_io_add_watch_full(e->io_channel, G_PRIORITY_DEFAULT, c | G_IO_ERR | G_IO_HUP, io_cb, e, NULL);
+ assert(e->source != (guint) -1);
+ e->io_condition = c;
+}
+
+static void glib_io_free(pa_io_event*e) {
+ assert(e && !e->dead);
+
+ if (e->source != (guint) -1) {
+ g_source_remove(e->source);
+ e->source = (guint) -1;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->io_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_io_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_io_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Time sources */
+
+static void glib_time_restart(pa_time_event*e, const struct timeval *tv);
+
+static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
+ pa_glib_mainloop *g;
+ pa_time_event *e;
+
+ assert(m && m->userdata && tv && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_time_event));
+ e->mainloop = g;
+ e->dead = 0;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+ e->source = (guint) -1;
+
+ glib_time_restart(e, tv);
+
+ e->next = g->time_events;
+ if (e->next) e->next->prev = e;
+ g->time_events = e;
+ e->prev = NULL;
+
+ return e;
+}
+
+static guint msec_diff(const struct timeval *a, const struct timeval *b) {
+ guint r;
+ assert(a && b);
+
+ if (a->tv_sec < b->tv_sec)
+ return 0;
+
+ if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
+ return 0;
+
+ r = (a->tv_sec-b->tv_sec)*1000;
+
+ if (a->tv_usec >= b->tv_usec)
+ r += (a->tv_usec - b->tv_usec) / 1000;
+ else
+ r -= (b->tv_usec - a->tv_usec) / 1000;
+
+ return r;
+}
+
+static gboolean time_cb(gpointer data) {
+ pa_time_event* e = data;
+ assert(e && e->mainloop && e->source != (guint) -1);
+
+ g_source_remove(e->source);
+ e->source = (guint) -1;
+
+ e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
+ return FALSE;
+}
+
+static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
+ struct timeval now;
+ assert(e && e->mainloop && !e->dead);
+
+ pa_gettimeofday(&now);
+ if (e->source != (guint) -1)
+ g_source_remove(e->source);
+
+ if (tv) {
+ e->timeval = *tv;
+ e->source = g_timeout_add_full(G_PRIORITY_DEFAULT, msec_diff(tv, &now), time_cb, e, NULL);
+ assert(e->source != (guint) -1);
+ } else
+ e->source = (guint) -1;
+ }
+
+static void glib_time_free(pa_time_event *e) {
+ assert(e && e->mainloop && !e->dead);
+
+ if (e->source != (guint) -1) {
+ g_source_remove(e->source);
+ e->source = (guint) -1;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->time_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_time_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_time_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Deferred sources */
+
+static void glib_defer_enable(pa_defer_event *e, int b);
+
+static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) {
+ pa_defer_event *e;
+ pa_glib_mainloop *g;
+
+ assert(m && m->userdata && callback);
+ g = m->userdata;
+
+ e = pa_xmalloc(sizeof(pa_defer_event));
+ e->mainloop = g;
+ e->dead = 0;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+ e->source = (guint) -1;
+
+ glib_defer_enable(e, 1);
+
+ e->next = g->defer_events;
+ if (e->next) e->next->prev = e;
+ g->defer_events = e;
+ e->prev = NULL;
+ return e;
+}
+
+static gboolean idle_cb(gpointer data) {
+ pa_defer_event* e = data;
+ assert(e && e->mainloop && e->source != (guint) -1);
+
+ e->callback(&e->mainloop->api, e, e->userdata);
+ return TRUE;
+}
+
+static void glib_defer_enable(pa_defer_event *e, int b) {
+ assert(e && e->mainloop);
+
+ if (e->source != (guint) -1 && !b) {
+ g_source_remove(e->source);
+ e->source = (guint) -1;
+ } else if (e->source == (guint) -1 && b) {
+ e->source = g_idle_add_full(G_PRIORITY_HIGH, idle_cb, e, NULL);
+ assert(e->source != (guint) -1);
+ }
+}
+
+static void glib_defer_free(pa_defer_event *e) {
+ assert(e && e->mainloop && !e->dead);
+
+ if (e->source != (guint) -1) {
+ g_source_remove(e->source);
+ e->source = (guint) -1;
+ }
+
+ if (e->prev)
+ e->prev->next = e->next;
+ else
+ e->mainloop->defer_events = e->next;
+
+ if (e->next)
+ e->next->prev = e->prev;
+
+ if ((e->next = e->mainloop->dead_defer_events))
+ e->next->prev = e;
+
+ e->mainloop->dead_defer_events = e;
+ e->prev = NULL;
+
+ e->dead = 1;
+ schedule_free_dead_events(e->mainloop);
+}
+
+static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* quit() */
+
+static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) {
+ pa_glib_mainloop *g;
+ assert(a && a->userdata);
+ g = a->userdata;
+
+ /* NOOP */
+}
+
+static const pa_mainloop_api vtable = {
+ .userdata = NULL,
+
+ .io_new = glib_io_new,
+ .io_enable = glib_io_enable,
+ .io_free = glib_io_free,
+ .io_set_destroy= glib_io_set_destroy,
+
+ .time_new = glib_time_new,
+ .time_restart = glib_time_restart,
+ .time_free = glib_time_free,
+ .time_set_destroy = glib_time_set_destroy,
+
+ .defer_new = glib_defer_new,
+ .defer_enable = glib_defer_enable,
+ .defer_free = glib_defer_free,
+ .defer_set_destroy = glib_defer_set_destroy,
+
+ .quit = glib_quit,
+};
+
+pa_glib_mainloop *pa_glib_mainloop_new(void) {
+ pa_glib_mainloop *g;
+
+ g = pa_xmalloc(sizeof(pa_glib_mainloop));
+
+ g->api = vtable;
+ g->api.userdata = g;
+
+ g->io_events = g->dead_io_events = NULL;
+ g->time_events = g->dead_time_events = NULL;
+ g->defer_events = g->dead_defer_events = NULL;
+
+ g->cleanup_source = (guint) -1;
+ return g;
+}
+
+static void free_io_events(pa_io_event *e) {
+ while (e) {
+ pa_io_event *r = e;
+ e = r->next;
+
+ if (r->source != (guint) -1)
+ g_source_remove(r->source);
+
+ if (r->io_channel)
+ g_io_channel_unref(r->io_channel);
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+static void free_time_events(pa_time_event *e) {
+ while (e) {
+ pa_time_event *r = e;
+ e = r->next;
+
+ if (r->source != (guint) -1)
+ g_source_remove(r->source);
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+static void free_defer_events(pa_defer_event *e) {
+ while (e) {
+ pa_defer_event *r = e;
+ e = r->next;
+
+ if (r->source != (guint) -1)
+ g_source_remove(r->source);
+
+ if (r->destroy_callback)
+ r->destroy_callback(&r->mainloop->api, r, r->userdata);
+
+ pa_xfree(r);
+ }
+}
+
+void pa_glib_mainloop_free(pa_glib_mainloop* g) {
+ assert(g);
+
+ free_io_events(g->io_events);
+ free_io_events(g->dead_io_events);
+ free_defer_events(g->defer_events);
+ free_defer_events(g->dead_defer_events);
+ free_time_events(g->time_events);
+ free_time_events(g->dead_time_events);
+
+ if (g->cleanup_source != (guint) -1)
+ g_source_remove(g->cleanup_source);
+
+ pa_xfree(g);
+}
+
+pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
+ assert(g);
+ return &g->api;
+}
+
+static gboolean free_dead_events(gpointer p) {
+ pa_glib_mainloop *g = p;
+ assert(g);
+
+ free_io_events(g->dead_io_events);
+ free_defer_events(g->dead_defer_events);
+ free_time_events(g->dead_time_events);
+
+ g->dead_io_events = NULL;
+ g->dead_defer_events = NULL;
+ g->dead_time_events = NULL;
+
+ g_source_remove(g->cleanup_source);
+ g->cleanup_source = (guint) -1;
+
+ return FALSE;
+}
+
+static void schedule_free_dead_events(pa_glib_mainloop *g) {
+ assert(g);
+
+ if (g->cleanup_source != (guint) -1)
+ return;
+
+ g->cleanup_source = g_idle_add_full(G_PRIORITY_HIGH, free_dead_events, g, NULL);
+}
diff --git a/src/polyp/mainloop-api.c b/src/polyp/mainloop-api.c
new file mode 100644
index 00000000..a3eaee9c
--- /dev/null
+++ b/src/polyp/mainloop-api.c
@@ -0,0 +1,68 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <assert.h>
+#include <stdlib.h>
+
+#include "mainloop-api.h"
+#include <polypcore/xmalloc.h>
+#include <polypcore/gccmacro.h>
+
+struct once_info {
+ void (*callback)(pa_mainloop_api*m, void *userdata);
+ void *userdata;
+};
+
+static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+ struct once_info *i = userdata;
+ assert(m && i && i->callback);
+
+ i->callback(m, i->userdata);
+
+ assert(m->defer_free);
+ m->defer_free(e);
+}
+
+static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) {
+ struct once_info *i = userdata;
+ assert(m && i);
+ pa_xfree(i);
+}
+
+void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) {
+ struct once_info *i;
+ pa_defer_event *e;
+ assert(m && callback);
+
+ i = pa_xnew(struct once_info, 1);
+ i->callback = callback;
+ i->userdata = userdata;
+
+ assert(m->defer_new);
+ e = m->defer_new(m, once_callback, i);
+ assert(e);
+ m->defer_set_destroy(e, free_callback);
+}
+
diff --git a/src/polyp/mainloop-api.h b/src/polyp/mainloop-api.h
new file mode 100644
index 00000000..91ee4111
--- /dev/null
+++ b/src/polyp/mainloop-api.h
@@ -0,0 +1,120 @@
+#ifndef foomainloopapihfoo
+#define foomainloopapihfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/time.h>
+#include <time.h>
+
+#include <polyp/cdecl.h>
+
+/** \file
+ *
+ * Main loop abstraction layer. Both the polypaudio core and the
+ * polypaudio client library use a main loop abstraction layer. Due to
+ * this it is possible to embed polypaudio into other
+ * applications easily. Two main loop implemenations are
+ * currently available:
+ * \li A minimal implementation based on the C library's poll() function (See \ref mainloop.h)
+ * \li A wrapper around the GLIB main loop. Use this to embed polypaudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h)
+ *
+ * The structure pa_mainloop_api is used as vtable for the main loop abstraction.
+ *
+ * This mainloop abstraction layer has no direct support for UNIX signals. Generic, mainloop implementation agnostic support is available throught \ref mainloop-signal.h.
+ * */
+
+PA_C_DECL_BEGIN
+
+/** A bitmask for IO events */
+typedef enum pa_io_event_flags {
+ PA_IO_EVENT_NULL = 0, /**< No event */
+ PA_IO_EVENT_INPUT = 1, /**< Input event */
+ PA_IO_EVENT_OUTPUT = 2, /**< Output event */
+ PA_IO_EVENT_HANGUP = 4, /**< Hangup event */
+ PA_IO_EVENT_ERROR = 8 /**< Error event */
+} pa_io_event_flags_t;
+
+/** \pa_io_event
+ * An opaque IO event source object */
+typedef struct pa_io_event pa_io_event;
+
+/** \pa_defer_event
+ * An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */
+typedef struct pa_defer_event pa_defer_event;
+
+/** \pa_time_event
+ * An opaque timer event source object */
+typedef struct pa_time_event pa_time_event;
+
+/** An abstract mainloop API vtable */
+typedef struct pa_mainloop_api pa_mainloop_api;
+
+struct pa_mainloop_api {
+ /** A pointer to some private, arbitrary data of the main loop implementation */
+ void *userdata;
+
+ /** Create a new IO event source object */
+ pa_io_event* (*io_new)(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, void (*callback) (pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata);
+
+ /** Enable or disable IO events on this object */
+ void (*io_enable)(pa_io_event* e, pa_io_event_flags_t events);
+
+ /** Free a IO event source object */
+ void (*io_free)(pa_io_event* e);
+
+ /** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */
+ void (*io_set_destroy)(pa_io_event *e, void (*callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata));
+
+ /** Create a new timer event source object for the specified Unix time */
+ pa_time_event* (*time_new)(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata), void *userdata);
+
+ /** Restart a running or expired timer event source with a new Unix time */
+ void (*time_restart)(pa_time_event* e, const struct timeval *tv);
+
+ /** Free a deferred timer event source object */
+ void (*time_free)(pa_time_event* e);
+
+ /** Set a function that is called when the timer event source is destroyed. Use this to free the userdata argument if required */
+ void (*time_set_destroy)(pa_time_event *e, void (*callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata));
+
+ /** Create a new deferred event source object */
+ pa_defer_event* (*defer_new)(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event* e, void *userdata), void *userdata);
+
+ /** Enable or disable a deferred event source temporarily */
+ void (*defer_enable)(pa_defer_event* e, int b);
+
+ /** Free a deferred event source object */
+ void (*defer_free)(pa_defer_event* e);
+
+ /** Set a function that is called when the deferred event source is destroyed. Use this to free the userdata argument if required */
+ void (*defer_set_destroy)(pa_defer_event *e, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata));
+
+ /** Exit the main loop and return the specfied retval*/
+ void (*quit)(pa_mainloop_api*a, int retval);
+};
+
+/** Run the specified callback function once from the main loop using an anonymous defer event. */
+void pa_mainloop_api_once(pa_mainloop_api*m, void (*callback)(pa_mainloop_api*m, void *userdata), void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/mainloop-signal.c b/src/polyp/mainloop-signal.c
new file mode 100644
index 00000000..a03c9159
--- /dev/null
+++ b/src/polyp/mainloop-signal.c
@@ -0,0 +1,267 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <polyp/mainloop-signal.h>
+#include <polypcore/util.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+#include <polypcore/gccmacro.h>
+
+struct pa_signal_event {
+ int sig;
+#ifdef HAVE_SIGACTION
+ struct sigaction saved_sigaction;
+#else
+ void (*saved_handler)(int sig);
+#endif
+ void (*callback) (pa_mainloop_api*a, pa_signal_event *e, int sig, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api*a, pa_signal_event*e, void *userdata);
+ pa_signal_event *previous, *next;
+};
+
+static pa_mainloop_api *api = NULL;
+static int signal_pipe[2] = { -1, -1 };
+static pa_io_event* io_event = NULL;
+static pa_defer_event *defer_event = NULL;
+static pa_signal_event *signals = NULL;
+
+#ifdef OS_IS_WIN32
+static unsigned int waiting_signals = 0;
+static CRITICAL_SECTION crit;
+#endif
+
+static void signal_handler(int sig) {
+#ifndef HAVE_SIGACTION
+ signal(sig, signal_handler);
+#endif
+ write(signal_pipe[1], &sig, sizeof(sig));
+
+#ifdef OS_IS_WIN32
+ EnterCriticalSection(&crit);
+ waiting_signals++;
+ LeaveCriticalSection(&crit);
+#endif
+}
+
+static void dispatch(pa_mainloop_api*a, int sig) {
+ pa_signal_event*s;
+
+ for (s = signals; s; s = s->next)
+ if (s->sig == sig) {
+ assert(s->callback);
+ s->callback(a, s, sig, s->userdata);
+ break;
+ }
+}
+
+static void defer(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event*e, PA_GCC_UNUSED void *userdata) {
+ ssize_t r;
+ int sig;
+ unsigned int sigs;
+
+#ifdef OS_IS_WIN32
+ EnterCriticalSection(&crit);
+ sigs = waiting_signals;
+ waiting_signals = 0;
+ LeaveCriticalSection(&crit);
+#endif
+
+ while (sigs) {
+ if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) {
+ pa_log(__FILE__": read(): %s\n", strerror(errno));
+ return;
+ }
+
+ if (r != sizeof(sig)) {
+ pa_log(__FILE__": short read()\n");
+ return;
+ }
+
+ dispatch(a, sig);
+
+ sigs--;
+ }
+}
+
+static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) {
+ ssize_t r;
+ int sig;
+ assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]);
+
+
+ if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) {
+ if (errno == EAGAIN)
+ return;
+
+ pa_log(__FILE__": read(): %s\n", strerror(errno));
+ return;
+ }
+
+ if (r != sizeof(sig)) {
+ pa_log(__FILE__": short read()\n");
+ return;
+ }
+
+ dispatch(a, sig);
+}
+
+int pa_signal_init(pa_mainloop_api *a) {
+ assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event && !defer_event);
+
+#ifdef OS_IS_WIN32
+ if (_pipe(signal_pipe, 200, _O_BINARY) < 0) {
+#else
+ if (pipe(signal_pipe) < 0) {
+#endif
+ pa_log(__FILE__": pipe() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ pa_make_nonblock_fd(signal_pipe[0]);
+ pa_make_nonblock_fd(signal_pipe[1]);
+ pa_fd_set_cloexec(signal_pipe[0], 1);
+ pa_fd_set_cloexec(signal_pipe[1], 1);
+
+ api = a;
+
+#ifndef OS_IS_WIN32
+ io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
+ assert(io_event);
+#else
+ defer_event = api->defer_new(api, defer, NULL);
+ assert(defer_event);
+
+ InitializeCriticalSection(&crit);
+#endif
+
+ return 0;
+}
+
+void pa_signal_done(void) {
+ assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && (io_event || defer_event));
+
+ while (signals)
+ pa_signal_free(signals);
+
+
+#ifndef OS_IS_WIN32
+ api->io_free(io_event);
+ io_event = NULL;
+#else
+ api->defer_free(defer_event);
+ defer_event = NULL;
+
+ DeleteCriticalSection(&crit);
+#endif
+
+ close(signal_pipe[0]);
+ close(signal_pipe[1]);
+ signal_pipe[0] = signal_pipe[1] = -1;
+
+ api = NULL;
+}
+
+pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata) {
+ pa_signal_event *e = NULL;
+
+#ifdef HAVE_SIGACTION
+ struct sigaction sa;
+#endif
+
+ assert(sig > 0 && _callback);
+
+ for (e = signals; e; e = e->next)
+ if (e->sig == sig)
+ goto fail;
+
+ e = pa_xmalloc(sizeof(pa_signal_event));
+ e->sig = sig;
+ e->callback = _callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+
+#ifdef HAVE_SIGACTION
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(sig, &sa, &e->saved_sigaction) < 0)
+#else
+ if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR)
+#endif
+ goto fail;
+
+ e->previous = NULL;
+ e->next = signals;
+ signals = e;
+
+ return e;
+fail:
+ if (e)
+ pa_xfree(e);
+ return NULL;
+}
+
+void pa_signal_free(pa_signal_event *e) {
+ assert(e);
+
+ if (e->next)
+ e->next->previous = e->previous;
+ if (e->previous)
+ e->previous->next = e->next;
+ else
+ signals = e->next;
+
+#ifdef HAVE_SIGACTION
+ sigaction(e->sig, &e->saved_sigaction, NULL);
+#else
+ signal(e->sig, e->saved_handler);
+#endif
+
+ if (e->destroy_callback)
+ e->destroy_callback(api, e, e->userdata);
+
+ pa_xfree(e);
+}
+
+void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = _callback;
+}
diff --git a/src/polyp/mainloop-signal.h b/src/polyp/mainloop-signal.h
new file mode 100644
index 00000000..76065b22
--- /dev/null
+++ b/src/polyp/mainloop-signal.h
@@ -0,0 +1,60 @@
+#ifndef foomainloopsignalhfoo
+#define foomainloopsignalhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/mainloop-api.h>
+#include <polyp/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+/** \file
+ * UNIX signal support for main loops. In contrast to other
+ * main loop event sources such as timer and IO events, UNIX signal
+ * support requires modification of the global process
+ * environment. Due to this the generic main loop abstraction layer as
+ * defined in \ref mainloop-api.h doesn't have direct support for UNIX
+ * signals. However, you may hook signal support into an abstract main loop via the routines defined herein.
+ */
+
+/** Initialize the UNIX signal subsystem and bind it to the specified main loop */
+int pa_signal_init(pa_mainloop_api *api);
+
+/** Cleanup the signal subsystem */
+void pa_signal_done(void);
+
+/** \pa_signal_event
+ * An opaque UNIX signal event source object */
+typedef struct pa_signal_event pa_signal_event;
+
+/** Create a new UNIX signal event source object */
+pa_signal_event* pa_signal_new(int sig, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata);
+
+/** Free a UNIX signal event source object */
+void pa_signal_free(pa_signal_event *e);
+
+/** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */
+void pa_signal_set_destroy(pa_signal_event *e, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata));
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/mainloop.c b/src/polyp/mainloop.c
new file mode 100644
index 00000000..3fa9245c
--- /dev/null
+++ b/src/polyp/mainloop.c
@@ -0,0 +1,812 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#else
+#include "poll.h"
+#endif
+
+#include <polypcore/winsock.h>
+
+#include "mainloop.h"
+#include <polypcore/util.h>
+#include <polypcore/idxset.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+struct pa_io_event {
+ pa_mainloop *mainloop;
+ int dead;
+ int fd;
+ pa_io_event_flags_t events;
+ void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
+ struct pollfd *pollfd;
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata);
+};
+
+struct pa_time_event {
+ pa_mainloop *mainloop;
+ int dead;
+ int enabled;
+ struct timeval timeval;
+ void (*callback)(pa_mainloop_api*a, pa_time_event *e, const struct timeval*tv, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata);
+};
+
+struct pa_defer_event {
+ pa_mainloop *mainloop;
+ int dead;
+ int enabled;
+ void (*callback)(pa_mainloop_api*a, pa_defer_event*e, void *userdata);
+ void *userdata;
+ void (*destroy_callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata);
+};
+
+struct pa_mainloop {
+ pa_idxset *io_events, *time_events, *defer_events;
+ int io_events_scan_dead, defer_events_scan_dead, time_events_scan_dead;
+
+ struct pollfd *pollfds;
+ unsigned max_pollfds, n_pollfds;
+ int rebuild_pollfds;
+
+ int prepared_timeout;
+
+ int quit, retval;
+ pa_mainloop_api api;
+
+ int deferred_pending;
+
+ int wakeup_pipe[2];
+
+ enum {
+ STATE_PASSIVE,
+ STATE_PREPARED,
+ STATE_POLLING,
+ STATE_POLLED,
+ STATE_QUIT
+ } state;
+};
+
+/* IO events */
+static pa_io_event* mainloop_io_new(
+ pa_mainloop_api*a,
+ int fd,
+ pa_io_event_flags_t events,
+ void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata),
+ void *userdata) {
+
+ pa_mainloop *m;
+ pa_io_event *e;
+
+ assert(a && a->userdata && fd >= 0 && callback);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ pa_mainloop_wakeup(m);
+
+ e = pa_xmalloc(sizeof(pa_io_event));
+ e->mainloop = m;
+ e->dead = 0;
+
+ e->fd = fd;
+ e->events = events;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+ e->pollfd = NULL;
+
+#ifdef OS_IS_WIN32
+ {
+ fd_set xset;
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ FD_ZERO (&xset);
+ FD_SET (fd, &xset);
+
+ if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset,
+ SELECT_TYPE_ARG5 &tv) == -1) &&
+ (WSAGetLastError() == WSAENOTSOCK)) {
+ pa_log_warn(__FILE__": WARNING: cannot monitor non-socket file descriptors.\n");
+ e->dead = 1;
+ }
+ }
+#endif
+
+ pa_idxset_put(m->io_events, e, NULL);
+ m->rebuild_pollfds = 1;
+ return e;
+}
+
+static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) {
+ assert(e && e->mainloop);
+
+ pa_mainloop_wakeup(e->mainloop);
+
+ e->events = events;
+ e->mainloop->rebuild_pollfds = 1;
+}
+
+static void mainloop_io_free(pa_io_event *e) {
+ assert(e && e->mainloop);
+
+ pa_mainloop_wakeup(e->mainloop);
+
+ e->dead = e->mainloop->io_events_scan_dead = e->mainloop->rebuild_pollfds = 1;
+}
+
+static void mainloop_io_set_destroy(pa_io_event *e, void (*callback)(pa_mainloop_api*a, pa_io_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Defer events */
+static pa_defer_event* mainloop_defer_new(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata), void *userdata) {
+ pa_mainloop *m;
+ pa_defer_event *e;
+
+ assert(a && a->userdata && callback);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ e = pa_xmalloc(sizeof(pa_defer_event));
+ e->mainloop = m;
+ e->dead = 0;
+
+ e->enabled = 1;
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+
+ pa_idxset_put(m->defer_events, e, NULL);
+
+ m->deferred_pending++;
+ return e;
+}
+
+static void mainloop_defer_enable(pa_defer_event *e, int b) {
+ assert(e);
+
+ if (e->enabled && !b) {
+ assert(e->mainloop->deferred_pending > 0);
+ e->mainloop->deferred_pending--;
+ } else if (!e->enabled && b)
+ e->mainloop->deferred_pending++;
+
+ e->enabled = b;
+}
+
+static void mainloop_defer_free(pa_defer_event *e) {
+ assert(e);
+ e->dead = e->mainloop->defer_events_scan_dead = 1;
+
+ if (e->enabled) {
+ e->enabled = 0;
+ assert(e->mainloop->deferred_pending > 0);
+ e->mainloop->deferred_pending--;
+ }
+}
+
+static void mainloop_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api*a, pa_defer_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* Time events */
+static pa_time_event* mainloop_time_new(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
+ pa_mainloop *m;
+ pa_time_event *e;
+
+ assert(a && a->userdata && callback);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ pa_mainloop_wakeup(m);
+
+ e = pa_xmalloc(sizeof(pa_time_event));
+ e->mainloop = m;
+ e->dead = 0;
+
+ e->enabled = !!tv;
+ if (tv)
+ e->timeval = *tv;
+
+ e->callback = callback;
+ e->userdata = userdata;
+ e->destroy_callback = NULL;
+
+ pa_idxset_put(m->time_events, e, NULL);
+
+ return e;
+}
+
+static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {
+ assert(e);
+
+ pa_mainloop_wakeup(e->mainloop);
+
+ if (tv) {
+ e->enabled = 1;
+ e->timeval = *tv;
+ } else
+ e->enabled = 0;
+}
+
+static void mainloop_time_free(pa_time_event *e) {
+ assert(e);
+
+ pa_mainloop_wakeup(e->mainloop);
+
+ e->dead = e->mainloop->time_events_scan_dead = 1;
+}
+
+static void mainloop_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*a, pa_time_event *e, void *userdata)) {
+ assert(e);
+ e->destroy_callback = callback;
+}
+
+/* quit() */
+
+static void mainloop_quit(pa_mainloop_api*a, int retval) {
+ pa_mainloop *m;
+ assert(a && a->userdata);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ pa_mainloop_wakeup(m);
+
+ m->quit = 1;
+ m->retval = retval;
+}
+
+static const pa_mainloop_api vtable = {
+ .userdata = NULL,
+
+ .io_new= mainloop_io_new,
+ .io_enable= mainloop_io_enable,
+ .io_free= mainloop_io_free,
+ .io_set_destroy= mainloop_io_set_destroy,
+
+ .time_new = mainloop_time_new,
+ .time_restart = mainloop_time_restart,
+ .time_free = mainloop_time_free,
+ .time_set_destroy = mainloop_time_set_destroy,
+
+ .defer_new = mainloop_defer_new,
+ .defer_enable = mainloop_defer_enable,
+ .defer_free = mainloop_defer_free,
+ .defer_set_destroy = mainloop_defer_set_destroy,
+
+ .quit = mainloop_quit,
+};
+
+pa_mainloop *pa_mainloop_new(void) {
+ pa_mainloop *m;
+
+ m = pa_xmalloc(sizeof(pa_mainloop));
+
+#ifndef OS_ISWIN32
+ if (pipe(m->wakeup_pipe) < 0) {
+ pa_xfree(m);
+ return NULL;
+ }
+
+ pa_make_nonblock_fd(m->wakeup_pipe[0]);
+ pa_make_nonblock_fd(m->wakeup_pipe[1]);
+#else
+ m->wakeup_pipe[0] = -1;
+ m->wakeup_pipe[1] = -1;
+#endif
+
+ m->io_events = pa_idxset_new(NULL, NULL);
+ m->defer_events = pa_idxset_new(NULL, NULL);
+ m->time_events = pa_idxset_new(NULL, NULL);
+
+ assert(m->io_events && m->defer_events && m->time_events);
+
+ m->io_events_scan_dead = m->defer_events_scan_dead = m->time_events_scan_dead = 0;
+
+ m->pollfds = NULL;
+ m->max_pollfds = m->n_pollfds = m->rebuild_pollfds = 0;
+
+ m->quit = m->retval = 0;
+
+ m->api = vtable;
+ m->api.userdata = m;
+
+ m->deferred_pending = 0;
+
+ m->state = STATE_PASSIVE;
+
+ return m;
+}
+
+static int io_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) {
+ pa_io_event *e = p;
+ int *all = userdata;
+ assert(e && del && all);
+
+ if (!*all && !e->dead)
+ return 0;
+
+ if (e->destroy_callback)
+ e->destroy_callback(&e->mainloop->api, e, e->userdata);
+ pa_xfree(e);
+ *del = 1;
+ return 0;
+}
+
+static int time_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) {
+ pa_time_event *e = p;
+ int *all = userdata;
+ assert(e && del && all);
+
+ if (!*all && !e->dead)
+ return 0;
+
+ if (e->destroy_callback)
+ e->destroy_callback(&e->mainloop->api, e, e->userdata);
+ pa_xfree(e);
+ *del = 1;
+ return 0;
+}
+
+static int defer_foreach(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void*userdata) {
+ pa_defer_event *e = p;
+ int *all = userdata;
+ assert(e && del && all);
+
+ if (!*all && !e->dead)
+ return 0;
+
+ if (e->destroy_callback)
+ e->destroy_callback(&e->mainloop->api, e, e->userdata);
+ pa_xfree(e);
+ *del = 1;
+ return 0;
+}
+
+void pa_mainloop_free(pa_mainloop* m) {
+ int all = 1;
+ assert(m && (m->state != STATE_POLLING));
+
+ pa_idxset_foreach(m->io_events, io_foreach, &all);
+ pa_idxset_foreach(m->time_events, time_foreach, &all);
+ pa_idxset_foreach(m->defer_events, defer_foreach, &all);
+
+ pa_idxset_free(m->io_events, NULL, NULL);
+ pa_idxset_free(m->time_events, NULL, NULL);
+ pa_idxset_free(m->defer_events, NULL, NULL);
+
+ pa_xfree(m->pollfds);
+
+ if (m->wakeup_pipe[0] >= 0)
+ close(m->wakeup_pipe[0]);
+ if (m->wakeup_pipe[1] >= 0)
+ close(m->wakeup_pipe[1]);
+
+ pa_xfree(m);
+}
+
+static void scan_dead(pa_mainloop *m) {
+ int all = 0;
+ assert(m);
+
+ if (m->io_events_scan_dead)
+ pa_idxset_foreach(m->io_events, io_foreach, &all);
+ if (m->time_events_scan_dead)
+ pa_idxset_foreach(m->time_events, time_foreach, &all);
+ if (m->defer_events_scan_dead)
+ pa_idxset_foreach(m->defer_events, defer_foreach, &all);
+
+ m->io_events_scan_dead = m->time_events_scan_dead = m->defer_events_scan_dead = 0;
+}
+
+static void rebuild_pollfds(pa_mainloop *m) {
+ pa_io_event*e;
+ struct pollfd *p;
+ uint32_t idx = PA_IDXSET_INVALID;
+ unsigned l;
+
+ l = pa_idxset_size(m->io_events) + 1;
+ if (m->max_pollfds < l) {
+ m->pollfds = pa_xrealloc(m->pollfds, sizeof(struct pollfd)*l);
+ m->max_pollfds = l;
+ }
+
+ m->n_pollfds = 0;
+ p = m->pollfds;
+
+ if (m->wakeup_pipe[0] >= 0) {
+ m->pollfds[0].fd = m->wakeup_pipe[0];
+ m->pollfds[0].events = POLLIN;
+ m->pollfds[0].revents = 0;
+ p++;
+ m->n_pollfds++;
+ }
+
+ for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) {
+ if (e->dead) {
+ e->pollfd = NULL;
+ continue;
+ }
+
+ e->pollfd = p;
+ p->fd = e->fd;
+ p->events =
+ ((e->events & PA_IO_EVENT_INPUT) ? POLLIN : 0) |
+ ((e->events & PA_IO_EVENT_OUTPUT) ? POLLOUT : 0) |
+ POLLHUP |
+ POLLERR;
+ p->revents = 0;
+
+ p++;
+ m->n_pollfds++;
+ }
+
+ m->rebuild_pollfds = 0;
+}
+
+static int dispatch_pollfds(pa_mainloop *m) {
+ uint32_t idx = PA_IDXSET_INVALID;
+ pa_io_event *e;
+ int r = 0;
+
+ for (e = pa_idxset_first(m->io_events, &idx); e && !m->quit; e = pa_idxset_next(m->io_events, &idx)) {
+ if (e->dead || !e->pollfd || !e->pollfd->revents)
+ continue;
+
+ assert(e->pollfd->fd == e->fd && e->callback);
+ e->callback(&m->api, e, e->fd,
+ (e->pollfd->revents & POLLHUP ? PA_IO_EVENT_HANGUP : 0) |
+ (e->pollfd->revents & POLLIN ? PA_IO_EVENT_INPUT : 0) |
+ (e->pollfd->revents & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) |
+ (e->pollfd->revents & POLLERR ? PA_IO_EVENT_ERROR : 0),
+ e->userdata);
+ e->pollfd->revents = 0;
+ r++;
+ }
+
+ return r;
+}
+
+static int dispatch_defer(pa_mainloop *m) {
+ uint32_t idx;
+ pa_defer_event *e;
+ int r = 0;
+
+ if (!m->deferred_pending)
+ return 0;
+
+ for (e = pa_idxset_first(m->defer_events, &idx); e && !m->quit; e = pa_idxset_next(m->defer_events, &idx)) {
+ if (e->dead || !e->enabled)
+ continue;
+
+ assert(e->callback);
+ e->callback(&m->api, e, e->userdata);
+ r++;
+ }
+
+ return r;
+}
+
+static int calc_next_timeout(pa_mainloop *m) {
+ uint32_t idx;
+ pa_time_event *e;
+ struct timeval now;
+ int t = -1;
+ int got_time = 0;
+
+ if (pa_idxset_isempty(m->time_events))
+ return -1;
+
+ for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) {
+ int tmp;
+
+ if (e->dead || !e->enabled)
+ continue;
+
+ /* Let's save a system call */
+ if (!got_time) {
+ pa_gettimeofday(&now);
+ got_time = 1;
+ }
+
+ if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec))
+ return 0;
+
+ tmp = (e->timeval.tv_sec - now.tv_sec)*1000;
+
+ if (e->timeval.tv_usec > now.tv_usec)
+ tmp += (e->timeval.tv_usec - now.tv_usec)/1000;
+ else
+ tmp -= (now.tv_usec - e->timeval.tv_usec)/1000;
+
+ if (tmp == 0)
+ return 0;
+ else if (t == -1 || tmp < t)
+ t = tmp;
+ }
+
+ return t;
+}
+
+static int dispatch_timeout(pa_mainloop *m) {
+ uint32_t idx;
+ pa_time_event *e;
+ struct timeval now;
+ int got_time = 0;
+ int r = 0;
+ assert(m);
+
+ if (pa_idxset_isempty(m->time_events))
+ return 0;
+
+ for (e = pa_idxset_first(m->time_events, &idx); e && !m->quit; e = pa_idxset_next(m->time_events, &idx)) {
+
+ if (e->dead || !e->enabled)
+ continue;
+
+ /* Let's save a system call */
+ if (!got_time) {
+ pa_gettimeofday(&now);
+ got_time = 1;
+ }
+
+ if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) {
+ assert(e->callback);
+
+ e->enabled = 0;
+ e->callback(&m->api, e, &e->timeval, e->userdata);
+
+ r++;
+ }
+ }
+
+ return r;
+}
+
+void pa_mainloop_wakeup(pa_mainloop *m) {
+ char c = 'W';
+ assert(m);
+
+ if (m->wakeup_pipe[1] >= 0)
+ write(m->wakeup_pipe[1], &c, sizeof(c));
+}
+
+static void clear_wakeup(pa_mainloop *m) {
+ char c[10];
+
+ assert(m);
+
+ if (m->wakeup_pipe[0] < 0)
+ return;
+
+ while (read(m->wakeup_pipe[0], &c, sizeof(c)) == sizeof(c));
+}
+
+int pa_mainloop_prepare(pa_mainloop *m, int timeout) {
+ int dispatched = 0;
+
+ assert(m && (m->state == STATE_PASSIVE));
+
+ clear_wakeup(m);
+
+ scan_dead(m);
+
+ if (m->quit)
+ goto quit;
+
+ dispatched += dispatch_defer(m);
+
+ if (m->quit)
+ goto quit;
+
+ if (m->rebuild_pollfds)
+ rebuild_pollfds(m);
+
+ m->prepared_timeout = calc_next_timeout(m);
+ if ((timeout >= 0) && (m->prepared_timeout > timeout))
+ m->prepared_timeout = timeout;
+
+ m->state = STATE_PREPARED;
+
+ return dispatched;
+
+quit:
+
+ m->state = STATE_QUIT;
+
+ return -2;
+}
+
+int pa_mainloop_poll(pa_mainloop *m) {
+ int r;
+
+ assert(m && (m->state == STATE_PREPARED));
+
+ m->state = STATE_POLLING;
+
+ r = poll(m->pollfds, m->n_pollfds, m->prepared_timeout);
+
+ if ((r < 0) && (errno == EINTR))
+ r = 0;
+
+ if (r < 0)
+ m->state = STATE_PASSIVE;
+ else
+ m->state = STATE_POLLED;
+
+ return r;
+}
+
+int pa_mainloop_dispatch(pa_mainloop *m) {
+ int dispatched = 0;
+
+ assert(m && (m->state == STATE_POLLED));
+
+ dispatched += dispatch_timeout(m);
+
+ if (m->quit)
+ goto quit;
+
+ dispatched += dispatch_pollfds(m);
+
+ if (m->quit)
+ goto quit;
+
+ m->state = STATE_PASSIVE;
+
+ return dispatched;
+
+quit:
+
+ m->state = STATE_QUIT;
+
+ return -2;
+}
+
+int pa_mainloop_get_retval(pa_mainloop *m) {
+ assert(m);
+ return m->retval;
+}
+
+int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
+ int r, dispatched = 0;
+
+ assert(m);
+
+ r = pa_mainloop_prepare(m, block ? -1 : 0);
+ if (r < 0) {
+ if ((r == -2) && retval)
+ *retval = pa_mainloop_get_retval(m);
+ return r;
+ }
+
+ dispatched += r;
+
+ r = pa_mainloop_poll(m);
+ if (r < 0) {
+ pa_log(__FILE__": poll(): %s\n", strerror(errno));
+ return r;
+ }
+
+ r = pa_mainloop_dispatch(m);
+ if (r < 0) {
+ if ((r == -2) && retval)
+ *retval = pa_mainloop_get_retval(m);
+ return r;
+ }
+
+ dispatched += r;
+
+ return dispatched;
+}
+
+int pa_mainloop_run(pa_mainloop *m, int *retval) {
+ int r;
+ while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0);
+
+ if (r == -2)
+ return 1;
+ else if (r < 0)
+ return -1;
+ else
+ return 0;
+}
+
+void pa_mainloop_quit(pa_mainloop *m, int r) {
+ assert(m);
+ pa_mainloop_wakeup(m);
+ m->quit = r;
+}
+
+pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) {
+ assert(m);
+ return &m->api;
+}
+
+int pa_mainloop_deferred_pending(pa_mainloop *m) {
+ assert(m);
+ return m->deferred_pending > 0;
+}
+
+
+#if 0
+void pa_mainloop_dump(pa_mainloop *m) {
+ assert(m);
+
+ pa_log(__FILE__": Dumping mainloop sources START\n");
+
+ {
+ uint32_t idx = PA_IDXSET_INVALID;
+ pa_io_event *e;
+ for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) {
+ if (e->dead)
+ continue;
+
+ pa_log(__FILE__": kind=io fd=%i events=%i callback=%p userdata=%p\n", e->fd, (int) e->events, (void*) e->callback, (void*) e->userdata);
+ }
+ }
+ {
+ uint32_t idx = PA_IDXSET_INVALID;
+ pa_defer_event *e;
+ for (e = pa_idxset_first(m->defer_events, &idx); e; e = pa_idxset_next(m->defer_events, &idx)) {
+ if (e->dead)
+ continue;
+
+ pa_log(__FILE__": kind=defer enabled=%i callback=%p userdata=%p\n", e->enabled, (void*) e->callback, (void*) e->userdata);
+ }
+ }
+ {
+ uint32_t idx = PA_IDXSET_INVALID;
+ pa_time_event *e;
+ for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) {
+ if (e->dead)
+ continue;
+
+ pa_log(__FILE__": kind=time enabled=%i time=%lu.%lu callback=%p userdata=%p\n", e->enabled, (unsigned long) e->timeval.tv_sec, (unsigned long) e->timeval.tv_usec, (void*) e->callback, (void*) e->userdata);
+ }
+ }
+
+ pa_log(__FILE__": Dumping mainloop sources STOP\n");
+
+}
+#endif
diff --git a/src/polyp/mainloop.h b/src/polyp/mainloop.h
new file mode 100644
index 00000000..691f8c50
--- /dev/null
+++ b/src/polyp/mainloop.h
@@ -0,0 +1,90 @@
+#ifndef foomainloophfoo
+#define foomainloophfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/mainloop-api.h>
+#include <polyp/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+/** \file
+ *
+ * A minimal main loop implementation based on the C library's poll()
+ * function. Using the routines defined herein you may create a simple
+ * main loop supporting the generic main loop abstraction layer as
+ * defined in \ref mainloop-api.h. This implementation is thread safe
+ * as long as you access the main loop object from a single thread only.*/
+
+/** \pa_mainloop
+ * An opaque main loop object
+ */
+typedef struct pa_mainloop pa_mainloop;
+
+/** Allocate a new main loop object */
+pa_mainloop *pa_mainloop_new(void);
+
+/** Free a main loop object */
+void pa_mainloop_free(pa_mainloop* m);
+
+
+/** Prepare for a single iteration of the main loop. Returns a negative value
+on error or exit request. timeout specifies a maximum timeout for the subsequent
+poll, or -1 for blocking behaviour. Defer events are also dispatched when this
+function is called. On success returns the number of source dispatched in this
+iteration.*/
+int pa_mainloop_prepare(pa_mainloop *m, int timeout);
+/** Execute the previously prepared poll. Returns a negative value on error.*/
+int pa_mainloop_poll(pa_mainloop *m);
+/** Dispatch timeout and io events from the previously executed poll. Returns
+a negative value on error. On success returns the number of source dispatched. */
+int pa_mainloop_dispatch(pa_mainloop *m);
+
+/** Return the return value as specified with the main loop's quit() routine. */
+int pa_mainloop_get_retval(pa_mainloop *m);
+
+/** Run a single iteration of the main loop. This is a convenience function
+for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
+Returns a negative value on error or exit request. If block is nonzero,
+block for events if none are queued. Optionally return the return value as
+specified with the main loop's quit() routine in the integer variable retval points
+to. On success returns the number of source dispatched in this iteration. */
+int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval);
+
+/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
+int pa_mainloop_run(pa_mainloop *m, int *retval);
+
+/** Return the abstract main loop abstraction layer vtable for this main loop. This calls pa_mainloop_iterate() iteratively.*/
+pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m);
+
+/** Return non-zero when there are any deferred events pending. \since 0.5 */
+int pa_mainloop_deferred_pending(pa_mainloop *m);
+
+/** Shutdown the main loop */
+void pa_mainloop_quit(pa_mainloop *m, int r);
+
+/** Interrupt a running poll (for threaded systems) */
+void pa_mainloop_wakeup(pa_mainloop *m);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-browser.c b/src/polyp/polyplib-browser.c
new file mode 100644
index 00000000..9a389484
--- /dev/null
+++ b/src/polyp/polyplib-browser.c
@@ -0,0 +1,312 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <assert.h>
+#include <howl.h>
+
+#include "polyplib-browser.h"
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+#include <polypcore/util.h>
+
+#define SERVICE_NAME_SINK "_polypaudio-sink._tcp."
+#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp."
+#define SERVICE_NAME_SERVER "_polypaudio-server._tcp."
+
+pa_browser {
+ int ref;
+ pa_mainloop_api *mainloop;
+
+ void (*callback)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata);
+ void *callback_userdata;
+
+ sw_discovery discovery;
+ pa_io_event *io_event;
+};
+
+
+static void io_callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags events, void *userdata) {
+ pa_browser *b = userdata;
+ assert(a && b && b->mainloop == a);
+
+ if (events != PA_IO_EVENT_INPUT || sw_discovery_read_socket(b->discovery) != SW_OKAY) {
+ pa_log(__FILE__": connection to HOWL daemon failed.\n");
+ b->mainloop->io_free(b->io_event);
+ b->io_event = NULL;
+ return;
+ }
+}
+
+static sw_result resolve_reply(
+ sw_discovery discovery,
+ sw_discovery_oid oid,
+ sw_uint32 interface_index,
+ sw_const_string name,
+ sw_const_string type,
+ sw_const_string domain,
+ sw_ipv4_address address,
+ sw_port port,
+ sw_octets text_record,
+ sw_ulong text_record_len,
+ sw_opaque extra) {
+
+ pa_browser *b = extra;
+ pa_browse_info i;
+ char ip[256], a[256];
+ pa_browse_opcode opcode;
+ int device_found = 0;
+ uint32_t cookie;
+ pa_typeid_t typeid;
+ pa_sample_spec ss;
+ int ss_valid = 0;
+ sw_text_record_iterator iterator;
+ int free_iterator = 0;
+ char *c = NULL;
+
+ assert(b);
+
+ sw_discovery_cancel(discovery, oid);
+
+ memset(&i, 0, sizeof(i));
+ i.name = name;
+
+ if (!b->callback)
+ goto fail;
+
+ if (!strcmp(type, SERVICE_NAME_SINK))
+ opcode = PA_BROWSE_NEW_SINK;
+ else if (!strcmp(type, SERVICE_NAME_SOURCE))
+ opcode = PA_BROWSE_NEW_SOURCE;
+ else if (!strcmp(type, SERVICE_NAME_SERVER))
+ opcode = PA_BROWSE_NEW_SERVER;
+ else
+ goto fail;
+
+
+ snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port);
+ i.server = a;
+
+ if (text_record && text_record_len) {
+ char key[SW_TEXT_RECORD_MAX_LEN];
+ uint8_t val[SW_TEXT_RECORD_MAX_LEN];
+ uint32_t val_len;
+
+ if (sw_text_record_iterator_init(&iterator, text_record, text_record_len) != SW_OKAY) {
+ pa_log("sw_text_record_string_iterator_init() failed.\n");
+ goto fail;
+ }
+
+ free_iterator = 1;
+
+ while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) {
+ c = pa_xstrndup((char*) val, val_len);
+
+ if (!strcmp(key, "device")) {
+ device_found = 1;
+ pa_xfree((char*) i.device);
+ i.device = c;
+ c = NULL;
+ } else if (!strcmp(key, "server-version")) {
+ pa_xfree((char*) i.server_version);
+ i.server_version = c;
+ c = NULL;
+ } else if (!strcmp(key, "user-name")) {
+ pa_xfree((char*) i.user_name);
+ i.user_name = c;
+ c = NULL;
+ } else if (!strcmp(key, "fqdn")) {
+ size_t l;
+
+ pa_xfree((char*) i.fqdn);
+ i.fqdn = c;
+ c = NULL;
+
+ l = strlen(a);
+ assert(l+1 <= sizeof(a));
+ strncat(a, " ", sizeof(a)-l-1);
+ strncat(a, i.fqdn, sizeof(a)-l-2);
+ } else if (!strcmp(key, "cookie")) {
+
+ if (pa_atou(c, &cookie) < 0)
+ goto fail;
+
+ i.cookie = &cookie;
+ } else if (!strcmp(key, "description")) {
+ pa_xfree((char*) i.description);
+ i.description = c;
+ c = NULL;
+ } else if (!strcmp(key, "typeid")) {
+
+ if (pa_atou(c, &typeid) < 0)
+ goto fail;
+
+ i.typeid = &typeid;
+ } else if (!strcmp(key, "channels")) {
+ uint32_t ch;
+
+ if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255)
+ goto fail;
+
+ ss.channels = (uint8_t) ch;
+ ss_valid |= 1;
+
+ } else if (!strcmp(key, "rate")) {
+ if (pa_atou(c, &ss.rate) < 0)
+ goto fail;
+ ss_valid |= 2;
+ } else if (!strcmp(key, "format")) {
+
+ if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID)
+ goto fail;
+
+ ss_valid |= 4;
+ }
+
+ pa_xfree(c);
+ c = NULL;
+ }
+
+ }
+
+ /* No device txt record was sent for a sink or source service */
+ if (opcode != PA_BROWSE_NEW_SERVER && !device_found)
+ goto fail;
+
+ if (ss_valid == 7)
+ i.sample_spec = &ss;
+
+
+ b->callback(b, opcode, &i, b->callback_userdata);
+
+fail:
+ pa_xfree((void*) i.device);
+ pa_xfree((void*) i.fqdn);
+ pa_xfree((void*) i.server_version);
+ pa_xfree((void*) i.user_name);
+ pa_xfree((void*) i.description);
+ pa_xfree(c);
+
+ if (free_iterator)
+ sw_text_record_iterator_fina(iterator);
+
+
+ return SW_OKAY;
+}
+
+static sw_result browse_reply(
+ sw_discovery discovery,
+ sw_discovery_oid id,
+ sw_discovery_browse_status status,
+ sw_uint32 interface_index,
+ sw_const_string name,
+ sw_const_string type,
+ sw_const_string domain,
+ sw_opaque extra) {
+
+ pa_browser *b = extra;
+ assert(b);
+
+ switch (status) {
+ case SW_DISCOVERY_BROWSE_ADD_SERVICE: {
+ sw_discovery_oid oid;
+
+ if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY)
+ pa_log("sw_discovery_resolve() failed\n");
+
+ break;
+ }
+
+ case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
+ if (b->callback) {
+ pa_browse_info i;
+ memset(&i, 0, sizeof(i));
+ i.name = name;
+ b->callback(b, PA_BROWSE_REMOVE, &i, b->callback_userdata);
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ return SW_OKAY;
+}
+
+pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
+ pa_browser *b;
+ sw_discovery_oid oid;
+
+ b = pa_xmalloc(sizeof(pa_browser));
+ b->mainloop = mainloop;
+ b->ref = 1;
+ b->callback = NULL;
+ b->callback_userdata = NULL;
+
+ if (sw_discovery_init(&b->discovery) != SW_OKAY) {
+ pa_log("sw_discovery_init() failed.\n");
+ pa_xfree(b);
+ return NULL;
+ }
+
+ if (sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SERVER, NULL, browse_reply, b, &oid) != SW_OKAY ||
+ sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SINK, NULL, browse_reply, b, &oid) != SW_OKAY ||
+ sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SOURCE, NULL, browse_reply, b, &oid) != SW_OKAY) {
+
+ pa_log("sw_discovery_browse() failed.\n");
+
+ sw_discovery_fina(b->discovery);
+ pa_xfree(b);
+ return NULL;
+ }
+
+ b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b);
+ return b;
+}
+
+static void browser_free(pa_browser *b) {
+ assert(b && b->mainloop);
+
+ if (b->io_event)
+ b->mainloop->io_free(b->io_event);
+
+ sw_discovery_fina(b->discovery);
+ pa_xfree(b);
+}
+
+pa_browser *pa_browser_ref(pa_browser *b) {
+ assert(b && b->ref >= 1);
+ b->ref++;
+ return b;
+}
+
+void pa_browser_unref(pa_browser *b) {
+ assert(b && b->ref >= 1);
+
+ if ((-- (b->ref)) <= 0)
+ browser_free(b);
+}
+
+void pa_browser_set_callback(pa_browser *b, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void* userdata), void *userdata) {
+ assert(b);
+
+ b->callback = cb;
+ b->callback_userdata = userdata;
+}
diff --git a/src/polyp/polyplib-browser.h b/src/polyp/polyplib-browser.h
new file mode 100644
index 00000000..853304d7
--- /dev/null
+++ b/src/polyp/polyplib-browser.h
@@ -0,0 +1,65 @@
+#ifndef foopolyplibbrowserhfoo
+#define foopolyplibbrowserhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/mainloop-api.h>
+#include <polyp/sample.h>
+#include <polyp/cdecl.h>
+#include <polyp/typeid.h>
+
+PA_C_DECL_BEGIN
+
+pa_browser;
+
+pa_browse_opcode {
+ PA_BROWSE_NEW_SERVER,
+ PA_BROWSE_NEW_SINK,
+ PA_BROWSE_NEW_SOURCE,
+ PA_BROWSE_REMOVE
+};
+
+pa_browser *pa_browser_new(pa_mainloop_api *mainloop);
+pa_browser *pa_browser_ref(pa_browser *z);
+void pa_browser_unref(pa_browser *z);
+
+pa_browse_info {
+ /* Unique service name */
+ const char *name; /* always available */
+
+ /* Server info */
+ const char *server; /* always available */
+ const char *server_version, *user_name, *fqdn; /* optional */
+ const uint32_t *cookie; /* optional */
+
+ /* Device info */
+ const char *device; /* always available when this information is of a sink/source */
+ const char *description; /* optional */
+ const pa_typeid_t *typeid; /* optional */
+ const pa_sample_spec *sample_spec; /* optional */
+};
+
+void pa_browser_set_callback(pa_browser *z, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata), void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-context.c b/src/polyp/polyplib-context.c
new file mode 100644
index 00000000..c392f0fc
--- /dev/null
+++ b/src/polyp/polyplib-context.c
@@ -0,0 +1,871 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include <polypcore/winsock.h>
+
+#include "polyplib-internal.h"
+#include "polyplib-context.h"
+#include "polyplib-version.h"
+#include <polypcore/native-common.h>
+#include <polypcore/pdispatch.h>
+#include <polypcore/pstream.h>
+#include <polypcore/dynarray.h>
+#include <polypcore/socket-client.h>
+#include <polypcore/pstream-util.h>
+#include <polypcore/util.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+#include <polyp/client-conf.h>
+#include <polypcore/socket-util.h>
+
+#ifdef HAVE_X11
+#include "client-conf-x11.h"
+#endif
+
+#define AUTOSPAWN_LOCK "autospawn.lock"
+
+static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = {
+ [PA_COMMAND_REQUEST] = pa_command_request,
+ [PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed,
+ [PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed,
+ [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
+};
+
+static void unlock_autospawn_lock_file(pa_context *c) {
+ assert(c);
+
+ if (c->autospawn_lock_fd >= 0) {
+ char lf[PATH_MAX];
+ pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
+
+ pa_unlock_lockfile(lf, c->autospawn_lock_fd);
+ c->autospawn_lock_fd = -1;
+ }
+}
+
+pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
+ pa_context *c;
+ assert(mainloop && name);
+
+ c = pa_xmalloc(sizeof(pa_context));
+ c->ref = 1;
+ c->name = pa_xstrdup(name);
+ c->mainloop = mainloop;
+ c->client = NULL;
+ c->pstream = NULL;
+ c->pdispatch = NULL;
+ c->playback_streams = pa_dynarray_new();
+ c->record_streams = pa_dynarray_new();
+ assert(c->playback_streams && c->record_streams);
+
+ PA_LLIST_HEAD_INIT(pa_stream, c->streams);
+ PA_LLIST_HEAD_INIT(pa_operation, c->operations);
+
+ c->error = PA_ERROR_OK;
+ c->state = PA_CONTEXT_UNCONNECTED;
+ c->ctag = 0;
+
+ c->state_callback = NULL;
+ c->state_userdata = NULL;
+
+ c->subscribe_callback = NULL;
+ c->subscribe_userdata = NULL;
+
+ c->memblock_stat = pa_memblock_stat_new();
+ c->local = -1;
+ c->server_list = NULL;
+ c->server = NULL;
+ c->autospawn_lock_fd = -1;
+ memset(&c->spawn_api, 0, sizeof(c->spawn_api));
+ c->do_autospawn = 0;
+
+#ifdef SIGPIPE
+ pa_check_signal_is_blocked(SIGPIPE);
+#endif
+
+ c->conf = pa_client_conf_new();
+ pa_client_conf_load(c->conf, NULL);
+#ifdef HAVE_X11
+ pa_client_conf_from_x11(c->conf, NULL);
+#endif
+ pa_client_conf_env(c->conf);
+
+ return c;
+}
+
+static void context_free(pa_context *c) {
+ assert(c);
+
+ unlock_autospawn_lock_file(c);
+
+ while (c->operations)
+ pa_operation_cancel(c->operations);
+
+ while (c->streams)
+ pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
+
+ if (c->client)
+ pa_socket_client_unref(c->client);
+ if (c->pdispatch)
+ pa_pdispatch_unref(c->pdispatch);
+ if (c->pstream) {
+ pa_pstream_close(c->pstream);
+ pa_pstream_unref(c->pstream);
+ }
+
+ if (c->record_streams)
+ pa_dynarray_free(c->record_streams, NULL, NULL);
+ if (c->playback_streams)
+ pa_dynarray_free(c->playback_streams, NULL, NULL);
+
+ pa_memblock_stat_unref(c->memblock_stat);
+
+ if (c->conf)
+ pa_client_conf_free(c->conf);
+
+ pa_strlist_free(c->server_list);
+
+ pa_xfree(c->name);
+ pa_xfree(c->server);
+ pa_xfree(c);
+}
+
+pa_context* pa_context_ref(pa_context *c) {
+ assert(c && c->ref >= 1);
+ c->ref++;
+ return c;
+}
+
+void pa_context_unref(pa_context *c) {
+ assert(c && c->ref >= 1);
+
+ if ((--(c->ref)) == 0)
+ context_free(c);
+}
+
+void pa_context_set_state(pa_context *c, pa_context_state_t st) {
+ assert(c);
+
+ if (c->state == st)
+ return;
+
+ pa_context_ref(c);
+
+ if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) {
+ pa_stream *s;
+
+ s = c->streams ? pa_stream_ref(c->streams) : NULL;
+ while (s) {
+ pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
+ pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
+ pa_stream_unref(s);
+ s = n;
+ }
+
+ if (c->pdispatch)
+ pa_pdispatch_unref(c->pdispatch);
+ c->pdispatch = NULL;
+
+ if (c->pstream) {
+ pa_pstream_close(c->pstream);
+ pa_pstream_unref(c->pstream);
+ }
+ c->pstream = NULL;
+
+ if (c->client)
+ pa_socket_client_unref(c->client);
+ c->client = NULL;
+ }
+
+ c->state = st;
+ if (c->state_callback)
+ c->state_callback(c, c->state_userdata);
+
+ pa_context_unref(c);
+}
+
+void pa_context_fail(pa_context *c, int error) {
+ assert(c);
+ c->error = error;
+ pa_context_set_state(c, PA_CONTEXT_FAILED);
+}
+
+static void pstream_die_callback(pa_pstream *p, void *userdata) {
+ pa_context *c = userdata;
+ assert(p && c);
+ pa_context_fail(c, PA_ERROR_CONNECTIONTERMINATED);
+}
+
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) {
+ pa_context *c = userdata;
+ assert(p && packet && c);
+
+ pa_context_ref(c);
+
+ if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) {
+ pa_log(__FILE__": invalid packet.\n");
+ pa_context_fail(c, PA_ERROR_PROTOCOL);
+ }
+
+ pa_context_unref(c);
+}
+
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, PA_GCC_UNUSED uint32_t delta, const pa_memchunk *chunk, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ assert(p && chunk && c && chunk->memblock && chunk->memblock->data);
+
+ pa_context_ref(c);
+
+ if ((s = pa_dynarray_get(c->record_streams, channel))) {
+ pa_mcalign_push(s->mcalign, chunk);
+
+ for (;;) {
+ pa_memchunk t;
+
+ if (pa_mcalign_pop(s->mcalign, &t) < 0)
+ break;
+
+ if (s->read_callback) {
+ s->read_callback(s, (uint8_t*) t.memblock->data + t.index, t.length, s->read_userdata);
+ s->counter += chunk->length;
+ }
+
+ pa_memblock_unref(t.memblock);
+ }
+ }
+
+ pa_context_unref(c);
+}
+
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) {
+ assert(c);
+
+ if (command == PA_COMMAND_ERROR) {
+ assert(t);
+
+ if (pa_tagstruct_getu32(t, &c->error) < 0) {
+ pa_context_fail(c, PA_ERROR_PROTOCOL);
+ return -1;
+
+ }
+ } else if (command == PA_COMMAND_TIMEOUT)
+ c->error = PA_ERROR_TIMEOUT;
+ else {
+ pa_context_fail(c, PA_ERROR_PROTOCOL);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ assert(pd && c && (c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME));
+
+ pa_context_ref(c);
+
+ if (command != PA_COMMAND_REPLY) {
+
+ if (pa_context_handle_error(c, command, t) < 0)
+ pa_context_fail(c, PA_ERROR_PROTOCOL);
+
+ pa_context_fail(c, c->error);
+ goto finish;
+ }
+
+ switch(c->state) {
+ case PA_CONTEXT_AUTHORIZING: {
+ pa_tagstruct *reply;
+ reply = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
+ pa_tagstruct_putu32(reply, tag = c->ctag++);
+ pa_tagstruct_puts(reply, c->name);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c);
+
+ pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
+ break;
+ }
+
+ case PA_CONTEXT_SETTING_NAME :
+ pa_context_set_state(c, PA_CONTEXT_READY);
+ break;
+
+ default:
+ assert(0);
+ }
+
+finish:
+ pa_context_unref(c);
+}
+
+static void setup_context(pa_context *c, pa_iochannel *io) {
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && io);
+
+ pa_context_ref(c);
+
+ assert(!c->pstream);
+ c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat);
+ assert(c->pstream);
+
+ pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
+ pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
+ pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);
+
+ assert(!c->pdispatch);
+ c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
+ assert(c->pdispatch);
+
+ if (!c->conf->cookie_valid) {
+ pa_context_fail(c, PA_ERROR_AUTHKEY);
+ goto finish;
+ }
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie));
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c);
+
+ pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);
+
+finish:
+
+ pa_context_unref(c);
+}
+
+static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata);
+
+#ifndef OS_IS_WIN32
+
+static int context_connect_spawn(pa_context *c) {
+ pid_t pid;
+ int status, r;
+ int fds[2] = { -1, -1} ;
+ pa_iochannel *io;
+
+ pa_context_ref(c);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+ pa_log(__FILE__": socketpair() failed: %s\n", strerror(errno));
+ pa_context_fail(c, PA_ERROR_INTERNAL);
+ goto fail;
+ }
+
+ pa_fd_set_cloexec(fds[0], 1);
+
+ pa_socket_low_delay(fds[0]);
+ pa_socket_low_delay(fds[1]);
+
+ if (c->spawn_api.prefork)
+ c->spawn_api.prefork();
+
+ if ((pid = fork()) < 0) {
+ pa_log(__FILE__": fork() failed: %s\n", strerror(errno));
+ pa_context_fail(c, PA_ERROR_INTERNAL);
+
+ if (c->spawn_api.postfork)
+ c->spawn_api.postfork();
+
+ goto fail;
+ } else if (!pid) {
+ /* Child */
+
+ char t[128];
+ const char *state = NULL;
+#define MAX_ARGS 64
+ const char * argv[MAX_ARGS+1];
+ int n;
+
+ /* Not required, since fds[0] has CLOEXEC enabled anyway */
+ close(fds[0]);
+
+ if (c->spawn_api.atfork)
+ c->spawn_api.atfork();
+
+ /* Setup argv */
+
+ n = 0;
+
+ argv[n++] = c->conf->daemon_binary;
+ argv[n++] = "--daemonize=yes";
+
+ snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]);
+ argv[n++] = strdup(t);
+
+ while (n < MAX_ARGS) {
+ char *a;
+
+ if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
+ break;
+
+ argv[n++] = a;
+ }
+
+ argv[n++] = NULL;
+
+ execv(argv[0], (char * const *) argv);
+ _exit(1);
+#undef MAX_ARGS
+ }
+
+ /* Parent */
+
+ r = waitpid(pid, &status, 0);
+
+ if (c->spawn_api.postfork)
+ c->spawn_api.postfork();
+
+ if (r < 0) {
+ pa_log(__FILE__": waitpid() failed: %s\n", strerror(errno));
+ pa_context_fail(c, PA_ERROR_INTERNAL);
+ goto fail;
+ } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED);
+ goto fail;
+ }
+
+ close(fds[1]);
+
+ c->local = 1;
+
+ io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
+
+ setup_context(c, io);
+ unlock_autospawn_lock_file(c);
+
+ pa_context_unref(c);
+
+ return 0;
+
+fail:
+ if (fds[0] != -1)
+ close(fds[0]);
+ if (fds[1] != -1)
+ close(fds[1]);
+
+ unlock_autospawn_lock_file(c);
+
+ pa_context_unref(c);
+
+ return -1;
+}
+
+#endif /* OS_IS_WIN32 */
+
+static int try_next_connection(pa_context *c) {
+ char *u = NULL;
+ int r = -1;
+ assert(c && !c->client);
+
+ for (;;) {
+ if (u)
+ pa_xfree(u);
+ u = NULL;
+
+ c->server_list = pa_strlist_pop(c->server_list, &u);
+
+ if (!u) {
+
+#ifndef OS_IS_WIN32
+ if (c->do_autospawn) {
+ r = context_connect_spawn(c);
+ goto finish;
+ }
+#endif
+
+ pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED);
+ goto finish;
+ }
+
+ pa_log_debug(__FILE__": Trying to connect to %s...\n", u);
+
+ pa_xfree(c->server);
+ c->server = pa_xstrdup(u);
+
+ if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
+ continue;
+
+ c->local = pa_socket_client_is_local(c->client);
+ pa_socket_client_set_callback(c->client, on_connection, c);
+ break;
+ }
+
+ r = 0;
+
+finish:
+ if (u)
+ pa_xfree(u);
+
+ return r;
+}
+
+static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
+ pa_context *c = userdata;
+ assert(client && c && c->state == PA_CONTEXT_CONNECTING);
+
+ pa_context_ref(c);
+
+ pa_socket_client_unref(client);
+ c->client = NULL;
+
+ if (!io) {
+ /* Try the item in the list */
+ if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) {
+ try_next_connection(c);
+ goto finish;
+ }
+
+ pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED);
+ goto finish;
+ }
+
+ unlock_autospawn_lock_file(c);
+ setup_context(c, io);
+
+finish:
+ pa_context_unref(c);
+}
+
+int pa_context_connect(pa_context *c, const char *server, int spawn, const pa_spawn_api *api) {
+ int r = -1;
+ assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED);
+
+ if (!server)
+ server = c->conf->default_server;
+
+ pa_context_ref(c);
+
+ assert(!c->server_list);
+
+ if (server) {
+ if (!(c->server_list = pa_strlist_parse(server))) {
+ pa_context_fail(c, PA_ERROR_INVALIDSERVER);
+ goto finish;
+ }
+ } else {
+ char *d;
+ char ufn[PATH_MAX];
+
+ /* Prepend in reverse order */
+
+ if ((d = getenv("DISPLAY"))) {
+ char *e;
+ d = pa_xstrdup(d);
+ if ((e = strchr(d, ':')))
+ *e = 0;
+
+ if (*d)
+ c->server_list = pa_strlist_prepend(c->server_list, d);
+
+ pa_xfree(d);
+ }
+
+ c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost");
+ c->server_list = pa_strlist_prepend(c->server_list, "localhost");
+ c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn)));
+
+ /* Wrap the connection attempts in a single transaction for sane autospawn locking */
+ if (spawn && c->conf->autospawn) {
+ char lf[PATH_MAX];
+
+ pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
+ pa_make_secure_parent_dir(lf);
+ assert(c->autospawn_lock_fd <= 0);
+ c->autospawn_lock_fd = pa_lock_lockfile(lf);
+
+ if (api)
+ c->spawn_api = *api;
+ c->do_autospawn = 1;
+ }
+
+ }
+
+ pa_context_set_state(c, PA_CONTEXT_CONNECTING);
+ r = try_next_connection(c);
+
+finish:
+ pa_context_unref(c);
+
+ return r;
+}
+
+void pa_context_disconnect(pa_context *c) {
+ assert(c);
+ pa_context_set_state(c, PA_CONTEXT_TERMINATED);
+}
+
+pa_context_state_t pa_context_get_state(pa_context *c) {
+ assert(c && c->ref >= 1);
+ return c->state;
+}
+
+int pa_context_errno(pa_context *c) {
+ assert(c && c->ref >= 1);
+ return c->error;
+}
+
+void pa_context_set_state_callback(pa_context *c, void (*cb)(pa_context *c, void *userdata), void *userdata) {
+ assert(c && c->ref >= 1);
+ c->state_callback = cb;
+ c->state_userdata = userdata;
+}
+
+int pa_context_is_pending(pa_context *c) {
+ assert(c && c->ref >= 1);
+
+/* pa_log("pstream: %i\n", pa_pstream_is_pending(c->pstream)); */
+/* pa_log("pdispatch: %i\n", pa_pdispatch_is_pending(c->pdispatch)); */
+
+ return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
+ (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
+ c->client;
+}
+
+static void set_dispatch_callbacks(pa_operation *o);
+
+static void pdispatch_drain_callback(PA_GCC_UNUSED pa_pdispatch*pd, void *userdata) {
+ set_dispatch_callbacks(userdata);
+}
+
+static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata) {
+ set_dispatch_callbacks(userdata);
+}
+
+static void set_dispatch_callbacks(pa_operation *o) {
+ int done = 1;
+ assert(o && o->context && o->context->ref >= 1 && o->ref >= 1 && o->context->state == PA_CONTEXT_READY);
+
+ pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL);
+ pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL);
+
+ if (pa_pdispatch_is_pending(o->context->pdispatch)) {
+ pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o);
+ done = 0;
+ }
+
+ if (pa_pstream_is_pending(o->context->pstream)) {
+ pa_pstream_set_drain_callback(o->context->pstream, pstream_drain_callback, o);
+ done = 0;
+ }
+
+ if (!done)
+ pa_operation_ref(o);
+ else {
+ if (o->callback) {
+ void (*cb)(pa_context *c, void *userdata);
+ cb = (void (*)(pa_context*, void*)) o->callback;
+ cb(o->context, o->userdata);
+ }
+
+ pa_operation_done(o);
+ }
+
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_drain(pa_context *c, void (*cb) (pa_context*c, void *userdata), void *userdata) {
+ pa_operation *o;
+ assert(c && c->ref >= 1);
+
+ if (c->state != PA_CONTEXT_READY)
+ return NULL;
+
+ if (!pa_context_is_pending(c))
+ return NULL;
+
+ o = pa_operation_new(c, NULL);
+ assert(o);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ set_dispatch_callbacks(pa_operation_ref(o));
+
+ return o;
+}
+
+void pa_context_exit_daemon(pa_context *c) {
+ pa_tagstruct *t;
+ assert(c && c->ref >= 1);
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_EXIT);
+ pa_tagstruct_putu32(t, c->ctag++);
+ pa_pstream_send_tagstruct(c->pstream, t);
+}
+
+void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ success = 0;
+ } else if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *c, int _success, void *_userdata) = (void (*)(pa_context *c, int _success, void *_userdata)) o->callback;
+ cb(o->context, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, command);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SINK);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_set_default_source(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SOURCE);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+int pa_context_is_local(pa_context *c) {
+ assert(c);
+ return c->local;
+}
+
+pa_operation* pa_context_set_name(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && name && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_CLIENT_NAME);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+const char* pa_get_library_version(void) {
+ return PACKAGE_VERSION;
+}
+
+const char* pa_context_get_server(pa_context *c) {
+
+ if (!c->server)
+ return NULL;
+
+ if (*c->server == '{') {
+ char *e = strchr(c->server+1, '}');
+ return e ? e+1 : c->server;
+ }
+
+ return c->server;
+}
diff --git a/src/polyp/polyplib-context.h b/src/polyp/polyplib-context.h
new file mode 100644
index 00000000..febb75f4
--- /dev/null
+++ b/src/polyp/polyplib-context.h
@@ -0,0 +1,117 @@
+#ifndef foopolyplibcontexthfoo
+#define foopolyplibcontexthfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/sample.h>
+#include <polyp/polyplib-def.h>
+#include <polyp/mainloop-api.h>
+#include <polyp/cdecl.h>
+#include <polyp/polyplib-operation.h>
+
+/** \file
+ * Connection contexts for asynchrononous communication with a
+ * server. A pa_context object wraps a connection to a polypaudio
+ * server using its native protocol. A context may be used to issue
+ * commands on the server or to create playback or recording
+ * streams. Multiple playback streams may be piped through a single
+ * connection context. Operations on the contect involving
+ * communication with the server are executed asynchronously: i.e. the
+ * client function do not implicitely wait for completion of the
+ * operation on the server. Instead the caller specifies a call back
+ * function that is called when the operation is completed. Currently
+ * running operations may be canceled using pa_operation_cancel(). */
+
+/** \example pacat.c
+ * A playback and recording tool using the asynchronous API */
+
+/** \example paplay.c
+ * A sound file playback tool using the asynchronous API, based on libsndfile */
+
+PA_C_DECL_BEGIN
+
+/** \pa_context
+ * An opaque connection context to a daemon */
+typedef struct pa_context pa_context;
+
+/** Instantiate a new connection context with an abstract mainloop API
+ * and an application name */
+pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
+
+/** Decrease the reference counter of the context by one */
+void pa_context_unref(pa_context *c);
+
+/** Increase the reference counter of the context by one */
+pa_context* pa_context_ref(pa_context *c);
+
+typedef void (*pa_context_state_callback)(pa_context *c, void *userdata);
+
+/** Set a callback function that is called whenever the context status changes */
+void pa_context_set_state_callback(pa_context *c, pa_context_state_callback callback, void *userdata);
+
+/** Return the error number of the last failed operation */
+int pa_context_errno(pa_context *c);
+
+/** Return non-zero if some data is pending to be written to the connection */
+int pa_context_is_pending(pa_context *c);
+
+/** Return the current context status */
+pa_context_state_t pa_context_get_state(pa_context *c);
+
+/** Connect the context to the specified server. If server is NULL,
+connect to the default server. This routine may but will not always
+return synchronously on error. Use pa_context_set_state_callback() to
+be notified when the connection is established. If spawn is non-zero
+and no specific server is specified or accessible a new daemon is
+spawned. If api is non-NULL, the functions specified in the structure
+are used when forking a new child process. */
+int pa_context_connect(pa_context *c, const char *server, int spawn, const pa_spawn_api *api);
+
+/** Terminate the context connection immediately */
+void pa_context_disconnect(pa_context *c);
+
+/** Drain the context. If there is nothing to drain, the function returns NULL */
+pa_operation* pa_context_drain(pa_context *c, void (*cb) (pa_context*c, void *userdata), void *userdata);
+
+/** Tell the daemon to exit. No operation object is returned as the
+ * connection is terminated when the daemon quits, thus this operation
+ * would never complete. */
+void pa_context_exit_daemon(pa_context *c);
+
+/** Set the name of the default sink. \since 0.4 */
+pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata);
+
+/** Set the name of the default source. \since 0.4 */
+pa_operation* pa_context_set_default_source(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata);
+
+/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */
+int pa_context_is_local(pa_context *c);
+
+/** Set a different application name for context on the server. \since 0.5 */
+pa_operation* pa_context_set_name(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata);
+
+/** Return the server name this context is connected to. \since 0.7 */
+const char* pa_context_get_server(pa_context *c);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-def.h b/src/polyp/polyplib-def.h
new file mode 100644
index 00000000..0591ce6c
--- /dev/null
+++ b/src/polyp/polyplib-def.h
@@ -0,0 +1,213 @@
+#ifndef foopolyplibdefhfoo
+#define foopolyplibdefhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <polyp/cdecl.h>
+#include <polyp/sample.h>
+
+/** \file
+ * Global definitions */
+
+PA_C_DECL_BEGIN
+
+/** The state of a connection context */
+typedef enum pa_context_state {
+ PA_CONTEXT_UNCONNECTED, /**< The context hasn't been connected yet */
+ PA_CONTEXT_CONNECTING, /**< A connection is being established */
+ PA_CONTEXT_AUTHORIZING, /**< The client is authorizing itself to the daemon */
+ PA_CONTEXT_SETTING_NAME, /**< The client is passing its application name to the daemon */
+ PA_CONTEXT_READY, /**< The connection is established, the context is ready to execute operations */
+ PA_CONTEXT_FAILED, /**< The connection failed or was disconnected */
+ PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */
+} pa_context_state_t;
+
+/** The state of a stream */
+typedef enum pa_stream_state {
+ PA_STREAM_DISCONNECTED, /**< The stream is not yet connected to any sink or source */
+ PA_STREAM_CREATING, /**< The stream is being created */
+ PA_STREAM_READY, /**< The stream is established, you may pass audio data to it now */
+ PA_STREAM_FAILED, /**< An error occured that made the stream invalid */
+ PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */
+} pa_stream_state_t;
+
+/** The state of an operation */
+typedef enum pa_operation_state {
+ PA_OPERATION_RUNNING, /**< The operation is still running */
+ PA_OPERATION_DONE, /**< The operation has been completed */
+ PA_OPERATION_CANCELED /**< The operation has been canceled */
+} pa_operation_state_t;
+
+/** An invalid index */
+#define PA_INVALID_INDEX ((uint32_t) -1)
+
+/** The direction of a pa_stream object */
+typedef enum pa_stream_direction {
+ PA_STREAM_NODIRECTION, /**< Invalid direction */
+ PA_STREAM_PLAYBACK, /**< Playback stream */
+ PA_STREAM_RECORD, /**< Record stream */
+ PA_STREAM_UPLOAD /**< Sample upload stream */
+} pa_stream_direction_t;
+
+/** Some special flags for stream connections. \since 0.6 */
+typedef enum pa_stream_flags {
+ PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */
+ PA_STREAM_INTERPOLATE_LATENCY = 2 /**< Interpolate the latency for
+ * this stream. When enabled,
+ * you can use
+ * pa_stream_interpolated_xxx()
+ * for synchronization. Using
+ * these functions instead of
+ * pa_stream_get_latency() has
+ * the advantage of not
+ * requiring a whole roundtrip
+ * for responses. Consider using
+ * this option when frequently
+ * requesting latency
+ * information. This is
+ * especially useful on long latency
+ * network connections. */
+} pa_stream_flags_t;
+
+/** Playback and record buffer metrics */
+typedef struct pa_buffer_attr {
+ uint32_t maxlength; /**< Maximum length of the buffer */
+ uint32_t tlength; /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */
+ uint32_t prebuf; /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */
+ uint32_t minreq; /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */
+ uint32_t fragsize; /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */
+} pa_buffer_attr;
+
+/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */
+enum {
+ PA_ERROR_OK, /**< No error */
+ PA_ERROR_ACCESS, /**< Access failure */
+ PA_ERROR_COMMAND, /**< Unknown command */
+ PA_ERROR_INVALID, /**< Invalid argument */
+ PA_ERROR_EXIST, /**< Entity exists */
+ PA_ERROR_NOENTITY, /**< No such entity */
+ PA_ERROR_CONNECTIONREFUSED, /**< Connection refused */
+ PA_ERROR_PROTOCOL, /**< Protocol error */
+ PA_ERROR_TIMEOUT, /**< Timeout */
+ PA_ERROR_AUTHKEY, /**< No authorization key */
+ PA_ERROR_INTERNAL, /**< Internal error */
+ PA_ERROR_CONNECTIONTERMINATED, /**< Connection terminated */
+ PA_ERROR_KILLED, /**< Entity killed */
+ PA_ERROR_INVALIDSERVER, /**< Invalid server */
+ PA_ERROR_INITFAILED, /**< Module initialization failed */
+ PA_ERROR_MAX /**< Not really an error but the first invalid error code */
+};
+
+/** Subscription event mask, as used by pa_context_subscribe() */
+typedef enum pa_subscription_mask {
+ PA_SUBSCRIPTION_MASK_NULL = 0, /**< No events */
+ PA_SUBSCRIPTION_MASK_SINK = 1, /**< Sink events */
+ PA_SUBSCRIPTION_MASK_SOURCE = 2, /**< Source events */
+ PA_SUBSCRIPTION_MASK_SINK_INPUT = 4, /**< Sink input events */
+ PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 8, /**< Source output events */
+ PA_SUBSCRIPTION_MASK_MODULE = 16, /**< Module events */
+ PA_SUBSCRIPTION_MASK_CLIENT = 32, /**< Client events */
+ PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64, /**< Sample cache events */
+ PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. \since 0.4 */
+ PA_SUBSCRIPTION_MASK_AUTOLOAD = 256 /**< Autoload table events. \since 0.5 */
+} pa_subscription_mask_t;
+
+/** Subscription event types, as used by pa_context_subscribe() */
+typedef enum pa_subscription_event_type {
+ PA_SUBSCRIPTION_EVENT_SINK = 0, /**< Event type: Sink */
+ PA_SUBSCRIPTION_EVENT_SOURCE = 1, /**< Event type: Source */
+ PA_SUBSCRIPTION_EVENT_SINK_INPUT = 2, /**< Event type: Sink input */
+ PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 3, /**< Event type: Source output */
+ PA_SUBSCRIPTION_EVENT_MODULE = 4, /**< Event type: Module */
+ PA_SUBSCRIPTION_EVENT_CLIENT = 5, /**< Event type: Client */
+ PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6, /**< Event type: Sample cache item */
+ PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4 */
+ PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. \since 0.5 */
+ PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */
+
+ PA_SUBSCRIPTION_EVENT_NEW = 0, /**< A new object was created */
+ PA_SUBSCRIPTION_EVENT_CHANGE = 16, /**< A property of the object was modified */
+ PA_SUBSCRIPTION_EVENT_REMOVE = 32, /**< An object was removed */
+ PA_SUBSCRIPTION_EVENT_TYPE_MASK = 16+32 /**< A mask to extract the event operation from an event value */
+} pa_subscription_event_type_t;
+
+/** Return one if an event type t matches an event mask bitfield */
+#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))))
+
+/** A structure for latency info. See pa_stream_get_latency(). The
+ * total output latency a sample that is written with
+ * pa_stream_write() takes to be played may be estimated by
+ * sink_usec+buffer_usec+transport_usec. The output buffer to which
+ * buffer_usec relates may be manipulated freely (with
+ * pa_stream_write()'s delta argument, pa_stream_flush() and friends),
+ * the buffers sink_usec/source_usec relates to is a first-in
+ * first-out buffer which cannot be flushed or manipulated in any
+ * way. The total input latency a sample that is recorded takes to be
+ * delivered to the application is:
+ * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of
+ * sign issues!) When connected to a monitor source sink_usec contains
+ * the latency of the owning sink.*/
+typedef struct pa_latency_info {
+ pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play. For both playback and record streams. */
+ pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */
+ pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/
+ pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */
+ int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */
+ uint32_t queue_length; /**< Queue size in bytes. For both playback and record streams. */
+ int synchronized_clocks; /**< Non-zero if the local and the
+ * remote machine have synchronized
+ * clocks. If synchronized clocks are
+ * detected transport_usec becomes much
+ * more reliable. However, the code that
+ * detects synchronized clocks is very
+ * limited und unreliable itself. \since
+ * 0.5 */
+ struct timeval timestamp; /**< The time when this latency info was current */
+ uint64_t counter; /**< The byte counter current when the latency info was requested. \since 0.6 */
+} pa_latency_info;
+
+/** A structure for the spawn api. This may be used to integrate auto
+ * spawned daemons into your application. For more information see
+ * pa_context_connect(). When spawning a new child process the
+ * waitpid() is used on the child's PID. The spawn routine will not
+ * block or ignore SIGCHLD signals, since this cannot be done in a
+ * thread compatible way. You might have to do this in
+ * prefork/postfork. \since 0.4 */
+typedef struct pa_spawn_api {
+ void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */
+ void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/
+ void (*atfork)(void); /**< Is called immediately after the
+ * fork in the child process. May be
+ * NULL. It is not safe to close all
+ * file descriptors in this function
+ * unconditionally, since a UNIX socket
+ * (created using socketpair()) is
+ * passed to the new process. */
+} pa_spawn_api;
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-error.c b/src/polyp/polyplib-error.c
new file mode 100644
index 00000000..188d6a93
--- /dev/null
+++ b/src/polyp/polyplib-error.c
@@ -0,0 +1,54 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <stdlib.h>
+
+#include "polyplib-error.h"
+#include <polypcore/native-common.h>
+
+static const char* const errortab[PA_ERROR_MAX] = {
+ [PA_ERROR_OK] = "OK",
+ [PA_ERROR_ACCESS] = "Access denied",
+ [PA_ERROR_COMMAND] = "Unknown command",
+ [PA_ERROR_INVALID] = "Invalid argument",
+ [PA_ERROR_EXIST] = "Entity exists",
+ [PA_ERROR_NOENTITY] = "No such entity",
+ [PA_ERROR_CONNECTIONREFUSED] = "Connection refused",
+ [PA_ERROR_PROTOCOL] = "Protocol error",
+ [PA_ERROR_TIMEOUT] = "Timeout",
+ [PA_ERROR_AUTHKEY] = "No authorization key",
+ [PA_ERROR_INTERNAL] = "Internal error",
+ [PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated",
+ [PA_ERROR_KILLED] = "Entity killed",
+ [PA_ERROR_INVALIDSERVER] = "Invalid server",
+};
+
+const char*pa_strerror(uint32_t error) {
+ if (error >= PA_ERROR_MAX)
+ return NULL;
+
+ return errortab[error];
+}
diff --git a/src/polyp/polyplib-error.h b/src/polyp/polyplib-error.h
new file mode 100644
index 00000000..1bb97822
--- /dev/null
+++ b/src/polyp/polyplib-error.h
@@ -0,0 +1,38 @@
+#ifndef foopolypliberrorhfoo
+#define foopolypliberrorhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <polyp/cdecl.h>
+
+/** \file
+ * Error management */
+
+PA_C_DECL_BEGIN
+
+/** Return a human readable error message for the specified numeric error code */
+const char* pa_strerror(uint32_t error);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-internal.h b/src/polyp/polyplib-internal.h
new file mode 100644
index 00000000..b95a20f3
--- /dev/null
+++ b/src/polyp/polyplib-internal.h
@@ -0,0 +1,154 @@
+#ifndef foopolyplibinternalhfoo
+#define foopolyplibinternalhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/mainloop-api.h>
+#include <polypcore/socket-client.h>
+#include <polypcore/pstream.h>
+#include <polypcore/pdispatch.h>
+#include <polypcore/dynarray.h>
+
+#include "polyplib-context.h"
+#include "polyplib-stream.h"
+#include "polyplib-operation.h"
+#include <polypcore/llist.h>
+#include <polypcore/native-common.h>
+#include <polyp/client-conf.h>
+#include <polypcore/strlist.h>
+#include <polypcore/mcalign.h>
+
+#define DEFAULT_TIMEOUT (10)
+
+struct pa_context {
+ int ref;
+
+ char *name;
+ pa_mainloop_api* mainloop;
+
+ pa_socket_client *client;
+ pa_pstream *pstream;
+ pa_pdispatch *pdispatch;
+
+ pa_dynarray *record_streams, *playback_streams;
+ PA_LLIST_HEAD(pa_stream, streams);
+ PA_LLIST_HEAD(pa_operation, operations);
+
+ uint32_t ctag;
+ uint32_t error;
+ pa_context_state_t state;
+
+ void (*state_callback)(pa_context*c, void *userdata);
+ void *state_userdata;
+
+ void (*subscribe_callback)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
+ void *subscribe_userdata;
+
+ pa_memblock_stat *memblock_stat;
+
+ int local;
+ int do_autospawn;
+ int autospawn_lock_fd;
+ pa_spawn_api spawn_api;
+
+ pa_strlist *server_list;
+
+ char *server;
+
+ pa_client_conf *conf;
+};
+
+struct pa_stream {
+ int ref;
+ pa_context *context;
+ pa_mainloop_api *mainloop;
+ PA_LLIST_FIELDS(pa_stream);
+
+ char *name;
+ pa_buffer_attr buffer_attr;
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+ uint32_t channel;
+ int channel_valid;
+ uint32_t device_index;
+ pa_stream_direction_t direction;
+ uint32_t requested_bytes;
+ uint64_t counter;
+ pa_usec_t previous_time;
+ pa_usec_t previous_ipol_time;
+ pa_stream_state_t state;
+ pa_mcalign *mcalign;
+
+ int interpolate;
+ int corked;
+
+ uint32_t ipol_usec;
+ struct timeval ipol_timestamp;
+ pa_time_event *ipol_event;
+ int ipol_requested;
+
+ void (*state_callback)(pa_stream*c, void *userdata);
+ void *state_userdata;
+
+ void (*read_callback)(pa_stream *p, const void*data, size_t length, void *userdata);
+ void *read_userdata;
+
+ void (*write_callback)(pa_stream *p, size_t length, void *userdata);
+ void *write_userdata;
+};
+
+typedef void (*pa_operation_callback)(void);
+
+struct pa_operation {
+ int ref;
+ pa_context *context;
+ pa_stream *stream;
+ PA_LLIST_FIELDS(pa_operation);
+
+ pa_operation_state_t state;
+ void *userdata;
+ pa_operation_callback callback;
+};
+
+void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+pa_operation *pa_operation_new(pa_context *c, pa_stream *s);
+void pa_operation_done(pa_operation *o);
+
+void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+void pa_context_fail(pa_context *c, int error);
+void pa_context_set_state(pa_context *c, pa_context_state_t st);
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t);
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
+
+void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
+
+void pa_stream_trash_ipol(pa_stream *s);
+
+
+#endif
diff --git a/src/polyp/polyplib-introspect.c b/src/polyp/polyplib-introspect.c
new file mode 100644
index 00000000..0bdffa35
--- /dev/null
+++ b/src/polyp/polyplib-introspect.c
@@ -0,0 +1,1003 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+
+#include "polyplib-introspect.h"
+#include "polyplib-context.h"
+#include "polyplib-internal.h"
+#include <polypcore/pstream-util.h>
+#include <polypcore/gccmacro.h>
+
+/*** Statistics ***/
+
+static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ pa_stat_info i, *p = &i;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ p = NULL;
+ } else if (pa_tagstruct_getu32(t, &i.memblock_total) < 0 ||
+ pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 ||
+ pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 ||
+ pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 ||
+ pa_tagstruct_getu32(t, &i.scache_size) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_stat_info*_i, void *_userdata) = (void (*)(pa_context *s, const pa_stat_info*_i, void *_userdata)) o->callback;
+ cb(o->context, p, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_stat(pa_context *c, void (*cb)(pa_context *c, const pa_stat_info*i, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, (pa_operation_callback) cb, userdata);
+}
+
+/*** Server Info ***/
+
+static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ pa_server_info i, *p = &i;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ p = NULL;
+ } else if (pa_tagstruct_gets(t, &i.server_name) < 0 ||
+ pa_tagstruct_gets(t, &i.server_version) < 0 ||
+ pa_tagstruct_gets(t, &i.user_name) < 0 ||
+ pa_tagstruct_gets(t, &i.host_name) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_gets(t, &i.default_sink_name) < 0 ||
+ pa_tagstruct_gets(t, &i.default_source_name) < 0 ||
+ pa_tagstruct_getu32(t, &i.cookie) < 0 ||
+ !pa_tagstruct_eof(t)) {
+
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_server_info*_i, void *_userdata) = (void (*)(pa_context *s, const pa_server_info*_i, void *_userdata)) o->callback;
+ cb(o->context, p, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_server_info(pa_context *c, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SERVER_INFO, context_get_server_info_callback, (pa_operation_callback) cb, userdata);
+}
+
+/*** Sink Info ***/
+
+static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eof = 1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eof = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_sink_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_gets(t, &i.description) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+ pa_tagstruct_getu32(t, &i.monitor_source) < 0 ||
+ pa_tagstruct_gets(t, &i.monitor_source_name) < 0 ||
+ pa_tagstruct_get_usec(t, &i.latency) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0) {
+
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, NULL, eof, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INFO_LIST, context_get_sink_info_callback, (pa_operation_callback) cb, userdata);
+}
+
+pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+/*** Source info ***/
+
+static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eof = 1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eof = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_source_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_gets(t, &i.description) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 ||
+ pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 ||
+ pa_tagstruct_get_usec(t, &i.latency) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0) {
+
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, NULL, eof, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_INFO_LIST, context_get_source_info_callback, (pa_operation_callback) cb, userdata);
+}
+
+pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+/*** Client info ***/
+
+static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eof = 1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eof = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_client_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0 ) {
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, NULL, eof, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_CLIENT_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_client_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_callback) cb, userdata);
+}
+
+/*** Module info ***/
+
+static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eof = 1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eof = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_module_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_gets(t, &i.argument) < 0 ||
+ pa_tagstruct_getu32(t, &i.n_used) < 0 ||
+ pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) {
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, NULL, eof, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_MODULE_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_module_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_MODULE_INFO_LIST, context_get_module_info_callback, (pa_operation_callback) cb, userdata);
+}
+
+/*** Sink input info ***/
+
+static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eof = 1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eof = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_sink_input_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_getu32(t, &i.client) < 0 ||
+ pa_tagstruct_getu32(t, &i.sink) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+ pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+ pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
+ pa_tagstruct_gets(t, &i.resample_method) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0) {
+
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, NULL, eof, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_input_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INPUT_INFO_LIST, context_get_sink_input_info_callback, (pa_operation_callback) cb, userdata);
+}
+
+/*** Source output info ***/
+
+static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eof = 1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eof = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_source_output_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+ pa_tagstruct_getu32(t, &i.client) < 0 ||
+ pa_tagstruct_getu32(t, &i.source) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+ pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
+ pa_tagstruct_gets(t, &i.resample_method) < 0 ||
+ pa_tagstruct_gets(t, &i.driver) < 0) {
+
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata))o->callback;
+ cb(o->context, NULL, eof, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_OUTPUT_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_output_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, context_get_source_output_info_callback, (pa_operation_callback) cb, userdata);
+}
+
+/*** Volume manipulation ***/
+
+pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && idx != PA_INVALID_INDEX);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_tagstruct_put_cvolume(t, volume);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && name);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_put_cvolume(t, volume);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && idx != PA_INVALID_INDEX);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_put_cvolume(t, volume);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+/** Sample Cache **/
+
+static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eof = 1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eof = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_sample_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+ pa_tagstruct_get_usec(t, &i.duration) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+ pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+ pa_tagstruct_getu32(t, &i.bytes) < 0 ||
+ pa_tagstruct_get_boolean(t, &i.lazy) < 0 ||
+ pa_tagstruct_gets(t, &i.filename) < 0) {
+
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, NULL, eof, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb && name);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_tagstruct_puts(t, NULL);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_callback) cb, userdata);
+}
+
+static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && idx != PA_INVALID_INDEX);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, command);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ return command_kill(c, PA_COMMAND_KILL_CLIENT, idx, cb, userdata);
+}
+
+pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ return command_kill(c, PA_COMMAND_KILL_SINK_INPUT, idx, cb, userdata);
+}
+
+pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, idx, cb, userdata);
+}
+
+static void load_module_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ uint32_t idx = -1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ } else if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *c, uint32_t _idx, void *_userdata) = (void (*)(pa_context *c, uint32_t _idx, void *_userdata)) o->callback;
+ cb(o->context, idx, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && name && argument);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_LOAD_MODULE);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_puts(t, argument);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, load_module_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ return command_kill(c, PA_COMMAND_UNLOAD_MODULE, idx, cb, userdata);
+}
+
+/*** Autoload stuff ***/
+
+static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int eof = 1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ eof = -1;
+ } else {
+
+ while (!pa_tagstruct_eof(t)) {
+ pa_autoload_info i;
+
+ if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+ pa_tagstruct_gets(t, &i.name) < 0 ||
+ pa_tagstruct_getu32(t, &i.type) < 0 ||
+ pa_tagstruct_gets(t, &i.module) < 0 ||
+ pa_tagstruct_gets(t, &i.argument) < 0) {
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, &i, 0, o->userdata);
+ }
+ }
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata)) o->callback;
+ cb(o->context, NULL, eof, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb && name);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_putu32(t, type);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(c && cb && idx != PA_INVALID_INDEX);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) {
+ return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_callback) cb, userdata);
+}
+
+static void context_add_autoload_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ uint32_t idx;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ idx = PA_INVALID_INDEX;
+ } else if (pa_tagstruct_getu32(t, &idx) ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_context *s, uint32_t _idx, void *_userdata) = (void (*)(pa_context *s, uint32_t _idx, void *_userdata)) o->callback;
+ cb(o->context, idx, o->userdata);
+ }
+
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && name && module && argument);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_ADD_AUTOLOAD);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_putu32(t, type);
+ pa_tagstruct_puts(t, module);
+ pa_tagstruct_puts(t, argument);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_add_autoload_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && name);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_putu32(t, type);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && idx != PA_INVALID_INDEX);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
diff --git a/src/polyp/polyplib-introspect.h b/src/polyp/polyplib-introspect.h
new file mode 100644
index 00000000..d3489908
--- /dev/null
+++ b/src/polyp/polyplib-introspect.h
@@ -0,0 +1,279 @@
+#ifndef foopolyplibintrospecthfoo
+#define foopolyplibintrospecthfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+#include <polyp/polyplib-operation.h>
+#include <polyp/polyplib-context.h>
+#include <polyp/cdecl.h>
+#include <polyp/channelmap.h>
+#include <polyp/volume.h>
+
+/** \file
+ *
+ * Routines for daemon introspection. When enumerating all entitites
+ * of a certain kind, use the pa_context_xxx_list() functions. The
+ * specified callback function is called once for each entry. The
+ * enumeration is finished by a call to the callback function with
+ * is_last=1 and i=NULL. Strings referenced in pa_xxx_info structures
+ * and the structures themselves point to internal memory that may not
+ * be modified. That memory is only valid during the call to the
+ * callback function. A deep copy is required if you need this data
+ * outside the callback functions. An error is signalled by a call to * the callback function with i=NULL and is_last=0.
+ *
+ * When using the routines that ask fo a single entry only, a callback
+ * with the same signature is used. However, no finishing call to the
+ * routine is issued. */
+
+PA_C_DECL_BEGIN
+
+/** Stores information about sinks */
+typedef struct pa_sink_info {
+ const char *name; /**< Name of the sink */
+ uint32_t index; /**< Index of the sink */
+ const char *description; /**< Description of this sink */
+ pa_sample_spec sample_spec; /**< Sample spec of this sink */
+ pa_channel_map channel_map; /**< Channel map \since 0.9 */
+ uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */
+ pa_cvolume volume; /**< Volume of the sink */
+ uint32_t monitor_source; /**< Index of the monitor source connected to this sink */
+ const char *monitor_source_name; /**< The name of the monitor source */
+ pa_usec_t latency; /**< Length of filled playback buffer of this sink */
+ const char *driver; /**< Driver name. \since 0.9 */
+} pa_sink_info;
+
+/** Get information about a sink by its name */
+pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata);
+
+/** Get information about a sink by its index */
+pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata);
+
+/** Get the complete sink list */
+pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata);
+
+/** Stores information about sources */
+typedef struct pa_source_info {
+ const char *name ; /**< Name of the source */
+ uint32_t index; /**< Index of the source */
+ const char *description; /**< Description of this source */
+ pa_sample_spec sample_spec; /**< Sample spec of this source */
+ pa_channel_map channel_map; /**< Channel map \since 0.9 */
+ uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */
+ uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */
+ const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */
+ pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */
+ const char *driver; /**< Driver name \since 0.9 */
+} pa_source_info;
+
+/** Get information about a source by its name */
+pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata);
+
+/** Get information about a source by its index */
+pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata);
+
+/** Get the complete source list */
+pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata);
+
+/** Server information */
+typedef struct pa_server_info {
+ const char *user_name; /**< User name of the daemon process */
+ const char *host_name; /**< Host name the daemon is running on */
+ const char *server_version; /**< Version string of the daemon */
+ const char *server_name; /**< Server package name (usually "polypaudio") */
+ pa_sample_spec sample_spec; /**< Default sample specification */
+ const char *default_sink_name; /**< Name of default sink. \since 0.4 */
+ const char *default_source_name; /**< Name of default sink. \since 0.4*/
+ uint32_t cookie; /**< A random cookie for identifying this instance of polypaudio. \since 0.8 */
+} pa_server_info;
+
+/** Get some information about the server */
+pa_operation* pa_context_get_server_info(pa_context *c, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), void *userdata);
+
+/** Stores information about modules */
+typedef struct pa_module_info {
+ uint32_t index; /**< Index of the module */
+ const char*name, /**< Name of the module */
+ *argument; /**< Argument string of the module */
+ uint32_t n_used; /**< Usage counter or PA_INVALID_INDEX */
+ int auto_unload; /**< Non-zero if this is an autoloaded module */
+} pa_module_info;
+
+/** Get some information about a module by its index */
+pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata);
+
+/** Get the complete list of currently loaded modules */
+pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata);
+
+/** Stores information about clients */
+typedef struct pa_client_info {
+ uint32_t index; /**< Index of this client */
+ const char *name; /**< Name of this client */
+ uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */
+ const char *driver; /**< Driver name \since 0.9 */
+} pa_client_info;
+
+/** Get information about a client by its index */
+pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata);
+
+/** Get the complete client list */
+pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata);
+
+/** Stores information about sink inputs */
+typedef struct pa_sink_input_info {
+ uint32_t index; /**< Index of the sink input */
+ const char *name; /**< Name of the sink input */
+ uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
+ uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
+ uint32_t sink; /**< Index of the connected sink */
+ pa_sample_spec sample_spec; /**< The sample specification of the sink input */
+ pa_channel_map channel_map; /**< Channel map */
+ pa_cvolume volume; /**< The volume of this sink input */
+ pa_usec_t buffer_usec; /**< Latency due to buffering in sink input, see pa_latency_info for details */
+ pa_usec_t sink_usec; /**< Latency of the sink device, see pa_latency_info for details */
+ const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */
+ const char *driver; /**< Driver name \since 0.9 */
+} pa_sink_input_info;
+
+/** Get some information about a sink input by its index */
+pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata);
+
+/** Get the complete sink input list */
+pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata);
+
+/** Stores information about source outputs */
+typedef struct pa_source_output_info {
+ uint32_t index; /**< Index of the sink input */
+ const char *name; /**< Name of the sink input */
+ uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
+ uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
+ uint32_t source; /**< Index of the connected source */
+ pa_sample_spec sample_spec; /**< The sample specification of the source output */
+ pa_channel_map channel_map; /**< Channel map */
+ pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */
+ pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */
+ const char *resample_method; /**< Thre resampling method used by this source output. \since 0.7 */
+ const char *driver; /**< Driver name \since 0.9 */
+} pa_source_output_info;
+
+/** Get information about a source output by its index */
+pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata);
+
+/** Get the complete list of source outputs */
+pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata);
+
+/** Set the volume of a sink device specified by its index */
+pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
+
+/** Set the volume of a sink device specified by its name */
+pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
+
+/** Set the volume of a sink input stream */
+pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
+
+/** Memory block statistics */
+typedef struct pa_stat_info {
+ uint32_t memblock_total; /**< Currently allocated memory blocks */
+ uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */
+ uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */
+ uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */
+ uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */
+} pa_stat_info;
+
+/** Get daemon memory block statistics */
+pa_operation* pa_context_stat(pa_context *c, void (*cb)(pa_context *c, const pa_stat_info *i, void *userdata), void *userdata);
+
+/** Stores information about sample cache entries */
+typedef struct pa_sample_info {
+ uint32_t index; /**< Index of this entry */
+ const char *name; /**< Name of this entry */
+ pa_cvolume volume; /**< Default volume of this entry */
+ pa_sample_spec sample_spec; /**< Sample specification of the sample */
+ pa_channel_map channel_map; /**< The channel map */
+ pa_usec_t duration; /**< Duration of this entry */
+ uint32_t bytes; /**< Length of this sample in bytes. \since 0.4 */
+ int lazy; /**< Non-zero when this is a lazy cache entry. \since 0.5 */
+ const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */
+} pa_sample_info;
+
+/** Get information about a sample by its name */
+pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata);
+
+/** Get information about a sample by its index */
+pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata);
+
+/** Get the complete list of samples stored in the daemon. */
+pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata);
+
+/** Kill a client. \since 0.5 */
+pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
+
+/** Kill a sink input. \since 0.5 */
+pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
+
+/** Kill a source output. \since 0.5 */
+pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
+
+/** Load a module. \since 0.5 */
+pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata);
+
+/** Unload a module. \since 0.5 */
+pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
+
+/** Type of an autoload entry. \since 0.5 */
+typedef enum pa_autoload_type {
+ PA_AUTOLOAD_SINK = 0,
+ PA_AUTOLOAD_SOURCE = 1
+} pa_autoload_type_t;
+
+/** Stores information about autoload entries. \since 0.5 */
+typedef struct pa_autoload_info {
+ uint32_t index; /**< Index of this autoload entry */
+ const char *name; /**< Name of the sink or source */
+ pa_autoload_type_t type; /**< Type of the autoload entry */
+ const char *module; /**< Module name to load */
+ const char *argument; /**< Argument string for module */
+} pa_autoload_info;
+
+/** Get info about a specific autoload entry. \since 0.6 */
+pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata);
+
+/** Get info about a specific autoload entry. \since 0.6 */
+pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata);
+
+/** Get the complete list of autoload entries. \since 0.5 */
+pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata);
+
+/** Add a new autoload entry. \since 0.5 */
+pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, void (*cb)(pa_context *c, int idx, void *userdata), void* userdata);
+
+/** Remove an autoload entry. \since 0.6 */
+pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, int success, void *userdata), void* userdata);
+
+/** Remove an autoload entry. \since 0.6 */
+pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata);
+
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-operation.c b/src/polyp/polyplib-operation.c
new file mode 100644
index 00000000..ea336c17
--- /dev/null
+++ b/src/polyp/polyplib-operation.c
@@ -0,0 +1,103 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+
+#include <polypcore/xmalloc.h>
+#include "polyplib-internal.h"
+#include "polyplib-operation.h"
+
+pa_operation *pa_operation_new(pa_context *c, pa_stream *s) {
+ pa_operation *o;
+ assert(c);
+
+ o = pa_xmalloc(sizeof(pa_operation));
+ o->ref = 1;
+ o->context = pa_context_ref(c);
+ o->stream = s ? pa_stream_ref(s) : NULL;
+
+ o->state = PA_OPERATION_RUNNING;
+ o->userdata = NULL;
+ o->callback = NULL;
+
+ PA_LLIST_PREPEND(pa_operation, o->context->operations, o);
+ return pa_operation_ref(o);
+}
+
+pa_operation *pa_operation_ref(pa_operation *o) {
+ assert(o && o->ref >= 1);
+ o->ref++;
+ return o;
+}
+
+void pa_operation_unref(pa_operation *o) {
+ assert(o && o->ref >= 1);
+
+ if ((--(o->ref)) == 0) {
+ assert(!o->context);
+ assert(!o->stream);
+ free(o);
+ }
+}
+
+static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
+ assert(o && o->ref >= 1);
+
+ if (st == o->state)
+ return;
+
+ if (!o->context)
+ return;
+
+ o->state = st;
+
+ if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) {
+ PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
+ pa_context_unref(o->context);
+ if (o->stream)
+ pa_stream_unref(o->stream);
+ o->context = NULL;
+ o->stream = NULL;
+ o->callback = NULL;
+ o->userdata = NULL;
+
+ pa_operation_unref(o);
+ }
+}
+
+void pa_operation_cancel(pa_operation *o) {
+ assert(o && o->ref >= 1);
+ operation_set_state(o, PA_OPERATION_CANCELED);
+}
+
+void pa_operation_done(pa_operation *o) {
+ assert(o && o->ref >= 1);
+ operation_set_state(o, PA_OPERATION_DONE);
+}
+
+pa_operation_state_t pa_operation_get_state(pa_operation *o) {
+ assert(o && o->ref >= 1);
+ return o->state;
+}
diff --git a/src/polyp/polyplib-operation.h b/src/polyp/polyplib-operation.h
new file mode 100644
index 00000000..cac03e30
--- /dev/null
+++ b/src/polyp/polyplib-operation.h
@@ -0,0 +1,51 @@
+#ifndef foopolypliboperationhfoo
+#define foopolypliboperationhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/cdecl.h>
+#include <polyp/polyplib-def.h>
+
+/** \file
+ * Asynchronous operations */
+
+PA_C_DECL_BEGIN
+
+/** \pa_operation
+ * An asynchronous operation object */
+typedef struct pa_operation pa_operation;
+
+/** Increase the reference count by one */
+pa_operation *pa_operation_ref(pa_operation *o);
+
+/** Decrease the reference count by one */
+void pa_operation_unref(pa_operation *o);
+
+/** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */
+void pa_operation_cancel(pa_operation *o);
+
+/** Return the current status of the operation */
+pa_operation_state_t pa_operation_get_state(pa_operation *o);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-scache.c b/src/polyp/polyplib-scache.c
new file mode 100644
index 00000000..1315af97
--- /dev/null
+++ b/src/polyp/polyplib-scache.c
@@ -0,0 +1,127 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "polyplib-scache.h"
+#include "polyplib-internal.h"
+#include <polypcore/pstream-util.h>
+
+void pa_stream_connect_upload(pa_stream *s, size_t length) {
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ assert(s && length);
+
+ pa_stream_ref(s);
+
+ s->state = PA_STREAM_CREATING;
+ s->direction = PA_STREAM_UPLOAD;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_CREATE_UPLOAD_STREAM);
+ pa_tagstruct_putu32(t, tag = s->context->ctag++);
+ pa_tagstruct_puts(t, s->name);
+ pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+ pa_tagstruct_putu32(t, length);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s);
+
+ pa_stream_unref(s);
+}
+
+void pa_stream_finish_upload(pa_stream *s) {
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(s);
+
+ if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY)
+ return;
+
+ pa_stream_ref(s);
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+
+ pa_tagstruct_putu32(t, PA_COMMAND_FINISH_UPLOAD_STREAM);
+ pa_tagstruct_putu32(t, tag = s->context->ctag++);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s);
+
+ pa_stream_unref(s);
+}
+
+pa_operation * pa_context_play_sample(pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && name && *name && (!dev || *dev));
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ if (!dev)
+ dev = c->conf->default_sink;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAY_SAMPLE);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, (uint32_t) -1);
+ pa_tagstruct_puts(t, dev);
+ pa_tagstruct_putu32(t, volume);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c && name);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_SAMPLE);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
diff --git a/src/polyp/polyplib-scache.h b/src/polyp/polyplib-scache.h
new file mode 100644
index 00000000..89d27597
--- /dev/null
+++ b/src/polyp/polyplib-scache.h
@@ -0,0 +1,50 @@
+#ifndef foopolyplibscachehfoo
+#define foopolyplibscachehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <polyp/polyplib-context.h>
+#include <polyp/polyplib-stream.h>
+#include <polyp/cdecl.h>
+
+/** \file
+ * All sample cache related routines */
+
+PA_C_DECL_BEGIN
+
+/** Make this stream a sample upload stream */
+void pa_stream_connect_upload(pa_stream *s, size_t length);
+
+/** Finish the sample upload, the stream name will become the sample name. You cancel a sample upload by issuing pa_stream_disconnect() */
+void pa_stream_finish_upload(pa_stream *s);
+
+/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */
+pa_operation* pa_context_play_sample(pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
+
+/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-simple.c b/src/polyp/polyplib-simple.c
new file mode 100644
index 00000000..7436f007
--- /dev/null
+++ b/src/polyp/polyplib-simple.c
@@ -0,0 +1,393 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "polyplib-simple.h"
+#include "polyplib.h"
+#include "mainloop.h"
+#include <polypcore/native-common.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/log.h>
+
+struct pa_simple {
+ pa_mainloop *mainloop;
+ pa_context *context;
+ pa_stream *stream;
+ pa_stream_direction_t direction;
+
+ int dead;
+
+ void *read_data;
+ size_t read_index, read_length;
+ pa_usec_t latency;
+};
+
+static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata);
+
+static int check_error(pa_simple *p, int *rerror) {
+ pa_context_state_t cst;
+ pa_stream_state_t sst;
+ assert(p);
+
+ if ((cst = pa_context_get_state(p->context)) == PA_CONTEXT_FAILED)
+ goto fail;
+
+ assert(cst != PA_CONTEXT_TERMINATED);
+
+ if (p->stream) {
+ if ((sst = pa_stream_get_state(p->stream)) == PA_STREAM_FAILED)
+ goto fail;
+
+ assert(sst != PA_STREAM_TERMINATED);
+ }
+
+ return 0;
+
+fail:
+ if (rerror)
+ *rerror = pa_context_errno(p->context);
+
+ p->dead = 1;
+
+ return -1;
+}
+
+static int iterate(pa_simple *p, int block, int *rerror) {
+ assert(p && p->context && p->mainloop);
+
+ if (check_error(p, rerror) < 0)
+ return -1;
+
+ if (!block && !pa_context_is_pending(p->context))
+ return 0;
+
+ do {
+ if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) {
+ if (rerror)
+ *rerror = PA_ERROR_INTERNAL;
+ return -1;
+ }
+
+ if (check_error(p, rerror) < 0)
+ return -1;
+
+ } while (pa_context_is_pending(p->context));
+
+
+ while (pa_mainloop_deferred_pending(p->mainloop)) {
+
+ if (pa_mainloop_iterate(p->mainloop, 0, NULL) < 0) {
+ if (rerror)
+ *rerror = PA_ERROR_INTERNAL;
+ return -1;
+ }
+
+ if (check_error(p, rerror) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+pa_simple* pa_simple_new(
+ const char *server,
+ const char *name,
+ pa_stream_direction_t dir,
+ const char *dev,
+ const char *stream_name,
+ const pa_sample_spec *ss,
+ const pa_buffer_attr *attr,
+ int *rerror) {
+
+ pa_simple *p;
+ int error = PA_ERROR_INTERNAL;
+ assert(ss && (dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD));
+
+ p = pa_xmalloc(sizeof(pa_simple));
+ p->context = NULL;
+ p->stream = NULL;
+ p->mainloop = pa_mainloop_new();
+ assert(p->mainloop);
+ p->dead = 0;
+ p->direction = dir;
+ p->read_data = NULL;
+ p->read_index = p->read_length = 0;
+ p->latency = 0;
+
+ if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name)))
+ goto fail;
+
+ pa_context_connect(p->context, server, 1, NULL);
+
+ /* Wait until the context is ready */
+ while (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
+ if (iterate(p, 1, &error) < 0)
+ goto fail;
+ }
+
+ if (!(p->stream = pa_stream_new(p->context, stream_name, ss, NULL)))
+ goto fail;
+
+ if (dir == PA_STREAM_PLAYBACK)
+ pa_stream_connect_playback(p->stream, dev, attr, 0, NULL);
+ else
+ pa_stream_connect_record(p->stream, dev, attr, 0);
+
+ /* Wait until the stream is ready */
+ while (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
+ if (iterate(p, 1, &error) < 0)
+ goto fail;
+ }
+
+ pa_stream_set_read_callback(p->stream, read_callback, p);
+
+ return p;
+
+fail:
+ if (rerror)
+ *rerror = error;
+ pa_simple_free(p);
+ return NULL;
+}
+
+void pa_simple_free(pa_simple *s) {
+ assert(s);
+
+ pa_xfree(s->read_data);
+
+ if (s->stream)
+ pa_stream_unref(s->stream);
+
+ if (s->context)
+ pa_context_unref(s->context);
+
+ if (s->mainloop)
+ pa_mainloop_free(s->mainloop);
+
+ pa_xfree(s);
+}
+
+int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
+ assert(p && data && p->direction == PA_STREAM_PLAYBACK);
+
+ if (p->dead) {
+ if (rerror)
+ *rerror = pa_context_errno(p->context);
+
+ return -1;
+ }
+
+ while (length > 0) {
+ size_t l;
+
+ while (!(l = pa_stream_writable_size(p->stream)))
+ if (iterate(p, 1, rerror) < 0)
+ return -1;
+
+ if (l > length)
+ l = length;
+
+ pa_stream_write(p->stream, data, l, NULL, 0);
+ data = (const uint8_t*) data + l;
+ length -= l;
+ }
+
+ /* Make sure that no data is pending for write */
+ if (iterate(p, 0, rerror) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata) {
+ pa_simple *p = userdata;
+ assert(s && data && length && p);
+
+ if (p->read_data) {
+ pa_log(__FILE__": Buffer overflow, dropping incoming memory blocks.\n");
+ pa_xfree(p->read_data);
+ }
+
+ p->read_data = pa_xmemdup(data, p->read_length = length);
+ p->read_index = 0;
+}
+
+int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
+ assert(p && data && p->direction == PA_STREAM_RECORD);
+
+ if (p->dead) {
+ if (rerror)
+ *rerror = pa_context_errno(p->context);
+
+ return -1;
+ }
+
+ while (length > 0) {
+ if (p->read_data) {
+ size_t l = length;
+
+ if (p->read_length <= l)
+ l = p->read_length;
+
+ memcpy(data, (uint8_t*) p->read_data+p->read_index, l);
+
+ data = (uint8_t*) data + l;
+ length -= l;
+
+ p->read_index += l;
+ p->read_length -= l;
+
+ if (!p->read_length) {
+ pa_xfree(p->read_data);
+ p->read_data = NULL;
+ p->read_index = 0;
+ }
+
+ if (!length)
+ return 0;
+
+ assert(!p->read_data);
+ }
+
+ if (iterate(p, 1, rerror) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void drain_or_flush_complete(pa_stream *s, int success, void *userdata) {
+ pa_simple *p = userdata;
+ assert(s && p);
+ if (!success)
+ p->dead = 1;
+}
+
+int pa_simple_drain(pa_simple *p, int *rerror) {
+ pa_operation *o;
+ assert(p && p->direction == PA_STREAM_PLAYBACK);
+
+ if (p->dead) {
+ if (rerror)
+ *rerror = pa_context_errno(p->context);
+
+ return -1;
+ }
+
+ o = pa_stream_drain(p->stream, drain_or_flush_complete, p);
+
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
+ if (iterate(p, 1, rerror) < 0) {
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
+ return -1;
+ }
+ }
+
+ pa_operation_unref(o);
+
+ if (p->dead && rerror)
+ *rerror = pa_context_errno(p->context);
+
+ return p->dead ? -1 : 0;
+}
+
+static void latency_complete(pa_stream *s, const pa_latency_info *l, void *userdata) {
+ pa_simple *p = userdata;
+ assert(s && p);
+
+ if (!l)
+ p->dead = 1;
+ else {
+ int negative = 0;
+ p->latency = pa_stream_get_latency(s, l, &negative);
+ if (negative)
+ p->latency = 0;
+ }
+}
+
+pa_usec_t pa_simple_get_playback_latency(pa_simple *p, int *rerror) {
+ pa_operation *o;
+ assert(p && p->direction == PA_STREAM_PLAYBACK);
+
+ if (p->dead) {
+ if (rerror)
+ *rerror = pa_context_errno(p->context);
+
+ return (pa_usec_t) -1;
+ }
+
+ p->latency = 0;
+ o = pa_stream_get_latency_info(p->stream, latency_complete, p);
+
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
+
+ if (iterate(p, 1, rerror) < 0) {
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
+ return -1;
+ }
+ }
+
+ pa_operation_unref(o);
+
+ if (p->dead && rerror)
+ *rerror = pa_context_errno(p->context);
+
+ return p->dead ? (pa_usec_t) -1 : p->latency;
+}
+
+int pa_simple_flush(pa_simple *p, int *rerror) {
+ pa_operation *o;
+ assert(p && p->direction == PA_STREAM_PLAYBACK);
+
+ if (p->dead) {
+ if (rerror)
+ *rerror = pa_context_errno(p->context);
+
+ return -1;
+ }
+
+ o = pa_stream_flush(p->stream, drain_or_flush_complete, p);
+
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
+ if (iterate(p, 1, rerror) < 0) {
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
+ return -1;
+ }
+ }
+
+ pa_operation_unref(o);
+
+ if (p->dead && rerror)
+ *rerror = pa_context_errno(p->context);
+
+ return p->dead ? -1 : 0;
+}
diff --git a/src/polyp/polyplib-simple.h b/src/polyp/polyplib-simple.h
new file mode 100644
index 00000000..b01f30d5
--- /dev/null
+++ b/src/polyp/polyplib-simple.h
@@ -0,0 +1,80 @@
+#ifndef foopolyplibsimplehfoo
+#define foopolyplibsimplehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include "sample.h"
+#include "polyplib-def.h"
+#include <polyp/cdecl.h>
+
+/** \file
+ * A simple but limited synchronous playback and recording
+ * API. This is synchronouse, simplified wrapper around the standard
+ * asynchronous API. */
+
+/** \example pacat-simple.c
+ * A simple playback tool using the simple API */
+
+/** \example parec-simple.c
+ * A simple recording tool using the simple API */
+
+PA_C_DECL_BEGIN
+
+/** \pa_simple
+ * An opaque simple connection object */
+typedef struct pa_simple pa_simple;
+
+/** Create a new connection to the server */
+pa_simple* pa_simple_new(
+ const char *server, /**< Server name, or NULL for default */
+ const char *name, /**< A descriptive name for this client (application name, ...) */
+ pa_stream_direction_t dir, /**< Open this stream for recording or playback? */
+ const char *dev, /**< Sink (resp. source) name, or NULL for default */
+ const char *stream_name, /**< A descriptive name for this client (application name, song title, ...) */
+ const pa_sample_spec *ss, /**< The sample type to use */
+ const pa_buffer_attr *attr, /**< Buffering attributes, or NULL for default */
+ int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */
+ );
+
+/** Close and free the connection to the server. The connection objects becomes invalid when this is called. */
+void pa_simple_free(pa_simple *s);
+
+/** Write some data to the server */
+int pa_simple_write(pa_simple *s, const void*data, size_t length, int *error);
+
+/** Wait until all data already written is played by the daemon */
+int pa_simple_drain(pa_simple *s, int *error);
+
+/** Read some data from the server */
+int pa_simple_read(pa_simple *s, void*data, size_t length, int *error);
+
+/** Return the playback latency. \since 0.5 */
+pa_usec_t pa_simple_get_playback_latency(pa_simple *s, int *error);
+
+/** Flush the playback buffer. \since 0.5 */
+int pa_simple_flush(pa_simple *s, int *error);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-stream.c b/src/polyp/polyplib-stream.c
new file mode 100644
index 00000000..63c9245b
--- /dev/null
+++ b/src/polyp/polyplib-stream.c
@@ -0,0 +1,807 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "polyplib-internal.h"
+#include <polypcore/xmalloc.h>
+#include <polypcore/pstream-util.h>
+#include <polypcore/util.h>
+#include <polypcore/log.h>
+
+#define LATENCY_IPOL_INTERVAL_USEC (10000L)
+
+pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
+ pa_stream *s;
+ assert(c);
+ assert(ss);
+
+ if (!pa_sample_spec_valid(ss))
+ return NULL;
+
+ if (map && !pa_channel_map_valid(map))
+ return NULL;
+
+ s = pa_xnew(pa_stream, 1);
+ s->ref = 1;
+ s->context = c;
+ s->mainloop = c->mainloop;
+
+ s->read_callback = NULL;
+ s->read_userdata = NULL;
+ s->write_callback = NULL;
+ s->write_userdata = NULL;
+ s->state_callback = NULL;
+ s->state_userdata = NULL;
+
+ s->direction = PA_STREAM_NODIRECTION;
+ s->name = pa_xstrdup(name);
+ s->sample_spec = *ss;
+
+ if (map)
+ s->channel_map = *map;
+ else
+ pa_channel_map_init_auto(&s->channel_map, ss->channels);
+
+ s->channel = 0;
+ s->channel_valid = 0;
+ s->device_index = PA_INVALID_INDEX;
+ s->requested_bytes = 0;
+ s->state = PA_STREAM_DISCONNECTED;
+ memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
+
+ s->mcalign = pa_mcalign_new(pa_frame_size(ss), c->memblock_stat);
+
+ s->counter = 0;
+ s->previous_time = 0;
+ s->previous_ipol_time = 0;
+
+ s->corked = 0;
+ s->interpolate = 0;
+
+ s->ipol_usec = 0;
+ memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp));
+ s->ipol_event = NULL;
+ s->ipol_requested = 0;
+
+ PA_LLIST_PREPEND(pa_stream, c->streams, s);
+
+ return pa_stream_ref(s);
+}
+
+static void stream_free(pa_stream *s) {
+ assert(s);
+
+ if (s->ipol_event) {
+ assert(s->mainloop);
+ s->mainloop->time_free(s->ipol_event);
+ }
+
+ pa_mcalign_free(s->mcalign);
+
+ pa_xfree(s->name);
+ pa_xfree(s);
+}
+
+void pa_stream_unref(pa_stream *s) {
+ assert(s && s->ref >= 1);
+
+ if (--(s->ref) == 0)
+ stream_free(s);
+}
+
+pa_stream* pa_stream_ref(pa_stream *s) {
+ assert(s && s->ref >= 1);
+ s->ref++;
+ return s;
+}
+
+pa_stream_state_t pa_stream_get_state(pa_stream *s) {
+ assert(s && s->ref >= 1);
+ return s->state;
+}
+
+pa_context* pa_stream_get_context(pa_stream *s) {
+ assert(s && s->ref >= 1);
+ return s->context;
+}
+
+uint32_t pa_stream_get_index(pa_stream *s) {
+ assert(s && s->ref >= 1);
+ return s->device_index;
+}
+
+void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
+ assert(s && s->ref >= 1);
+
+ if (s->state == st)
+ return;
+
+ pa_stream_ref(s);
+
+ s->state = st;
+
+ if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) {
+ if (s->channel_valid)
+ pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+
+ PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
+ pa_stream_unref(s);
+ }
+
+ if (s->state_callback)
+ s->state_callback(s, s->state_userdata);
+
+ pa_stream_unref(s);
+}
+
+void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+ assert(pd && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c);
+
+ pa_context_ref(c);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
+ goto finish;
+
+ c->error = PA_ERROR_KILLED;
+ pa_stream_set_state(s, PA_STREAM_FAILED);
+
+finish:
+ pa_context_unref(c);
+}
+
+void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_stream *s;
+ pa_context *c = userdata;
+ uint32_t bytes, channel;
+ assert(pd && command == PA_COMMAND_REQUEST && t && c);
+
+ pa_context_ref(c);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_getu32(t, &bytes) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ pa_stream_ref(s);
+
+ s->requested_bytes += bytes;
+
+ if (s->requested_bytes && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
+
+ pa_stream_unref(s);
+
+finish:
+ pa_context_unref(c);
+}
+
+static void ipol_callback(pa_mainloop_api *m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+ struct timeval tv2;
+ pa_stream *s = userdata;
+
+ pa_stream_ref(s);
+
+/* pa_log("requesting new ipol data\n"); */
+
+ if (s->state == PA_STREAM_READY && !s->ipol_requested) {
+ pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
+ s->ipol_requested = 1;
+ }
+
+ pa_gettimeofday(&tv2);
+ pa_timeval_add(&tv2, LATENCY_IPOL_INTERVAL_USEC);
+
+ m->time_restart(e, &tv2);
+
+ pa_stream_unref(s);
+}
+
+
+void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_stream *s = userdata;
+ assert(pd && s && s->state == PA_STREAM_CREATING);
+
+ pa_stream_ref(s);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(s->context, command, t) < 0)
+ goto finish;
+
+ pa_stream_set_state(s, PA_STREAM_FAILED);
+ goto finish;
+ }
+
+ if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
+ ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) ||
+ ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(s->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ s->channel_valid = 1;
+ pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
+ pa_stream_set_state(s, PA_STREAM_READY);
+
+ if (s->interpolate) {
+ struct timeval tv;
+ pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
+
+ pa_gettimeofday(&tv);
+ tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */
+
+ assert(!s->ipol_event);
+ s->ipol_event = s->mainloop->time_new(s->mainloop, &tv, &ipol_callback, s);
+ }
+
+ if (s->requested_bytes && s->ref > 1 && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
+
+finish:
+ pa_stream_unref(s);
+}
+
+static void create_stream(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, const pa_cvolume *volume) {
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(s && s->ref >= 1 && s->state == PA_STREAM_DISCONNECTED);
+
+ pa_stream_ref(s);
+
+ s->interpolate = !!(flags & PA_STREAM_INTERPOLATE_LATENCY);
+ pa_stream_trash_ipol(s);
+
+ if (attr)
+ s->buffer_attr = *attr;
+ else {
+ /* half a second */
+ s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2;
+ s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2;
+ s->buffer_attr.minreq = s->buffer_attr.tlength/100;
+ s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq;
+ s->buffer_attr.fragsize = s->buffer_attr.tlength/100;
+ }
+
+ pa_stream_set_state(s, PA_STREAM_CREATING);
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+
+ if (!dev) {
+ if (s->direction == PA_STREAM_PLAYBACK)
+ dev = s->context->conf->default_sink;
+ else
+ dev = s->context->conf->default_source;
+ }
+
+ pa_tagstruct_put(t,
+ PA_TAG_U32, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM,
+ PA_TAG_U32, tag = s->context->ctag++,
+ PA_TAG_STRING, s->name,
+ PA_TAG_SAMPLE_SPEC, &s->sample_spec,
+ PA_TAG_CHANNEL_MAP, &s->channel_map,
+ PA_TAG_U32, PA_INVALID_INDEX,
+ PA_TAG_STRING, dev,
+ PA_TAG_U32, s->buffer_attr.maxlength,
+ PA_TAG_BOOLEAN, !!(flags & PA_STREAM_START_CORKED),
+ PA_TAG_INVALID);
+
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ pa_cvolume cv;
+ pa_tagstruct_put(t,
+ PA_TAG_U32, s->buffer_attr.tlength,
+ PA_TAG_U32, s->buffer_attr.prebuf,
+ PA_TAG_U32, s->buffer_attr.minreq,
+ PA_TAG_INVALID);
+
+ if (!volume) {
+ pa_cvolume_reset(&cv, s->sample_spec.channels);
+ volume = &cv;
+ }
+
+ pa_tagstruct_put_cvolume(t, volume);
+ } else
+ pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s);
+
+ pa_stream_unref(s);
+}
+
+void pa_stream_connect_playback(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, pa_cvolume *volume) {
+ assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1);
+ s->direction = PA_STREAM_PLAYBACK;
+ create_stream(s, dev, attr, flags, volume);
+}
+
+void pa_stream_connect_record(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags) {
+ assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1);
+ s->direction = PA_STREAM_RECORD;
+ create_stream(s, dev, attr, flags, 0);
+}
+
+void pa_stream_write(pa_stream *s, const void *data, size_t length, void (*free_cb)(void *p), size_t delta) {
+ pa_memchunk chunk;
+ assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1);
+
+ if (free_cb) {
+ chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat);
+ assert(chunk.memblock && chunk.memblock->data);
+ } else {
+ chunk.memblock = pa_memblock_new(length, s->context->memblock_stat);
+ assert(chunk.memblock && chunk.memblock->data);
+ memcpy(chunk.memblock->data, data, length);
+ }
+ chunk.index = 0;
+ chunk.length = length;
+
+ pa_pstream_send_memblock(s->context->pstream, s->channel, delta, &chunk);
+ pa_memblock_unref(chunk.memblock);
+
+ if (length < s->requested_bytes)
+ s->requested_bytes -= length;
+ else
+ s->requested_bytes = 0;
+
+ s->counter += length;
+}
+
+size_t pa_stream_writable_size(pa_stream *s) {
+ assert(s && s->ref >= 1);
+ return s->state == PA_STREAM_READY ? s->requested_bytes : 0;
+}
+
+pa_operation * pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(s && s->ref >= 1 && s->state == PA_STREAM_READY);
+
+ o = pa_operation_new(s->context, s);
+ assert(o);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_DRAIN_PLAYBACK_STREAM);
+ pa_tagstruct_putu32(t, tag = s->context->ctag++);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ pa_latency_info i, *p = NULL;
+ struct timeval local, remote, now;
+ assert(pd && o && o->stream && o->context);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ } else if (pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
+ pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
+ pa_tagstruct_get_boolean(t, &i.playing) < 0 ||
+ pa_tagstruct_getu32(t, &i.queue_length) < 0 ||
+ pa_tagstruct_get_timeval(t, &local) < 0 ||
+ pa_tagstruct_get_timeval(t, &remote) < 0 ||
+ pa_tagstruct_getu64(t, &i.counter) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ } else {
+ pa_gettimeofday(&now);
+
+ if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) {
+ /* local and remote seem to have synchronized clocks */
+
+ if (o->stream->direction == PA_STREAM_PLAYBACK)
+ i.transport_usec = pa_timeval_diff(&remote, &local);
+ else
+ i.transport_usec = pa_timeval_diff(&now, &remote);
+
+ i.synchronized_clocks = 1;
+ i.timestamp = remote;
+ } else {
+ /* clocks are not synchronized, let's estimate latency then */
+ i.transport_usec = pa_timeval_diff(&now, &local)/2;
+ i.synchronized_clocks = 0;
+ i.timestamp = local;
+ pa_timeval_add(&i.timestamp, i.transport_usec);
+ }
+
+ if (o->stream->interpolate) {
+/* pa_log("new interpol data\n"); */
+ o->stream->ipol_timestamp = i.timestamp;
+ o->stream->ipol_usec = pa_stream_get_time(o->stream, &i);
+ o->stream->ipol_requested = 0;
+ }
+
+ p = &i;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_stream *s, const pa_latency_info *_i, void *_userdata) = (void (*)(pa_stream *s, const pa_latency_info *_i, void *_userdata)) o->callback;
+ cb(o->stream, p, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_stream_get_latency_info(pa_stream *s, void (*cb)(pa_stream *p, const pa_latency_info*i, void *userdata), void *userdata) {
+ uint32_t tag;
+ pa_operation *o;
+ pa_tagstruct *t;
+ struct timeval now;
+ assert(s && s->direction != PA_STREAM_UPLOAD);
+
+ o = pa_operation_new(s->context, s);
+ assert(o);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY);
+ pa_tagstruct_putu32(t, tag = s->context->ctag++);
+ pa_tagstruct_putu32(t, s->channel);
+
+ pa_gettimeofday(&now);
+ pa_tagstruct_put_timeval(t, &now);
+ pa_tagstruct_putu64(t, s->counter);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_info_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_stream *s = userdata;
+ assert(pd && s && s->ref >= 1);
+
+ pa_stream_ref(s);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(s->context, command, t) < 0)
+ goto finish;
+
+ pa_stream_set_state(s, PA_STREAM_FAILED);
+ goto finish;
+ } else if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(s->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ pa_stream_set_state(s, PA_STREAM_TERMINATED);
+
+finish:
+ pa_stream_unref(s);
+}
+
+void pa_stream_disconnect(pa_stream *s) {
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(s && s->ref >= 1);
+
+ if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY)
+ return;
+
+ pa_stream_ref(s);
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+
+ pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM :
+ (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM));
+ pa_tagstruct_putu32(t, tag = s->context->ctag++);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s);
+
+ pa_stream_unref(s);
+}
+
+void pa_stream_set_read_callback(pa_stream *s, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) {
+ assert(s && s->ref >= 1);
+ s->read_callback = cb;
+ s->read_userdata = userdata;
+}
+
+void pa_stream_set_write_callback(pa_stream *s, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata) {
+ assert(s && s->ref >= 1);
+ s->write_callback = cb;
+ s->write_userdata = userdata;
+}
+
+void pa_stream_set_state_callback(pa_stream *s, void (*cb)(pa_stream *s, void *userdata), void *userdata) {
+ assert(s && s->ref >= 1);
+ s->state_callback = cb;
+ s->state_userdata = userdata;
+}
+
+void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+ assert(pd && o && o->context && o->ref >= 1);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ success = 0;
+ } else if (!pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ void (*cb)(pa_stream *s, int _success, void *_userdata) = (void (*)(pa_stream *s, int _success, void *_userdata)) o->callback;
+ cb(o->stream, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+pa_operation* pa_stream_cork(pa_stream *s, int b, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(s && s->ref >= 1 && s->state == PA_STREAM_READY);
+
+ if (s->interpolate) {
+ if (!s->corked && b)
+ /* Pausing */
+ s->ipol_usec = pa_stream_get_interpolated_time(s);
+ else if (s->corked && !b)
+ /* Unpausing */
+ pa_gettimeofday(&s->ipol_timestamp);
+ }
+
+ s->corked = b;
+
+ o = pa_operation_new(s->context, s);
+ assert(o);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM);
+ pa_tagstruct_putu32(t, tag = s->context->ctag++);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_put_boolean(t, !!b);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
+
+ pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
+
+ return pa_operation_ref(o);
+}
+
+static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) {
+ pa_tagstruct *t;
+ pa_operation *o;
+ uint32_t tag;
+ assert(s && s->ref >= 1 && s->state == PA_STREAM_READY);
+
+ o = pa_operation_new(s->context, s);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, command);
+ pa_tagstruct_putu32(t, tag = s->context->ctag++);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+pa_operation* pa_stream_flush(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata);
+ pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
+ return o;
+}
+
+pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata);
+ pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
+ return o;
+}
+
+pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata);
+ pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
+ return o;
+}
+
+pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(s && s->ref >= 1 && s->state == PA_STREAM_READY && name && s->direction != PA_STREAM_UPLOAD);
+
+ o = pa_operation_new(s->context, s);
+ assert(o);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME);
+ pa_tagstruct_putu32(t, tag = s->context->ctag++);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_puts(t, name);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+uint64_t pa_stream_get_counter(pa_stream *s) {
+ assert(s);
+ return s->counter;
+}
+
+pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i) {
+ pa_usec_t usec;
+ assert(s);
+
+ usec = pa_bytes_to_usec(i->counter, &s->sample_spec);
+
+ if (i) {
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ pa_usec_t latency = i->transport_usec + i->buffer_usec + i->sink_usec;
+ if (usec < latency)
+ usec = 0;
+ else
+ usec -= latency;
+
+ } else if (s->direction == PA_STREAM_RECORD) {
+ usec += i->source_usec + i->buffer_usec + i->transport_usec;
+
+ if (usec > i->sink_usec)
+ usec -= i->sink_usec;
+ else
+ usec = 0;
+ }
+ }
+
+ if (usec < s->previous_time)
+ usec = s->previous_time;
+
+ s->previous_time = usec;
+
+ return usec;
+}
+
+static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t t, pa_usec_t c, int *negative) {
+ assert(s);
+
+ if (negative)
+ *negative = 0;
+
+ if (c < t) {
+ if (s->direction == PA_STREAM_RECORD) {
+ if (negative)
+ *negative = 1;
+
+ return t-c;
+ } else
+ return 0;
+ } else
+ return c-t;
+}
+
+pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative) {
+ pa_usec_t t, c;
+ assert(s && i);
+
+ t = pa_stream_get_time(s, i);
+ c = pa_bytes_to_usec(s->counter, &s->sample_spec);
+
+ return time_counter_diff(s, t, c, negative);
+}
+
+const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {
+ assert(s);
+ return &s->sample_spec;
+}
+
+void pa_stream_trash_ipol(pa_stream *s) {
+ assert(s);
+
+ if (!s->interpolate)
+ return;
+
+ memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp));
+ s->ipol_usec = 0;
+}
+
+pa_usec_t pa_stream_get_interpolated_time(pa_stream *s) {
+ pa_usec_t usec;
+ assert(s && s->interpolate);
+
+ if (s->corked)
+ usec = s->ipol_usec;
+ else {
+ if (s->ipol_timestamp.tv_sec == 0)
+ usec = 0;
+ else
+ usec = s->ipol_usec + pa_timeval_age(&s->ipol_timestamp);
+ }
+
+ if (usec < s->previous_ipol_time)
+ usec = s->previous_ipol_time;
+
+ s->previous_ipol_time = usec;
+
+ return usec;
+}
+
+pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative) {
+ pa_usec_t t, c;
+ assert(s && s->interpolate);
+
+ t = pa_stream_get_interpolated_time(s);
+ c = pa_bytes_to_usec(s->counter, &s->sample_spec);
+ return time_counter_diff(s, t, c, negative);
+}
diff --git a/src/polyp/polyplib-stream.h b/src/polyp/polyplib-stream.h
new file mode 100644
index 00000000..bc828b71
--- /dev/null
+++ b/src/polyp/polyplib-stream.h
@@ -0,0 +1,181 @@
+#ifndef foopolyplibstreamhfoo
+#define foopolyplibstreamhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <polyp/sample.h>
+#include <polyp/channelmap.h>
+#include <polyp/volume.h>
+#include <polyp/polyplib-def.h>
+#include <polyp/cdecl.h>
+#include <polyp/polyplib-operation.h>
+
+/** \file
+ * Audio streams for input, output and sample upload */
+
+PA_C_DECL_BEGIN
+
+/** \pa_stream
+ * An opaque stream for playback or recording */
+typedef struct pa_stream pa_stream;
+
+/** Create a new, unconnected stream with the specified name and sample type */
+pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map);
+
+/** Decrease the reference counter by one */
+void pa_stream_unref(pa_stream *s);
+
+/** Increase the reference counter by one */
+pa_stream *pa_stream_ref(pa_stream *s);
+
+/** Return the current state of the stream */
+pa_stream_state_t pa_stream_get_state(pa_stream *p);
+
+/** Return the context this stream is attached to */
+pa_context* pa_stream_get_context(pa_stream *p);
+
+/** Return the device (sink input or source output) index this stream is connected to */
+uint32_t pa_stream_get_index(pa_stream *s);
+
+/** Connect the stream to a sink */
+void pa_stream_connect_playback(
+ pa_stream *s,
+ const char *dev,
+ const pa_buffer_attr *attr,
+ pa_stream_flags_t flags,
+ pa_cvolume *volume);
+
+/** Connect the stream to a source */
+void pa_stream_connect_record(
+ pa_stream *s,
+ const char *dev,
+ const pa_buffer_attr *attr,
+ pa_stream_flags_t flags);
+
+/** Disconnect a stream from a source/sink */
+void pa_stream_disconnect(pa_stream *s);
+
+/** Write some data to the server (for playback sinks), if free_cb is
+ * non-NULL this routine is called when all data has been written out
+ * and an internal reference to the specified data is kept, the data
+ * is not copied. If NULL, the data is copied into an internal
+ * buffer. */
+void pa_stream_write(pa_stream *p /**< The stream to use */,
+ const void *data /**< The data to write */,
+ size_t length /**< The length of the data to write */,
+ void (*free_cb)(void *p) /**< A cleanup routine for the data or NULL to request an internal copy */,
+ size_t delta /**< Drop this many
+ bytes in the playback
+ buffer before writing
+ this data. Use
+ (size_t) -1 for
+ clearing the whole
+ playback
+ buffer. Normally you
+ will specify 0 here,
+ i.e. append to the
+ playback buffer. If
+ the value given here
+ is greater than the
+ buffered data length
+ the buffer is cleared
+ and the data is
+ written to the
+ buffer's start. This
+ value is ignored on
+ upload streams. */);
+
+/** Return the amount of bytes that may be written using pa_stream_write() */
+size_t pa_stream_writable_size(pa_stream *p);
+
+/** Drain a playback stream */
+pa_operation* pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata);
+
+/** Get the playback latency of a stream */
+pa_operation* pa_stream_get_latency_info(pa_stream *p, void (*cb)(pa_stream *p, const pa_latency_info *i, void *userdata), void *userdata);
+
+/** Set the callback function that is called whenever the state of the stream changes */
+void pa_stream_set_state_callback(pa_stream *s, void (*cb)(pa_stream *s, void *userdata), void *userdata);
+
+/** Set the callback function that is called when new data may be
+ * written to the stream. */
+void pa_stream_set_write_callback(pa_stream *p, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata);
+
+/** Set the callback function that is called when new data is available from the stream */
+void pa_stream_set_read_callback(pa_stream *p, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), void *userdata);
+
+/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */
+pa_operation* pa_stream_cork(pa_stream *s, int b, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata);
+
+/** Flush the playback buffer of this stream. Most of the time you're
+ * better off using the parameter delta of pa_stream_write() instead of this
+ * function. Available on both playback and recording streams. \since 0.3 */
+pa_operation* pa_stream_flush(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata);
+
+/** Reenable prebuffering. Available for playback streams only. \since 0.6 */
+pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata);
+
+/** Request immediate start of playback on this stream. This disables
+ * prebuffering as specified in the pa_buffer_attr structure. Available for playback streams only. \since
+ * 0.3 */
+pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata);
+
+/** Rename the stream. \since 0.5 */
+pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata);
+
+/** Return the total number of bytes written to/read from the
+ * stream. This counter is not reset on pa_stream_flush(), you may do
+ * this yourself using pa_stream_reset_counter(). \since 0.6 */
+uint64_t pa_stream_get_counter(pa_stream *s);
+
+/** Return the current playback/recording time. This is based on the
+ * counter accessible with pa_stream_get_counter(). This function
+ * requires a pa_latency_info structure as argument, which should be
+ * acquired using pa_stream_get_latency(). \since 0.6 */
+pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i);
+
+/** Return the total stream latency. Thus function requires a
+ * pa_latency_info structure as argument, which should be aquired
+ * using pa_stream_get_latency(). In case the stream is a monitoring
+ * stream the result can be negative, i.e. the captured samples are
+ * not yet played. In this case *negative is set to 1. \since 0.6 */
+pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative);
+
+/** Return the interpolated playback/recording time. Requires the
+ * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the stream. In
+ * contrast to pa_stream_get_latency() this function doesn't require
+ * a whole roundtrip for response. \since 0.6 */
+pa_usec_t pa_stream_get_interpolated_time(pa_stream *s);
+
+/** Return the interpolated playback/recording latency. Requires the
+ * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the
+ * stream. \since 0.6 */
+pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative);
+
+/** Return a pointer to the streams sample specification. \since 0.6 */
+const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-subscribe.c b/src/polyp/polyplib-subscribe.c
new file mode 100644
index 00000000..13fcfb42
--- /dev/null
+++ b/src/polyp/polyplib-subscribe.c
@@ -0,0 +1,81 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdio.h>
+
+#include "polyplib-subscribe.h"
+#include "polyplib-internal.h"
+#include <polypcore/pstream-util.h>
+#include <polypcore/gccmacro.h>
+
+void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_subscription_event_type_t e;
+ uint32_t index;
+ assert(pd && command == PA_COMMAND_SUBSCRIBE_EVENT && t && c);
+
+ pa_context_ref(c);
+
+ if (pa_tagstruct_getu32(t, &e) < 0 ||
+ pa_tagstruct_getu32(t, &index) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERROR_PROTOCOL);
+ goto finish;
+ }
+
+ if (c->subscribe_callback)
+ c->subscribe_callback(c, e, index, c->subscribe_userdata);
+
+finish:
+ pa_context_unref(c);
+}
+
+
+pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ assert(c);
+
+ o = pa_operation_new(c, NULL);
+ o->callback = (pa_operation_callback) cb;
+ o->userdata = userdata;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_putu32(t, m);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
+
+ return pa_operation_ref(o);
+}
+
+void pa_context_set_subscribe_callback(pa_context *c, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) {
+ assert(c);
+ c->subscribe_callback = cb;
+ c->subscribe_userdata = userdata;
+}
diff --git a/src/polyp/polyplib-subscribe.h b/src/polyp/polyplib-subscribe.h
new file mode 100644
index 00000000..920c9853
--- /dev/null
+++ b/src/polyp/polyplib-subscribe.h
@@ -0,0 +1,47 @@
+#ifndef foopolyplibsubscribehfoo
+#define foopolyplibsubscribehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+#include <polyp/polyplib-def.h>
+#include <polyp/polyplib-context.h>
+#include <polyp/cdecl.h>
+
+/** \file
+ * Daemon introspection event subscription subsystem. Use this
+ * to be notified whenever the internal layout of daemon changes:
+ * i.e. entities such as sinks or sources are create, removed or
+ * modified. */
+
+PA_C_DECL_BEGIN
+
+/** Enable event notification */
+pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), void *userdata);
+
+/** Set the context specific call back function that is called whenever the state of the daemon changes */
+void pa_context_set_subscribe_callback(pa_context *c, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib-version.h.in b/src/polyp/polyplib-version.h.in
new file mode 100644
index 00000000..89e0a0e5
--- /dev/null
+++ b/src/polyp/polyplib-version.h.in
@@ -0,0 +1,47 @@
+#ifndef foopolyplibversionhfoo /*-*-C-*-*/
+#define foopolyplibversionhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/* WARNING: Make sure to edit the real source file polyplib-version.h.in! */
+
+/** \file
+ * Define header version */
+
+PA_C_DECL_BEGIN
+
+/** Return the version of the header files. Keep in mind that this is
+a macro and not a function, so it is impossible to get the pointer of
+it. */
+#define pa_get_headers_version() ("@PACKAGE_VERSION@")
+
+/** Return the version of the library the current application is linked to. */
+const char* pa_get_library_version(void);
+
+/** The current API version. Version 6 relates to polypaudio
+ * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have
+ * PA_API_VERSION undefined. */
+#define PA_API_VERSION @PA_API_VERSION@
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/polyplib.h b/src/polyp/polyplib.h
new file mode 100644
index 00000000..b9b9b447
--- /dev/null
+++ b/src/polyp/polyplib.h
@@ -0,0 +1,86 @@
+#ifndef foopolyplibhfoo
+#define foopolyplibhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/cdecl.h>
+#include <polyp/mainloop-api.h>
+#include <polyp/sample.h>
+#include <polyp/polyplib-def.h>
+#include <polyp/polyplib-context.h>
+#include <polyp/polyplib-stream.h>
+#include <polyp/polyplib-introspect.h>
+#include <polyp/polyplib-subscribe.h>
+#include <polyp/polyplib-scache.h>
+#include <polyp/polyplib-version.h>
+
+/** \file
+ * Include all polyplib header file at once. The following files are included: \ref mainloop-api.h, \ref sample.h,
+ * \ref polyplib-def.h, \ref polyplib-context.h, \ref polyplib-stream.h,
+ * \ref polyplib-introspect.h, \ref polyplib-subscribe.h and \ref polyplib-scache.h \ref polyplib-version.h
+ * at once */
+
+/** \mainpage
+ *
+ * \section intro_sec Introduction
+ *
+ * This document describes the client API for the polypaudio sound
+ * server. The API comes in two flavours:
+ *
+ * \li The complete but somewhat complicated to use asynchronous API
+ * \li And the simplified, easy to use, but limited synchronous API
+ *
+ * The polypaudio client libraries are thread safe as long as all
+ * objects created by any library function are accessed from the thread
+ * that created them only.
+ *
+ * \section simple_sec Simple API
+ *
+ * Use this if you develop your program in synchronous style and just
+ * need a way to play or record data on the sound server. See
+ * \ref polyplib-simple.h for more details.
+ *
+ * \section async_api Asynchronous API
+ *
+ * Use this if you develop your programs in asynchronous, main loop
+ * based style or want to use advanced features of the polypaudio
+ * API. A good starting point is \ref polyplib-context.h
+ *
+ * The asynchronous API relies on an abstract main loop API that is
+ * described in \ref mainloop-api.h. Two distinct implementations are
+ * available:
+ *
+ * \li \ref mainloop.h: a minimal but fast implementation based on poll()
+ * \li \ref glib-mainloop.h: a wrapper around GLIB's main loop
+ *
+ * UNIX signals may be hooked to a main loop using the functions from
+ * \ref mainloop-signal.h
+ *
+ * \section pkgconfig pkg-config
+ *
+ * The polypaudio libraries provide pkg-config snippets for the different modules. To use the
+ * asynchronous API use "polyplib" as pkg-config file. GLIB main loop
+ * support is available as "polyplib-glib-mainloop". The simple
+ * synchronous API is available as "polyplib-simple".
+ */
+
+#endif
diff --git a/src/polyp/sample.c b/src/polyp/sample.c
new file mode 100644
index 00000000..d587170c
--- /dev/null
+++ b/src/polyp/sample.c
@@ -0,0 +1,149 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <math.h>
+#include <string.h>
+
+#include "sample.h"
+
+size_t pa_sample_size(const pa_sample_spec *spec) {
+ assert(spec);
+
+ switch (spec->format) {
+ case PA_SAMPLE_U8:
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
+ return 1;
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ return 2;
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ return 4;
+ default:
+ assert(0);
+ }
+}
+
+size_t pa_frame_size(const pa_sample_spec *spec) {
+ assert(spec);
+
+ return pa_sample_size(spec) * spec->channels;
+}
+
+size_t pa_bytes_per_second(const pa_sample_spec *spec) {
+ assert(spec);
+ return spec->rate*pa_frame_size(spec);
+}
+
+pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
+ assert(spec);
+
+ return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate);
+}
+
+int pa_sample_spec_valid(const pa_sample_spec *spec) {
+ assert(spec);
+
+ if (spec->rate <= 0 ||
+ spec->channels <= 0 ||
+ spec->channels > PA_CHANNELS_MAX ||
+ spec->format >= PA_SAMPLE_MAX ||
+ spec->format < 0)
+ return 0;
+
+ return 1;
+}
+
+int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
+ assert(a && b);
+
+ return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels);
+}
+
+const char *pa_sample_format_to_string(pa_sample_format_t f) {
+ static const char* const table[]= {
+ [PA_SAMPLE_U8] = "u8",
+ [PA_SAMPLE_ALAW] = "aLaw",
+ [PA_SAMPLE_ULAW] = "uLaw",
+ [PA_SAMPLE_S16LE] = "s16le",
+ [PA_SAMPLE_S16BE] = "s16be",
+ [PA_SAMPLE_FLOAT32LE] = "float32le",
+ [PA_SAMPLE_FLOAT32BE] = "float32be",
+ };
+
+ if (f >= PA_SAMPLE_MAX)
+ return NULL;
+
+ return table[f];
+}
+
+char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
+ assert(s && l && spec);
+
+ if (!pa_sample_spec_valid(spec))
+ snprintf(s, l, "Invalid");
+ else
+ snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
+
+ return s;
+}
+
+void pa_bytes_snprint(char *s, size_t l, unsigned v) {
+ if (v >= ((unsigned) 1024)*1024*1024)
+ snprintf(s, l, "%0.1f GB", ((double) v)/1024/1024/1024);
+ else if (v >= ((unsigned) 1024)*1024)
+ snprintf(s, l, "%0.1f MB", ((double) v)/1024/1024);
+ else if (v >= (unsigned) 1024)
+ snprintf(s, l, "%0.1f KB", ((double) v)/1024);
+ else
+ snprintf(s, l, "%u B", (unsigned) v);
+}
+
+pa_sample_format_t pa_parse_sample_format(const char *format) {
+
+ if (strcasecmp(format, "s16le") == 0)
+ return PA_SAMPLE_S16LE;
+ else if (strcasecmp(format, "s16be") == 0)
+ return PA_SAMPLE_S16BE;
+ else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0)
+ return PA_SAMPLE_S16NE;
+ else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0)
+ return PA_SAMPLE_U8;
+ else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0)
+ return PA_SAMPLE_FLOAT32;
+ else if (strcasecmp(format, "float32le") == 0)
+ return PA_SAMPLE_FLOAT32LE;
+ else if (strcasecmp(format, "float32be") == 0)
+ return PA_SAMPLE_FLOAT32BE;
+ else if (strcasecmp(format, "ulaw") == 0)
+ return PA_SAMPLE_ULAW;
+ else if (strcasecmp(format, "alaw") == 0)
+ return PA_SAMPLE_ALAW;
+
+ return -1;
+}
diff --git a/src/polyp/sample.h b/src/polyp/sample.h
new file mode 100644
index 00000000..c1b98f1c
--- /dev/null
+++ b/src/polyp/sample.h
@@ -0,0 +1,120 @@
+#ifndef foosamplehfoo
+#define foosamplehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <math.h>
+
+#include <polyp/cdecl.h>
+
+/** \file
+ * Constants and routines for sample type handling */
+
+PA_C_DECL_BEGIN
+
+/* Maximum allowed channels */
+#define PA_CHANNELS_MAX 16
+
+/** Sample format */
+typedef enum pa_sample_format {
+ PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */
+ PA_SAMPLE_ALAW, /**< 8 Bit a-Law */
+ PA_SAMPLE_ULAW, /**< 8 Bit mu-Law */
+ PA_SAMPLE_S16LE, /**< Signed 16 Bit PCM, little endian (PC) */
+ PA_SAMPLE_S16BE, /**< Signed 16 Bit PCM, big endian */
+ PA_SAMPLE_FLOAT32LE, /**< 32 Bit IEEE floating point, little endian, range -1..1 */
+ PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1..1 */
+ PA_SAMPLE_MAX, /**< Upper limit of valid sample types */
+ PA_SAMPLE_INVALID = -1 /**< An invalid value */
+} pa_sample_format_t;
+
+#ifdef WORDS_BIGENDIAN
+/** Signed 16 Bit PCM, native endian */
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
+/** 32 Bit IEEE floating point, native endian */
+#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
+/** Signed 16 Bit PCM reverse endian */
+#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
+/** 32 Bit IEEE floating point, reverse endian */
+#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
+#else
+/** Signed 16 Bit PCM, native endian */
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
+/** 32 Bit IEEE floating point, native endian */
+#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
+/** Signed 16 Bit PCM reverse endian */
+#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
+/** 32 Bit IEEE floating point, reverse endian */
+#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
+#endif
+
+/** A Shortcut for PA_SAMPLE_FLOAT32NE */
+#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE
+
+/** A sample format and attribute specification */
+typedef struct pa_sample_spec {
+ pa_sample_format_t format; /**< The sample format */
+ uint32_t rate; /**< The sample rate. (e.g. 44100) */
+ uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */
+} pa_sample_spec;
+
+/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */
+typedef uint64_t pa_usec_t;
+
+/** Return the amount of bytes playback of a second of audio with the specified sample type takes */
+size_t pa_bytes_per_second(const pa_sample_spec *spec);
+
+/** Return the size of a frame with the specific sample type */
+size_t pa_frame_size(const pa_sample_spec *spec);
+
+/** Return the size of a sample with the specific sample type */
+size_t pa_sample_size(const pa_sample_spec *spec);
+
+/** Calculate the time the specified bytes take to play with the specified sample type */
+pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec);
+
+/** Return non-zero when the sample type specification is valid */
+int pa_sample_spec_valid(const pa_sample_spec *spec);
+
+/** Return non-zero when the two sample type specifications match */
+int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b);
+
+/* Return a descriptive string for the specified sample format. \since 0.8 */
+const char *pa_sample_format_to_string(pa_sample_format_t f);
+
+/** Parse a sample format text. Inverse of pa_sample_format_to_string() */
+pa_sample_format_t pa_parse_sample_format(const char *format);
+
+/** Maximum required string length for pa_sample_spec_snprint() */
+#define PA_SAMPLE_SPEC_SNPRINT_MAX 32
+
+/** Pretty print a sample type specification to a string */
+char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec);
+
+/** Pretty print a byte size value. (i.e. "2.5 MB") */
+void pa_bytes_snprint(char *s, size_t l, unsigned v);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polyp/volume.c b/src/polyp/volume.c
new file mode 100644
index 00000000..0f153141
--- /dev/null
+++ b/src/polyp/volume.c
@@ -0,0 +1,176 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "volume.h"
+
+int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
+ int i;
+ assert(a);
+ assert(b);
+
+ if (a->channels != b->channels)
+ return 0;
+
+ for (i = 0; i < a->channels; i++)
+ if (a->values[i] != b->values[i])
+ return 0;
+
+ return 1;
+}
+
+pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
+ int i;
+
+ assert(a);
+ assert(channels > 0);
+ assert(channels <= PA_CHANNELS_MAX);
+
+ a->channels = channels;
+
+ for (i = 0; i < a->channels; i++)
+ a->values[i] = v;
+
+ return a;
+}
+
+pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
+ uint64_t sum = 0;
+ int i;
+ assert(a);
+
+ for (i = 0; i < a->channels; i++)
+ sum += a->values[i];
+
+ sum /= a->channels;
+
+ return (pa_volume_t) sum;
+}
+
+pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
+ return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b));
+}
+
+#define USER_DECIBEL_RANGE 30
+
+pa_volume_t pa_sw_volume_from_dB(double dB) {
+ if (dB <= -USER_DECIBEL_RANGE)
+ return PA_VOLUME_MUTED;
+
+ return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM);
+}
+
+double pa_sw_volume_to_dB(pa_volume_t v) {
+ if (v == PA_VOLUME_MUTED)
+ return PA_DECIBEL_MININFTY;
+
+ return ((double) v/PA_VOLUME_NORM-1)*USER_DECIBEL_RANGE;
+}
+
+pa_volume_t pa_sw_volume_from_linear(double v) {
+
+ if (v <= 0)
+ return PA_VOLUME_MUTED;
+
+ if (v > .999 && v < 1.001)
+ return PA_VOLUME_NORM;
+
+ return pa_sw_volume_from_dB(20*log10(v));
+}
+
+double pa_sw_volume_to_linear(pa_volume_t v) {
+
+ if (v == PA_VOLUME_MUTED)
+ return 0;
+
+ return pow(10, pa_sw_volume_to_dB(v)/20);
+}
+
+char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
+ unsigned channel;
+ int first = 1;
+ char *e;
+
+ assert(s);
+ assert(l > 0);
+ assert(c);
+
+ *(e = s) = 0;
+
+ for (channel = 0; channel < c->channels && l > 1; channel++) {
+ l -= snprintf(e, l, "%s%u: %3u%%",
+ first ? "" : " ",
+ channel,
+ (c->values[channel]*100)/PA_VOLUME_NORM);
+
+ e = strchr(e, 0);
+ first = 0;
+ }
+
+ return s;
+}
+
+/** Return non-zero if the volume of all channels is equal to the specified value */
+int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
+ unsigned c;
+ assert(a);
+
+ for (c = 0; c < a->channels; c++)
+ if (a->values[c] != v)
+ return 0;
+
+ return 1;
+}
+
+pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
+ unsigned i;
+
+ assert(dest);
+ assert(a);
+ assert(b);
+
+ for (i = 0; i < a->channels || i < b->channels || i < PA_CHANNELS_MAX; i++) {
+
+ dest->values[i] = pa_sw_volume_multiply(
+ i < a->channels ? a->values[i] : PA_VOLUME_NORM,
+ i < b->channels ? b->values[i] : PA_VOLUME_NORM);
+ }
+
+ dest->channels = i;
+
+ return dest;
+}
+
+int pa_cvolume_valid(const pa_cvolume *v) {
+ assert(v);
+
+ if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX)
+ return 0;
+
+ return 1;
+}
diff --git a/src/polyp/volume.h b/src/polyp/volume.h
new file mode 100644
index 00000000..b2a48084
--- /dev/null
+++ b/src/polyp/volume.h
@@ -0,0 +1,107 @@
+#ifndef foovolumehfoo
+#define foovolumehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <polyp/cdecl.h>
+#include <polyp/sample.h>
+
+/** \file
+ * Constants and routines for volume handling */
+
+PA_C_DECL_BEGIN
+
+/** Volume specification:
+ * PA_VOLUME_MUTED: silence;
+ * < PA_VOLUME_NORM: decreased volume;
+ * PA_VOLUME_NORM: normal volume;
+ * > PA_VOLUME_NORM: increased volume */
+typedef uint32_t pa_volume_t;
+
+/** Normal volume (100%) */
+#define PA_VOLUME_NORM (0x10000)
+
+/** Muted volume (0%) */
+#define PA_VOLUME_MUTED (0)
+
+/** A structure encapsulating a per-channel volume */
+typedef struct pa_cvolume {
+ uint8_t channels;
+ pa_volume_t values[PA_CHANNELS_MAX];
+} pa_cvolume;
+
+/** Return non-zero when *a == *b */
+int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b);
+
+/** Set the volume of all channels to PA_VOLUME_NORM */
+#define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM)
+
+/** Set the volume of all channels to PA_VOLUME_MUTED */
+#define pa_cvolume_mute(a, n) pa_cvolume_set((a), (n), PA_VOLUME_MUTED)
+
+/** Set the volume of all channels to the specified parameter */
+pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v);
+
+/** Pretty print a volume structure */
+#define PA_CVOLUME_SNPRINT_MAX 64
+char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c);
+
+/** Return the average volume of all channels */
+pa_volume_t pa_cvolume_avg(const pa_cvolume *a);
+
+/** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */
+int pa_cvolume_valid(const pa_cvolume *v);
+
+/** Return non-zero if the volume of all channels is equal to the specified value */
+int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v);
+
+#define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED)
+#define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM)
+
+/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. */
+pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b);
+
+pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
+
+/** Convert a decibel value to a volume. \since 0.4 */
+pa_volume_t pa_sw_volume_from_dB(double f);
+
+/** Convert a volume to a decibel value. \since 0.4 */
+double pa_sw_volume_to_dB(pa_volume_t v);
+
+/** Convert a linear factor to a volume. \since 0.8 */
+pa_volume_t pa_sw_volume_from_linear(double v);
+
+/** Convert a volume to a linear factor. \since 0.8 */
+double pa_sw_volume_to_linear(pa_volume_t v);
+
+#ifdef INFINITY
+#define PA_DECIBEL_MININFTY (-INFINITY)
+#else
+/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */
+#define PA_DECIBEL_MININFTY (-200)
+#endif
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polypcore/alsa-util.c b/src/polypcore/alsa-util.c
new file mode 100644
index 00000000..7528ee0b
--- /dev/null
+++ b/src/polypcore/alsa-util.c
@@ -0,0 +1,124 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <sys/types.h>
+#include <asoundlib.h>
+
+#include "alsa-util.h"
+#include <polyp/sample.h>
+#include "xmalloc.h"
+#include "log.h"
+
+/* Set the hardware parameters of the given ALSA device. Returns the
+ * selected fragment settings in *period and *period_size */
+int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size) {
+ int ret = -1;
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_hw_params_t *hwparams = NULL;
+ static const snd_pcm_format_t format_trans[] = {
+ [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8,
+ [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
+ [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
+ [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
+ [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
+ [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
+ [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
+ };
+ assert(pcm_handle && ss && periods && period_size);
+
+ if (snd_pcm_hw_params_malloc(&hwparams) < 0 ||
+ snd_pcm_hw_params_any(pcm_handle, hwparams) < 0 ||
+ snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 ||
+ snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[ss->format]) < 0 ||
+ snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &ss->rate, NULL) < 0 ||
+ snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss->channels) < 0 ||
+ (*periods > 0 && snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0) ||
+ (*period_size > 0 && snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL) < 0) ||
+ snd_pcm_hw_params(pcm_handle, hwparams) < 0)
+ goto finish;
+
+ if (snd_pcm_prepare(pcm_handle) < 0)
+ goto finish;
+
+ if (snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size) < 0 ||
+ snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL) < 0)
+ goto finish;
+
+ assert(buffer_size > 0);
+ assert(*period_size > 0);
+ *periods = buffer_size / *period_size;
+ assert(*periods > 0);
+
+ ret = 0;
+
+finish:
+ if (hwparams)
+ snd_pcm_hw_params_free(hwparams);
+
+ return ret;
+}
+
+/* Allocate an IO event for every ALSA poll descriptor for the
+ * specified ALSA device. Return a pointer to such an array in
+ * *io_events. Store the length of that array in *n_io_events. Use the
+ * specified callback function and userdata. The array has to be freed
+ * with pa_free_io_events(). */
+int pa_create_io_events(snd_pcm_t *pcm_handle, pa_mainloop_api* m, pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata) {
+ unsigned i;
+ struct pollfd *pfds, *ppfd;
+ pa_io_event **ios;
+ assert(pcm_handle && m && io_events && n_io_events && cb);
+
+ *n_io_events = snd_pcm_poll_descriptors_count(pcm_handle);
+
+ pfds = pa_xmalloc(sizeof(struct pollfd) * *n_io_events);
+ if (snd_pcm_poll_descriptors(pcm_handle, pfds, *n_io_events) < 0) {
+ pa_xfree(pfds);
+ return -1;
+ }
+
+ *io_events = pa_xmalloc(sizeof(void*) * *n_io_events);
+
+ for (i = 0, ios = *io_events, ppfd = pfds; i < *n_io_events; i++, ios++, ppfd++) {
+ *ios = m->io_new(m, ppfd->fd,
+ ((ppfd->events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
+ ((ppfd->events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0), cb, userdata);
+ assert(*ios);
+ }
+
+ pa_xfree(pfds);
+ return 0;
+}
+
+/* Free the memory allocated by pa_create_io_events() */
+void pa_free_io_events(pa_mainloop_api* m, pa_io_event **io_events, unsigned n_io_events) {
+ unsigned i;
+ pa_io_event **ios;
+ assert(m && io_events);
+
+ for (ios = io_events, i = 0; i < n_io_events; i++, ios++)
+ m->io_free(*ios);
+ pa_xfree(io_events);
+}
diff --git a/src/polypcore/alsa-util.h b/src/polypcore/alsa-util.h
new file mode 100644
index 00000000..5d6d6634
--- /dev/null
+++ b/src/polypcore/alsa-util.h
@@ -0,0 +1,35 @@
+#ifndef fooalsautilhfoo
+#define fooalsautilhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <asoundlib.h>
+
+#include <polyp/sample.h>
+#include <polyp/mainloop-api.h>
+
+int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size);
+
+int pa_create_io_events(snd_pcm_t *pcm_handle, pa_mainloop_api *m, pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata);
+void pa_free_io_events(pa_mainloop_api* m, pa_io_event **io_sources, unsigned n_io_sources);
+
+#endif
diff --git a/src/polypcore/authkey-prop.c b/src/polypcore/authkey-prop.c
new file mode 100644
index 00000000..8657f5a5
--- /dev/null
+++ b/src/polypcore/authkey-prop.c
@@ -0,0 +1,87 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <assert.h>
+#include <string.h>
+
+#include "xmalloc.h"
+#include "authkey-prop.h"
+#include "props.h"
+#include "log.h"
+
+struct authkey_data {
+ int ref;
+ size_t length;
+};
+
+int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) {
+ struct authkey_data *a;
+ assert(c && name && data && len > 0);
+
+ if (!(a = pa_property_get(c, name)))
+ return -1;
+
+ assert(a->length == len);
+ memcpy(data, a+1, len);
+ return 0;
+}
+
+int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) {
+ struct authkey_data *a;
+ assert(c && name);
+
+ if (pa_property_get(c, name))
+ return -1;
+
+ a = pa_xmalloc(sizeof(struct authkey_data) + len);
+ a->ref = 1;
+ a->length = len;
+ memcpy(a+1, data, len);
+
+ pa_property_set(c, name, a);
+
+ return 0;
+}
+
+void pa_authkey_prop_ref(pa_core *c, const char *name) {
+ struct authkey_data *a;
+ assert(c && name);
+
+ a = pa_property_get(c, name);
+ assert(a && a->ref >= 1);
+
+ a->ref++;
+}
+
+void pa_authkey_prop_unref(pa_core *c, const char *name) {
+ struct authkey_data *a;
+ assert(c && name);
+
+ a = pa_property_get(c, name);
+ assert(a && a->ref >= 1);
+
+ if (!(--a->ref)) {
+ pa_property_remove(c, name);
+ pa_xfree(a);
+ }
+}
+
+
diff --git a/src/polypcore/authkey-prop.h b/src/polypcore/authkey-prop.h
new file mode 100644
index 00000000..29b40bb2
--- /dev/null
+++ b/src/polypcore/authkey-prop.h
@@ -0,0 +1,43 @@
+#ifndef fooauthkeyprophfoo
+#define fooauthkeyprophfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "core.h"
+
+/* The authkey-prop uses a central property to store a previously
+ * loaded cookie in memory. Useful for sharing the same cookie between
+ * several modules. */
+
+/* Return the data of the specified authorization key property. Doesn't alter the refernce count of the key */
+int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len);
+
+/* Store data in the specified authorization key property. The initial reference count is set to 1 */
+int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len);
+
+/* Increase the reference count of the specified authorization key */
+void pa_authkey_prop_ref(pa_core *c, const char *name);
+
+/* Decrease the reference count of the specified authorization key */
+void pa_authkey_prop_unref(pa_core *c, const char *name);
+
+#endif
diff --git a/src/polypcore/authkey.c b/src/polypcore/authkey.c
new file mode 100644
index 00000000..969f09d9
--- /dev/null
+++ b/src/polypcore/authkey.c
@@ -0,0 +1,201 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/stat.h>
+
+#include "authkey.h"
+#include "util.h"
+#include "log.h"
+#include "random.h"
+
+/* Generate a new authorization key, store it in file fd and return it in *data */
+static int generate(int fd, void *ret_data, size_t length) {
+ ssize_t r;
+ assert(fd >= 0 && ret_data && length);
+
+ pa_random(ret_data, length);
+
+ lseek(fd, 0, SEEK_SET);
+ ftruncate(fd, 0);
+
+ if ((r = pa_loop_write(fd, ret_data, length)) < 0 || (size_t) r != length) {
+ pa_log(__FILE__": failed to write cookie file: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Load an euthorization cookie from file fn and store it in data. If
+ * the cookie file doesn't exist, create it */
+static int load(const char *fn, void *data, size_t length) {
+ int fd = -1;
+ int writable = 1;
+ int unlock = 0, ret = -1;
+ ssize_t r;
+ assert(fn && data && length);
+
+ if ((fd = open(fn, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
+ if (errno != EACCES || (fd = open(fn, O_RDONLY)) < 0) {
+ pa_log(__FILE__": failed to open cookie file '%s': %s\n", fn, strerror(errno));
+ goto finish;
+ } else
+ writable = 0;
+ }
+
+ unlock = pa_lock_fd(fd, 1) >= 0;
+
+ if ((r = pa_loop_read(fd, data, length)) < 0) {
+ pa_log(__FILE__": failed to read cookie file '%s': %s\n", fn, strerror(errno));
+ goto finish;
+ }
+
+ if ((size_t) r != length) {
+
+ if (!writable) {
+ pa_log(__FILE__": unable to write cookie to read only file\n");
+ goto finish;
+ }
+
+ if (generate(fd, data, length) < 0)
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+
+ if (fd >= 0) {
+
+ if (unlock)
+ pa_lock_fd(fd, 0);
+
+ close(fd);
+ }
+
+ return ret;
+}
+
+/* Load a cookie from a cookie file. If the file doesn't exist, create it. */
+int pa_authkey_load(const char *path, void *data, size_t length) {
+ int ret;
+
+ assert(path && data && length);
+
+ ret = load(path, data, length);
+
+ if (ret < 0)
+ pa_log(__FILE__": Failed to load authorization key '%s': %s\n", path,
+ (ret == -1) ? strerror(errno) : "file corrupt");
+
+ return ret;
+}
+
+/* If the specified file path starts with / return it, otherwise
+ * return path prepended with home directory */
+static const char *normalize_path(const char *fn, char *s, size_t l) {
+ assert(fn && s && l > 0);
+
+#ifndef OS_IS_WIN32
+ if (fn[0] != '/') {
+#else
+ if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
+#endif
+ char homedir[PATH_MAX];
+ if (!pa_get_home_dir(homedir, sizeof(homedir)))
+ return NULL;
+
+#ifndef OS_IS_WIN32
+ snprintf(s, l, "%s/%s", homedir, fn);
+#else
+ snprintf(s, l, "%s\\%s", homedir, fn);
+#endif
+ return s;
+ }
+
+ return fn;
+}
+
+/* Load a cookie from a file in the home directory. If the specified
+ * path starts with /, use it as absolute path instead. */
+int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
+ char path[PATH_MAX];
+ const char *p;
+ assert(fn && data && length);
+
+ if (!(p = normalize_path(fn, path, sizeof(path))))
+ return -2;
+
+ return pa_authkey_load(p, data, length);
+}
+
+/* Store the specified cookie in the speicified cookie file */
+int pa_authkey_save(const char *fn, const void *data, size_t length) {
+ int fd = -1;
+ int unlock = 0, ret = -1;
+ ssize_t r;
+ char path[PATH_MAX];
+ const char *p;
+ assert(fn && data && length);
+
+ if (!(p = normalize_path(fn, path, sizeof(path))))
+ return -2;
+
+ if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
+ pa_log(__FILE__": failed to open cookie file '%s': %s\n", fn, strerror(errno));
+ goto finish;
+ }
+
+ unlock = pa_lock_fd(fd, 1) >= 0;
+
+ if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) {
+ pa_log(__FILE__": failed to read cookie file '%s': %s\n", fn, strerror(errno));
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+
+ if (fd >= 0) {
+
+ if (unlock)
+ pa_lock_fd(fd, 0);
+
+ close(fd);
+ }
+
+ return ret;
+}
diff --git a/src/polypcore/authkey.h b/src/polypcore/authkey.h
new file mode 100644
index 00000000..81b1a578
--- /dev/null
+++ b/src/polypcore/authkey.h
@@ -0,0 +1,32 @@
+#ifndef fooauthkeyhfoo
+#define fooauthkeyhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+int pa_authkey_load(const char *path, void *data, size_t len);
+int pa_authkey_load_auto(const char *fn, void *data, size_t length);
+
+int pa_authkey_save(const char *path, const void *data, size_t length);
+
+#endif
diff --git a/src/polypcore/autoload.c b/src/polypcore/autoload.c
new file mode 100644
index 00000000..ff2916cb
--- /dev/null
+++ b/src/polypcore/autoload.c
@@ -0,0 +1,180 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "autoload.h"
+#include "module.h"
+#include "xmalloc.h"
+#include "memchunk.h"
+#include "sound-file.h"
+#include "log.h"
+#include "scache.h"
+#include "subscribe.h"
+
+static void entry_free(pa_autoload_entry *e) {
+ assert(e);
+ pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX);
+ pa_xfree(e->name);
+ pa_xfree(e->module);
+ pa_xfree(e->argument);
+ pa_xfree(e);
+}
+
+static void entry_remove_and_free(pa_autoload_entry *e) {
+ assert(e && e->core);
+
+ pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
+ pa_hashmap_remove(e->core->autoload_hashmap, e->name);
+ entry_free(e);
+}
+
+static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
+ pa_autoload_entry *e = NULL;
+ assert(c && name);
+
+ if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name)))
+ return NULL;
+
+ e = pa_xmalloc(sizeof(pa_autoload_entry));
+ e->core = c;
+ e->name = pa_xstrdup(name);
+ e->module = e->argument = NULL;
+ e->in_action = 0;
+
+ if (!c->autoload_hashmap)
+ c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ assert(c->autoload_hashmap);
+
+ pa_hashmap_put(c->autoload_hashmap, e->name, e);
+
+ if (!c->autoload_idxset)
+ c->autoload_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ pa_idxset_put(c->autoload_idxset, e, &e->index);
+
+ pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index);
+
+ return e;
+}
+
+int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) {
+ pa_autoload_entry *e = NULL;
+ assert(c && name && module && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
+
+ if (!(e = entry_new(c, name)))
+ return -1;
+
+ e->module = pa_xstrdup(module);
+ e->argument = pa_xstrdup(argument);
+ e->type = type;
+
+ if (idx)
+ *idx = e->index;
+
+ return 0;
+}
+
+int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
+ pa_autoload_entry *e;
+ assert(c && name && type);
+
+ if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
+ return -1;
+
+ entry_remove_and_free(e);
+ return 0;
+}
+
+int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
+ pa_autoload_entry *e;
+ assert(c && idx != PA_IDXSET_INVALID);
+
+ if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
+ return -1;
+
+ entry_remove_and_free(e);
+ return 0;
+}
+
+void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
+ pa_autoload_entry *e;
+ pa_module *m;
+ assert(c && name);
+
+ if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type))
+ return;
+
+ if (e->in_action)
+ return;
+
+ e->in_action = 1;
+
+ if (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) {
+ if ((m = pa_module_load(c, e->module, e->argument)))
+ m->auto_unload = 1;
+ }
+
+ e->in_action = 0;
+}
+
+static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
+ pa_autoload_entry *e = p;
+ pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
+ entry_free(e);
+}
+
+void pa_autoload_free(pa_core *c) {
+ if (c->autoload_hashmap) {
+ pa_hashmap_free(c->autoload_hashmap, free_func, NULL);
+ c->autoload_hashmap = NULL;
+ }
+
+ if (c->autoload_idxset) {
+ pa_idxset_free(c->autoload_idxset, NULL, NULL);
+ c->autoload_idxset = NULL;
+ }
+}
+
+const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
+ pa_autoload_entry *e;
+ assert(c && name);
+
+ if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
+ return NULL;
+
+ return e;
+}
+
+const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) {
+ pa_autoload_entry *e;
+ assert(c && idx != PA_IDXSET_INVALID);
+
+ if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
+ return NULL;
+
+ return e;
+}
diff --git a/src/polypcore/autoload.h b/src/polypcore/autoload.h
new file mode 100644
index 00000000..7350c16a
--- /dev/null
+++ b/src/polypcore/autoload.h
@@ -0,0 +1,58 @@
+#ifndef fooautoloadhfoo
+#define fooautoloadhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "namereg.h"
+
+/* Using the autoloading facility, modules by be loaded on-demand and
+ * synchronously. The user may register a "ghost sink" or "ghost
+ * source". Whenever this sink/source is requested but not available a
+ * specified module is loaded. */
+
+/* An autoload entry, or "ghost" sink/source */
+typedef struct pa_autoload_entry {
+ pa_core *core;
+ uint32_t index;
+ char *name;
+ pa_namereg_type_t type; /* Type of the autoload entry */
+ int in_action; /* Currently loaded */
+ char *module, *argument;
+} pa_autoload_entry;
+
+/* Add a new autoload entry of the given time, with the speicified
+ * sink/source name, module name and argument. Return the entry's
+ * index in *index */
+int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx);
+
+/* Free all autoload entries */
+void pa_autoload_free(pa_core *c);
+int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
+int pa_autoload_remove_by_index(pa_core *c, uint32_t idx);
+
+/* Request an autoload entry by its name, effectively causing a module to be loaded */
+void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type);
+
+const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
+const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx);
+
+#endif
diff --git a/src/polypcore/cli-command.c b/src/polypcore/cli-command.c
new file mode 100644
index 00000000..f6192bf8
--- /dev/null
+++ b/src/polypcore/cli-command.c
@@ -0,0 +1,834 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "cli-command.h"
+#include "module.h"
+#include "sink.h"
+#include "source.h"
+#include "client.h"
+#include "sink-input.h"
+#include "source-output.h"
+#include "tokenizer.h"
+#include "strbuf.h"
+#include "namereg.h"
+#include "cli-text.h"
+#include "scache.h"
+#include "sample-util.h"
+#include "sound-file.h"
+#include "play-memchunk.h"
+#include "autoload.h"
+#include "xmalloc.h"
+#include "sound-file-stream.h"
+#include "props.h"
+#include "util.h"
+
+struct command {
+ const char *name;
+ int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, int *fail);
+ const char *help;
+ unsigned args;
+};
+
+#define INCLUDE_META ".include"
+#define FAIL_META ".fail"
+#define NOFAIL_META ".nofail"
+
+/* Prototypes for all available commands */
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
+
+
+/* A method table for all available commands */
+
+static const struct command commands[] = {
+ { "exit", pa_cli_command_exit, "Terminate the daemon", 1 },
+ { "help", pa_cli_command_help, "Show this help", 1 },
+ { "list-modules", pa_cli_command_modules, "List loaded modules", 1 },
+ { "list-sinks", pa_cli_command_sinks, "List loaded sinks", 1 },
+ { "list-sources", pa_cli_command_sources, "List loaded sources", 1 },
+ { "list-clients", pa_cli_command_clients, "List loaded clients", 1 },
+ { "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 },
+ { "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 },
+ { "stat", pa_cli_command_stat, "Show memory block statistics", 1 },
+ { "info", pa_cli_command_info, "Show comprehensive status", 1 },
+ { "ls", pa_cli_command_info, NULL, 1 },
+ { "list", pa_cli_command_info, NULL, 1 },
+ { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
+ { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2},
+ { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
+ { "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3},
+ { "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
+ { "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2},
+ { "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
+ { "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2},
+ { "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
+ { "list-samples", pa_cli_command_scache_list, "List all entries in the sample cache", 1},
+ { "play-sample", pa_cli_command_scache_play, "Play a sample from the sample cache (args: name, sink|index)", 3},
+ { "remove-sample", pa_cli_command_scache_remove, "Remove a sample from the sample cache (args: name)", 2},
+ { "load-sample", pa_cli_command_scache_load, "Load a sound file into the sample cache (args: name, filename)", 3},
+ { "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3},
+ { "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
+ { "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
+ { "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1},
+ { "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4},
+ { "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4},
+ { "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2},
+ { "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2},
+ { "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
+ { "list-props", pa_cli_command_list_props, NULL, 1},
+ { NULL, NULL, NULL, 0 }
+};
+
+static const char whitespace[] = " \t\n\r";
+static const char linebreak[] = "\n\r";
+
+static uint32_t parse_index(const char *n) {
+ uint32_t idx;
+
+ if (pa_atou(n, &idx) < 0)
+ return (uint32_t) PA_IDXSET_INVALID;
+
+ return idx;
+}
+
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, PA_GCC_UNUSED pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ assert(c && c->mainloop && t);
+ c->mainloop->quit(c->mainloop, 0);
+ return 0;
+}
+
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ const struct command*command;
+ assert(c && t && buf);
+
+ pa_strbuf_puts(buf, "Available commands:\n");
+
+ for (command = commands; command->name; command++)
+ if (command->help)
+ pa_strbuf_printf(buf, " %-25s %s\n", command->name, command->help);
+ return 0;
+}
+
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ char *s;
+ assert(c && t);
+ s = pa_module_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ pa_xfree(s);
+ return 0;
+}
+
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ char *s;
+ assert(c && t);
+ s = pa_client_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ pa_xfree(s);
+ return 0;
+}
+
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ char *s;
+ assert(c && t);
+ s = pa_sink_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ pa_xfree(s);
+ return 0;
+}
+
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ char *s;
+ assert(c && t);
+ s = pa_source_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ pa_xfree(s);
+ return 0;
+}
+
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ char *s;
+ assert(c && t);
+ s = pa_sink_input_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ pa_xfree(s);
+ return 0;
+}
+
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ char *s;
+ assert(c && t);
+ s = pa_source_output_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ pa_xfree(s);
+ return 0;
+}
+
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ char s[256];
+ assert(c && t);
+
+ pa_bytes_snprint(s, sizeof(s), c->memblock_stat->total_size);
+ pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
+ c->memblock_stat->total,
+ s);
+
+ pa_bytes_snprint(s, sizeof(s), c->memblock_stat->allocated_size);
+ pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
+ c->memblock_stat->allocated,
+ s);
+
+ pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c));
+ pa_strbuf_printf(buf, "Total sample cache size: %s.\n", s);
+
+ pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec);
+ pa_strbuf_printf(buf, "Default sample spec: %s\n", s);
+
+ pa_strbuf_printf(buf, "Default sink name: %s\n"
+ "Default source name: %s\n",
+ pa_namereg_get_default_sink_name(c),
+ pa_namereg_get_default_source_name(c));
+
+ return 0;
+}
+
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ assert(c && t);
+ pa_cli_command_stat(c, t, buf, fail);
+ pa_cli_command_modules(c, t, buf, fail);
+ pa_cli_command_sinks(c, t, buf, fail);
+ pa_cli_command_sources(c, t, buf, fail);
+ pa_cli_command_clients(c, t, buf, fail);
+ pa_cli_command_sink_inputs(c, t, buf, fail);
+ pa_cli_command_source_outputs(c, t, buf, fail);
+ pa_cli_command_scache_list(c, t, buf, fail);
+ pa_cli_command_autoload_list(c, t, buf, fail);
+ return 0;
+}
+
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ pa_module *m;
+ const char *name;
+ assert(c && t);
+
+ if (!(name = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
+ return -1;
+ }
+
+ if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) {
+ pa_strbuf_puts(buf, "Module load failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ pa_module *m;
+ uint32_t idx;
+ const char *i;
+ char *e;
+ assert(c && t);
+
+ if (!(i = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify the module index.\n");
+ return -1;
+ }
+
+ idx = (uint32_t) strtoul(i, &e, 10);
+ if (*e || !(m = pa_idxset_get_by_index(c->modules, idx))) {
+ pa_strbuf_puts(buf, "Invalid module index.\n");
+ return -1;
+ }
+
+ pa_module_unload_request(m);
+ return 0;
+}
+
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ const char *n, *v;
+ pa_sink *sink;
+ uint32_t volume;
+ pa_cvolume cvolume;
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(v = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
+ return -1;
+ }
+
+ if (pa_atou(v, &volume) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse volume.\n");
+ return -1;
+ }
+
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+ pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+ return -1;
+ }
+
+ pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
+ pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume);
+ return 0;
+}
+
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ const char *n, *v;
+ pa_sink_input *si;
+ pa_volume_t volume;
+ pa_cvolume cvolume;
+ uint32_t idx;
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+ return -1;
+ }
+
+ if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+ pa_strbuf_puts(buf, "Failed to parse index.\n");
+ return -1;
+ }
+
+ if (!(v = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
+ return -1;
+ }
+
+ if (pa_atou(v, &volume) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse volume.\n");
+ return -1;
+ }
+
+ if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+ pa_strbuf_puts(buf, "No sink input found with this index.\n");
+ return -1;
+ }
+
+ pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
+ pa_sink_input_set_volume(si, &cvolume);
+ return 0;
+}
+
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ const char *n;
+ assert(c && t);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+ return -1;
+ }
+
+ pa_namereg_set_default(c, n, PA_NAMEREG_SINK);
+ return 0;
+}
+
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ const char *n;
+ assert(c && t);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+ return -1;
+ }
+
+ pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE);
+ return 0;
+}
+
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ const char *n;
+ pa_client *client;
+ uint32_t idx;
+ assert(c && t);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
+ return -1;
+ }
+
+ if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+ pa_strbuf_puts(buf, "Failed to parse index.\n");
+ return -1;
+ }
+
+ if (!(client = pa_idxset_get_by_index(c->clients, idx))) {
+ pa_strbuf_puts(buf, "No client found by this index.\n");
+ return -1;
+ }
+
+ pa_client_kill(client);
+ return 0;
+}
+
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ const char *n;
+ pa_sink_input *sink_input;
+ uint32_t idx;
+ assert(c && t);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+ return -1;
+ }
+
+ if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+ pa_strbuf_puts(buf, "Failed to parse index.\n");
+ return -1;
+ }
+
+ if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) {
+ pa_strbuf_puts(buf, "No sink input found by this index.\n");
+ return -1;
+ }
+
+ pa_sink_input_kill(sink_input);
+ return 0;
+}
+
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ const char *n;
+ pa_source_output *source_output;
+ uint32_t idx;
+ assert(c && t);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+ return -1;
+ }
+
+ if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+ pa_strbuf_puts(buf, "Failed to parse index.\n");
+ return -1;
+ }
+
+ if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) {
+ pa_strbuf_puts(buf, "No source output found by this index.\n");
+ return -1;
+ }
+
+ pa_source_output_kill(source_output);
+ return 0;
+}
+
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ char *s;
+ assert(c && t);
+ s = pa_scache_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ pa_xfree(s);
+ return 0;
+}
+
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *n, *sink_name;
+ pa_sink *sink;
+ assert(c && t && buf && fail);
+
+ if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
+ return -1;
+ }
+
+ if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
+ pa_strbuf_puts(buf, "No sink by that name.\n");
+ return -1;
+ }
+
+ if (pa_scache_play_item(c, n, sink, NULL) < 0) {
+ pa_strbuf_puts(buf, "Failed to play sample.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *n;
+ assert(c && t && buf && fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sample name.\n");
+ return -1;
+ }
+
+ if (pa_scache_remove_item(c, n) < 0) {
+ pa_strbuf_puts(buf, "Failed to remove sample.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *fname, *n;
+ int r;
+ assert(c && t && buf && fail);
+
+ if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
+ return -1;
+ }
+
+ if (strstr(pa_tokenizer_get(t, 0), "lazy"))
+ r = pa_scache_add_file_lazy(c, n, fname, NULL);
+ else
+ r = pa_scache_add_file(c, n, fname, NULL);
+
+ if (r < 0)
+ pa_strbuf_puts(buf, "Failed to load sound file.\n");
+
+ return 0;
+}
+
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *pname;
+ assert(c && t && buf && fail);
+
+ if (!(pname = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a path name.\n");
+ return -1;
+ }
+
+ if (pa_scache_add_directory_lazy(c, pname) < 0) {
+ pa_strbuf_puts(buf, "Failed to load directory.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *fname, *sink_name;
+ pa_sink *sink;
+ assert(c && t && buf && fail);
+
+ if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
+ return -1;
+ }
+
+ if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
+ pa_strbuf_puts(buf, "No sink by that name.\n");
+ return -1;
+ }
+
+
+ return pa_play_file(sink, fname, NULL);
+}
+
+static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *a, *b;
+ assert(c && t && buf && fail);
+
+ if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
+ return -1;
+ }
+
+ pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL);
+
+ return 0;
+}
+
+static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
+ const char *name;
+ assert(c && t && buf && fail);
+
+ if (!(name = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a device name\n");
+ return -1;
+ }
+
+ if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) {
+ pa_strbuf_puts(buf, "Failed to remove autload entry\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ char *s;
+ assert(c && t);
+ s = pa_autoload_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ pa_xfree(s);
+ return 0;
+}
+
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ assert(c && t);
+ pa_property_dump(c, buf);
+ return 0;
+}
+
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
+ pa_module *m;
+ pa_sink *s;
+ int nl;
+ const char *p;
+ uint32_t idx;
+ char txt[256];
+ time_t now;
+ void *i;
+ pa_autoload_entry *a;
+
+ assert(c && t);
+
+ time(&now);
+
+#ifdef HAVE_CTIME_R
+ pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt));
+#else
+ pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
+#endif
+
+
+ for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
+ if (m->auto_unload)
+ continue;
+
+ pa_strbuf_printf(buf, "load-module %s", m->name);
+
+ if (m->argument)
+ pa_strbuf_printf(buf, " %s", m->argument);
+
+ pa_strbuf_puts(buf, "\n");
+ }
+
+ nl = 0;
+
+ for (s = pa_idxset_first(c->sinks, &idx); s; s = pa_idxset_next(c->sinks, &idx)) {
+ if (s->owner && s->owner->auto_unload)
+ continue;
+
+ if (!nl) {
+ pa_strbuf_puts(buf, "\n");
+ nl = 1;
+ }
+
+ pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", s->name, pa_cvolume_avg(pa_sink_get_volume(s, PA_MIXER_HARDWARE)));
+ }
+
+
+ if (c->autoload_hashmap) {
+ nl = 0;
+
+ i = NULL;
+ while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) {
+
+ if (!nl) {
+ pa_strbuf_puts(buf, "\n");
+ nl = 1;
+ }
+
+ pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module);
+
+ if (a->argument)
+ pa_strbuf_printf(buf, " %s", a->argument);
+
+ pa_strbuf_puts(buf, "\n");
+ }
+ }
+
+ nl = 0;
+
+ if ((p = pa_namereg_get_default_sink_name(c))) {
+ if (!nl) {
+ pa_strbuf_puts(buf, "\n");
+ nl = 1;
+ }
+ pa_strbuf_printf(buf, "set-default-sink %s\n", p);
+ }
+
+ if ((p = pa_namereg_get_default_source_name(c))) {
+ if (!nl) {
+ pa_strbuf_puts(buf, "\n");
+ nl = 1;
+ }
+ pa_strbuf_printf(buf, "set-default-source %s\n", p);
+ }
+
+ pa_strbuf_puts(buf, "\n### EOF\n");
+
+ return 0;
+}
+
+
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
+ const char *cs;
+
+ cs = s+strspn(s, whitespace);
+
+ if (*cs == '#' || !*cs)
+ return 0;
+ else if (*cs == '.') {
+ if (!strcmp(cs, FAIL_META))
+ *fail = 1;
+ else if (!strcmp(cs, NOFAIL_META))
+ *fail = 0;
+ else {
+ size_t l;
+ l = strcspn(cs, whitespace);
+
+ if (l == sizeof(INCLUDE_META)-1 && !strncmp(cs, INCLUDE_META, l)) {
+ const char *filename = cs+l+strspn(cs+l, whitespace);
+
+ if (pa_cli_command_execute_file(c, filename, buf, fail) < 0)
+ if (*fail) return -1;
+ } else {
+ pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
+ if (*fail) return -1;
+ }
+ }
+ } else {
+ const struct command*command;
+ int unknown = 1;
+ size_t l;
+
+ l = strcspn(cs, whitespace);
+
+ for (command = commands; command->name; command++)
+ if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
+ int ret;
+ pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
+ assert(t);
+ ret = command->proc(c, t, buf, fail);
+ pa_tokenizer_free(t);
+ unknown = 0;
+
+ if (ret < 0 && *fail)
+ return -1;
+
+ break;
+ }
+
+ if (unknown) {
+ pa_strbuf_printf(buf, "Unknown command: %s\n", cs);
+ if (*fail)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail) {
+ char line[256];
+ FILE *f = NULL;
+ int ret = -1;
+ assert(c && fn && buf);
+
+ if (!(f = fopen(fn, "r"))) {
+ pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, strerror(errno));
+ if (!*fail)
+ ret = 0;
+ goto fail;
+ }
+
+ while (fgets(line, sizeof(line), f)) {
+ char *e = line + strcspn(line, linebreak);
+ *e = 0;
+
+ if (pa_cli_command_execute_line(c, line, buf, fail) < 0 && *fail)
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+ if (f)
+ fclose(f);
+
+ return ret;
+}
+
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
+ const char *p;
+ assert(c && s && buf && fail);
+
+ p = s;
+ while (*p) {
+ size_t l = strcspn(p, linebreak);
+ char *line = pa_xstrndup(p, l);
+
+ if (pa_cli_command_execute_line(c, line, buf, fail) < 0&& *fail) {
+ pa_xfree(line);
+ return -1;
+ }
+ pa_xfree(line);
+
+ p += l;
+ p += strspn(p, linebreak);
+ }
+
+ return 0;
+}
diff --git a/src/polypcore/cli-command.h b/src/polypcore/cli-command.h
new file mode 100644
index 00000000..78b8d5c6
--- /dev/null
+++ b/src/polypcore/cli-command.h
@@ -0,0 +1,40 @@
+#ifndef fooclicommandhfoo
+#define fooclicommandhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "strbuf.h"
+#include "core.h"
+
+/* Execute a single CLI command. Write the results to the string
+ * buffer *buf. If *fail is non-zero the function will return -1 when
+ * one or more of the executed commands failed. *fail
+ * may be modified by the function call. */
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
+
+/* Execute a whole file of CLI commands */
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail);
+
+/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
+
+#endif
diff --git a/src/polypcore/cli-text.c b/src/polypcore/cli-text.c
new file mode 100644
index 00000000..58248d8e
--- /dev/null
+++ b/src/polypcore/cli-text.c
@@ -0,0 +1,385 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <string.h>
+
+#include "cli-text.h"
+#include "module.h"
+#include "client.h"
+#include "sink.h"
+#include "source.h"
+#include "sink-input.h"
+#include "source-output.h"
+#include "strbuf.h"
+#include "sample-util.h"
+#include "scache.h"
+#include "autoload.h"
+#include "xmalloc.h"
+#include <polyp/volume.h>
+
+char *pa_module_list_to_string(pa_core *c) {
+ pa_strbuf *s;
+ pa_module *m;
+ uint32_t idx = PA_IDXSET_INVALID;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules));
+
+ for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx))
+ pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n\tused: %i\n\tauto unload: %s\n", m->index, m->name, m->argument, m->n_used, m->auto_unload ? "yes" : "no");
+
+ return pa_strbuf_tostring_free(s);
+}
+
+char *pa_client_list_to_string(pa_core *c) {
+ pa_strbuf *s;
+ pa_client *client;
+ uint32_t idx = PA_IDXSET_INVALID;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
+
+ for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) {
+ pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver);
+
+ if (client->owner)
+ pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index);
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
+
+char *pa_sink_list_to_string(pa_core *c) {
+ pa_strbuf *s;
+ pa_sink *sink;
+ uint32_t idx = PA_IDXSET_INVALID;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
+
+ for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ pa_strbuf_printf(
+ s,
+ " %c index: %u\n"
+ "\tname: <%s>\n"
+ "\tdriver: <%s>\n"
+ "\tvolume: <%s>\n"
+ "\tlatency: <%0.0f usec>\n"
+ "\tmonitor_source: <%u>\n"
+ "\tsample spec: <%s>\n"
+ "\tchannel map: <%s>\n",
+ c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
+ sink->index, sink->name,
+ sink->driver,
+ pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, PA_MIXER_HARDWARE)),
+ (double) pa_sink_get_latency(sink),
+ sink->monitor_source->index,
+ pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map));
+
+ if (sink->owner)
+ pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index);
+ if (sink->description)
+ pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description);
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
+
+char *pa_source_list_to_string(pa_core *c) {
+ pa_strbuf *s;
+ pa_source *source;
+ uint32_t idx = PA_IDXSET_INVALID;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));
+
+ for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+
+ pa_strbuf_printf(
+ s,
+ " %c index: %u\n"
+ "\tname: <%s>\n"
+ "\tdriver: <%s>\n"
+ "\tlatency: <%0.0f usec>\n"
+ "\tsample spec: <%s>\n"
+ "\tchannel map: <%s>\n",
+ c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
+ source->index,
+ source->name,
+ source->driver,
+ (double) pa_source_get_latency(source),
+ pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map));
+
+ if (source->monitor_of)
+ pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index);
+ if (source->owner)
+ pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index);
+ if (source->description)
+ pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description);
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
+
+
+char *pa_source_output_list_to_string(pa_core *c) {
+ pa_strbuf *s;
+ pa_source_output *o;
+ uint32_t idx = PA_IDXSET_INVALID;
+ static const char* const state_table[] = {
+ "RUNNING",
+ "CORKED",
+ "DISCONNECTED"
+ };
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs));
+
+ for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ assert(o->source);
+
+ pa_strbuf_printf(
+ s,
+ " index: %u\n"
+ "\tname: '%s'\n"
+ "\tdriver: <%s>\n"
+ "\tstate: %s\n"
+ "\tsource: <%u> '%s'\n"
+ "\tsample spec: <%s>\n"
+ "\tchannel map: <%s>\n"
+ "\tresample method: %s\n",
+ o->index,
+ o->name,
+ o->driver,
+ state_table[o->state],
+ o->source->index, o->source->name,
+ pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
+ pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
+ if (o->owner)
+ pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index);
+ if (o->client)
+ pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name);
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
+
+char *pa_sink_input_list_to_string(pa_core *c) {
+ pa_strbuf *s;
+ pa_sink_input *i;
+ uint32_t idx = PA_IDXSET_INVALID;
+ static const char* const state_table[] = {
+ "RUNNING",
+ "CORKED",
+ "DISCONNECTED"
+ };
+
+ assert(c);
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
+
+ for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ assert(i->sink);
+
+ pa_strbuf_printf(
+ s,
+ " index: %u\n"
+ "\tname: <%s>\n"
+ "\tdriver: <%s>\n"
+ "\tstate: %s\n"
+ "\tsink: <%u> '%s'\n"
+ "\tvolume: <%s>\n"
+ "\tlatency: <%0.0f usec>\n"
+ "\tsample spec: <%s>\n"
+ "\tchannel map: <%s>\n"
+ "\tresample method: %s\n",
+ i->index,
+ i->name,
+ i->driver,
+ state_table[i->state],
+ i->sink->index, i->sink->name,
+ pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
+ (double) pa_sink_input_get_latency(i),
+ pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+ pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
+
+ if (i->owner)
+ pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index);
+ if (i->client)
+ pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name);
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
+
+char *pa_scache_list_to_string(pa_core *c) {
+ pa_strbuf *s;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
+
+ if (c->scache) {
+ pa_scache_entry *e;
+ uint32_t idx = PA_IDXSET_INVALID;
+
+ for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
+ double l = 0;
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ if (e->memchunk.memblock) {
+ pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
+ pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map);
+ l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec);
+ }
+
+ pa_strbuf_printf(
+ s,
+ " name: <%s>\n"
+ "\tindex: <%u>\n"
+ "\tsample spec: <%s>\n"
+ "\tchannel map: <%s>\n"
+ "\tlength: <%u>\n"
+ "\tduration: <%0.1fs>\n"
+ "\tvolume: <%s>\n"
+ "\tlazy: %s\n"
+ "\tfilename: %s\n",
+ e->name,
+ e->index,
+ ss,
+ cm,
+ e->memchunk.memblock ? e->memchunk.length : 0,
+ l,
+ pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
+ e->lazy ? "yes" : "no",
+ e->filename ? e->filename : "n/a");
+ }
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
+
+char *pa_autoload_list_to_string(pa_core *c) {
+ pa_strbuf *s;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0);
+
+ if (c->autoload_hashmap) {
+ pa_autoload_entry *e;
+ void *state = NULL;
+
+ while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
+ pa_strbuf_printf(
+ s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n",
+ e->name,
+ e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
+ e->index,
+ e->module,
+ e->argument);
+
+ }
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
+
+char *pa_full_status_string(pa_core *c) {
+ pa_strbuf *s;
+ int i;
+
+ s = pa_strbuf_new();
+
+ for (i = 0; i < 8; i++) {
+ char *t = NULL;
+
+ switch (i) {
+ case 0:
+ t = pa_sink_list_to_string(c);
+ break;
+ case 1:
+ t = pa_source_list_to_string(c);
+ break;
+ case 2:
+ t = pa_sink_input_list_to_string(c);
+ break;
+ case 3:
+ t = pa_source_output_list_to_string(c);
+ break;
+ case 4:
+ t = pa_client_list_to_string(c);
+ break;
+ case 5:
+ t = pa_module_list_to_string(c);
+ break;
+ case 6:
+ t = pa_scache_list_to_string(c);
+ break;
+ case 7:
+ t = pa_autoload_list_to_string(c);
+ break;
+ }
+
+ pa_strbuf_puts(s, t);
+ pa_xfree(t);
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
diff --git a/src/polypcore/cli-text.h b/src/polypcore/cli-text.h
new file mode 100644
index 00000000..7a1a0361
--- /dev/null
+++ b/src/polypcore/cli-text.h
@@ -0,0 +1,42 @@
+#ifndef fooclitexthfoo
+#define fooclitexthfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "core.h"
+
+/* Some functions to generate pretty formatted listings of
+ * entities. The returned strings have to be freed manually. */
+
+char *pa_sink_input_list_to_string(pa_core *c);
+char *pa_source_output_list_to_string(pa_core *c);
+char *pa_sink_list_to_string(pa_core *core);
+char *pa_source_list_to_string(pa_core *c);
+char *pa_client_list_to_string(pa_core *c);
+char *pa_module_list_to_string(pa_core *c);
+char *pa_scache_list_to_string(pa_core *c);
+char *pa_autoload_list_to_string(pa_core *c);
+
+char *pa_full_status_string(pa_core *c);
+
+#endif
+
diff --git a/src/polypcore/cli.c b/src/polypcore/cli.c
new file mode 100644
index 00000000..bc0c285d
--- /dev/null
+++ b/src/polypcore/cli.c
@@ -0,0 +1,147 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "ioline.h"
+#include "cli.h"
+#include "module.h"
+#include "sink.h"
+#include "source.h"
+#include "client.h"
+#include "sink-input.h"
+#include "source-output.h"
+#include "tokenizer.h"
+#include "strbuf.h"
+#include "namereg.h"
+#include "cli-text.h"
+#include "cli-command.h"
+#include "xmalloc.h"
+#include "log.h"
+
+#define PROMPT ">>> "
+
+struct pa_cli {
+ pa_core *core;
+ pa_ioline *line;
+
+ void (*eof_callback)(pa_cli *c, void *userdata);
+ void *userdata;
+
+ pa_client *client;
+
+ int fail, kill_requested, defer_kill;
+};
+
+static void line_callback(pa_ioline *line, const char *s, void *userdata);
+static void client_kill(pa_client *c);
+
+pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
+ char cname[256];
+ pa_cli *c;
+ assert(io);
+
+ c = pa_xmalloc(sizeof(pa_cli));
+ c->core = core;
+ c->line = pa_ioline_new(io);
+ assert(c->line);
+
+ c->userdata = NULL;
+ c->eof_callback = NULL;
+
+ pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
+ c->client = pa_client_new(core, __FILE__, cname);
+ assert(c->client);
+ c->client->kill = client_kill;
+ c->client->userdata = c;
+ c->client->owner = m;
+
+ pa_ioline_set_callback(c->line, line_callback, c);
+ pa_ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n"PROMPT);
+
+ c->fail = c->kill_requested = c->defer_kill = 0;
+
+ return c;
+}
+
+void pa_cli_free(pa_cli *c) {
+ assert(c);
+ pa_ioline_close(c->line);
+ pa_ioline_unref(c->line);
+ pa_client_free(c->client);
+ pa_xfree(c);
+}
+
+static void client_kill(pa_client *client) {
+ pa_cli *c;
+ assert(client && client->userdata);
+ c = client->userdata;
+
+ pa_log_debug(__FILE__": CLI client killed.\n");
+ if (c->defer_kill)
+ c->kill_requested = 1;
+ else {
+ if (c->eof_callback)
+ c->eof_callback(c, c->userdata);
+ }
+}
+
+static void line_callback(pa_ioline *line, const char *s, void *userdata) {
+ pa_strbuf *buf;
+ pa_cli *c = userdata;
+ char *p;
+ assert(line && c);
+
+ if (!s) {
+ pa_log_debug(__FILE__": CLI got EOF from user.\n");
+ if (c->eof_callback)
+ c->eof_callback(c, c->userdata);
+
+ return;
+ }
+
+ buf = pa_strbuf_new();
+ assert(buf);
+ c->defer_kill++;
+ pa_cli_command_execute_line(c->core, s, buf, &c->fail);
+ c->defer_kill--;
+ pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf));
+ pa_xfree(p);
+
+ if (c->kill_requested) {
+ if (c->eof_callback)
+ c->eof_callback(c, c->userdata);
+ } else
+ pa_ioline_puts(line, PROMPT);
+}
+
+void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) {
+ assert(c);
+ c->eof_callback = cb;
+ c->userdata = userdata;
+}
diff --git a/src/polypcore/cli.h b/src/polypcore/cli.h
new file mode 100644
index 00000000..03f31c22
--- /dev/null
+++ b/src/polypcore/cli.h
@@ -0,0 +1,38 @@
+#ifndef fooclihfoo
+#define fooclihfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "iochannel.h"
+#include "core.h"
+#include "module.h"
+
+typedef struct pa_cli pa_cli;
+
+/* Create a new command line session on the specified io channel owned by the specified module */
+pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m);
+void pa_cli_free(pa_cli *cli);
+
+/* Set a callback function that is called whenever the command line session is terminated */
+void pa_cli_set_eof_callback(pa_cli *cli, void (*cb)(pa_cli*c, void *userdata), void *userdata);
+
+#endif
diff --git a/src/polypcore/client.c b/src/polypcore/client.c
new file mode 100644
index 00000000..3c2084bf
--- /dev/null
+++ b/src/polypcore/client.c
@@ -0,0 +1,91 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "client.h"
+#include "xmalloc.h"
+#include "subscribe.h"
+#include "log.h"
+
+pa_client *pa_client_new(pa_core *core, const char *name, const char *driver) {
+ pa_client *c;
+ int r;
+ assert(core);
+
+ c = pa_xmalloc(sizeof(pa_client));
+ c->name = pa_xstrdup(name);
+ c->driver = pa_xstrdup(driver);
+ c->owner = NULL;
+ c->core = core;
+
+ c->kill = NULL;
+ c->userdata = NULL;
+
+ r = pa_idxset_put(core->clients, c, &c->index);
+ assert(c->index != PA_IDXSET_INVALID && r >= 0);
+
+ pa_log_info(__FILE__": created %u \"%s\"\n", c->index, c->name);
+ pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
+
+ pa_core_check_quit(core);
+
+ return c;
+}
+
+void pa_client_free(pa_client *c) {
+ assert(c && c->core);
+
+ pa_idxset_remove_by_data(c->core->clients, c, NULL);
+
+ pa_core_check_quit(c->core);
+
+ pa_log_info(__FILE__": freed %u \"%s\"\n", c->index, c->name);
+ pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
+ pa_xfree(c->name);
+ pa_xfree(c->driver);
+ pa_xfree(c);
+}
+
+void pa_client_kill(pa_client *c) {
+ assert(c);
+ if (!c->kill) {
+ pa_log_warn(__FILE__": kill() operation not implemented for client %u\n", c->index);
+ return;
+ }
+
+ c->kill(c);
+}
+
+void pa_client_set_name(pa_client *c, const char *name) {
+ assert(c);
+ pa_xfree(c->name);
+ c->name = pa_xstrdup(name);
+
+ pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
+}
diff --git a/src/polypcore/client.h b/src/polypcore/client.h
new file mode 100644
index 00000000..92430338
--- /dev/null
+++ b/src/polypcore/client.h
@@ -0,0 +1,57 @@
+#ifndef fooclienthfoo
+#define fooclienthfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "core.h"
+#include "module.h"
+
+/* Every connection to the server should have a pa_client
+ * attached. That way the user may generate a listing of all connected
+ * clients easily and kill them if he wants.*/
+
+typedef struct pa_client pa_client;
+
+struct pa_client {
+ uint32_t index;
+
+ pa_module *owner;
+ char *name, *driver;
+ pa_core *core;
+
+ void (*kill)(pa_client *c);
+ void *userdata;
+};
+
+pa_client *pa_client_new(pa_core *c, const char *name, const char *driver);
+
+/* This function should be called only by the code that created the client */
+void pa_client_free(pa_client *c);
+
+/* Code that didn't create the client should call this function to
+ * request destruction of the client */
+void pa_client_kill(pa_client *c);
+
+/* Rename the client */
+void pa_client_set_name(pa_client *c, const char *name);
+
+#endif
diff --git a/src/polypcore/conf-parser.c b/src/polypcore/conf-parser.c
new file mode 100644
index 00000000..507f2bf1
--- /dev/null
+++ b/src/polypcore/conf-parser.c
@@ -0,0 +1,176 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "conf-parser.h"
+#include "log.h"
+#include "util.h"
+#include "xmalloc.h"
+
+#define WHITESPACE " \t\n"
+#define COMMENTS "#;\n"
+
+/* Run the user supplied parser for an assignment */
+static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
+ assert(filename && t && lvalue && rvalue);
+
+ for (; t->parse; t++)
+ if (!strcmp(lvalue, t->lvalue))
+ return t->parse(filename, line, lvalue, rvalue, t->data, userdata);
+
+ pa_log(__FILE__": [%s:%u] Unknown lvalue '%s'.\n", filename, line, lvalue);
+
+ return -1;
+}
+
+/* Returns non-zero when c is contained in s */
+static int in_string(char c, const char *s) {
+ assert(s);
+
+ for (; *s; s++)
+ if (*s == c)
+ return 1;
+
+ return 0;
+}
+
+/* Remove all whitepsapce from the beginning and the end of *s. *s may
+ * be modified. */
+static char *strip(char *s) {
+ char *b = s+strspn(s, WHITESPACE);
+ char *e, *l = NULL;
+
+ for (e = b; *e; e++)
+ if (!in_string(*e, WHITESPACE))
+ l = e;
+
+ if (l)
+ *(l+1) = 0;
+
+ return b;
+}
+
+/* Parse a variable assignment line */
+static int parse_line(const char *filename, unsigned line, const pa_config_item *t, char *l, void *userdata) {
+ char *e, *c, *b = l+strspn(l, WHITESPACE);
+
+ if ((c = strpbrk(b, COMMENTS)))
+ *c = 0;
+
+ if (!*b)
+ return 0;
+
+ if (!(e = strchr(b, '='))) {
+ pa_log(__FILE__": [%s:%u] Missing '='.\n", filename, line);
+ return -1;
+ }
+
+ *e = 0;
+ e++;
+
+ return next_assignment(filename, line, t, strip(b), strip(e), userdata);
+}
+
+/* Go through the file and parse each line */
+int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata) {
+ int r = -1;
+ unsigned line = 0;
+ int do_close = !f;
+ assert(filename && t);
+
+ if (!f && !(f = fopen(filename, "r"))) {
+ if (errno == ENOENT) {
+ r = 0;
+ goto finish;
+ }
+
+ pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno));
+ goto finish;
+ }
+
+ while (!feof(f)) {
+ char l[256];
+ if (!fgets(l, sizeof(l), f)) {
+ if (feof(f))
+ break;
+
+ pa_log(__FILE__": WARNING: failed to read configuration file '%s': %s\n", filename, strerror(errno));
+ goto finish;
+ }
+
+ if (parse_line(filename, ++line, t, l, userdata) < 0)
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+
+ if (do_close && f)
+ fclose(f);
+
+ return r;
+}
+
+int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ int *i = data;
+ int32_t k;
+ assert(filename && lvalue && rvalue && data);
+
+ if (pa_atoi(rvalue, &k) < 0) {
+ pa_log(__FILE__": [%s:%u] Failed to parse numeric value: %s\n", filename, line, rvalue);
+ return -1;
+ }
+
+ *i = (int) k;
+ return 0;
+}
+
+int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ int *b = data, k;
+ assert(filename && lvalue && rvalue && data);
+
+ if ((k = pa_parse_boolean(rvalue)) < 0) {
+ pa_log(__FILE__": [%s:%u] Failed to parse boolean value: %s\n", filename, line, rvalue);
+ return -1;
+ }
+
+ *b = k;
+
+ return 0;
+}
+
+int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+ char **s = data;
+ assert(filename && lvalue && rvalue && data);
+
+ pa_xfree(*s);
+ *s = *rvalue ? pa_xstrdup(rvalue) : NULL;
+ return 0;
+}
diff --git a/src/polypcore/conf-parser.h b/src/polypcore/conf-parser.h
new file mode 100644
index 00000000..2dca3bce
--- /dev/null
+++ b/src/polypcore/conf-parser.h
@@ -0,0 +1,47 @@
+#ifndef fooconfparserhfoo
+#define fooconfparserhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <stdio.h>
+
+/* An abstract parser for simple, line based, shallow configuration
+ * files consisting of variable assignments only. */
+
+/* Wraps info for parsing a specific configuration variable */
+typedef struct pa_config_item {
+ const char *lvalue; /* name of the variable */
+ int (*parse)(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); /* Function that is called to parse the variable's value */
+ void *data; /* Where to store the variable's data */
+} pa_config_item;
+
+/* The configuration file parsing routine. Expects a table of
+ * pa_config_items in *t that is terminated by an item where lvalue is
+ * NULL */
+int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata);
+
+/* Generic parsers for integers, booleans and strings */
+int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
+
+#endif
diff --git a/src/polypcore/core.c b/src/polypcore/core.c
new file mode 100644
index 00000000..678e8212
--- /dev/null
+++ b/src/polypcore/core.c
@@ -0,0 +1,157 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include "core.h"
+#include "module.h"
+#include "sink.h"
+#include "source.h"
+#include "namereg.h"
+#include "util.h"
+#include "scache.h"
+#include "autoload.h"
+#include "xmalloc.h"
+#include "subscribe.h"
+#include "props.h"
+#include "random.h"
+
+pa_core* pa_core_new(pa_mainloop_api *m) {
+ pa_core* c;
+ c = pa_xmalloc(sizeof(pa_core));
+
+ c->mainloop = m;
+ c->clients = pa_idxset_new(NULL, NULL);
+ c->sinks = pa_idxset_new(NULL, NULL);
+ c->sources = pa_idxset_new(NULL, NULL);
+ c->source_outputs = pa_idxset_new(NULL, NULL);
+ c->sink_inputs = pa_idxset_new(NULL, NULL);
+
+ c->default_source_name = c->default_sink_name = NULL;
+
+ c->modules = NULL;
+ c->namereg = NULL;
+ c->scache = NULL;
+ c->autoload_idxset = NULL;
+ c->autoload_hashmap = NULL;
+ c->running_as_daemon = 0;
+
+ c->default_sample_spec.format = PA_SAMPLE_S16NE;
+ c->default_sample_spec.rate = 44100;
+ c->default_sample_spec.channels = 2;
+
+ c->module_auto_unload_event = NULL;
+ c->module_defer_unload_event = NULL;
+ c->scache_auto_unload_event = NULL;
+
+ c->subscription_defer_event = NULL;
+ c->subscription_event_queue = NULL;
+ c->subscriptions = NULL;
+
+ c->memblock_stat = pa_memblock_stat_new();
+
+ c->disallow_module_loading = 0;
+
+ c->quit_event = NULL;
+
+ c->exit_idle_time = -1;
+ c->module_idle_time = 20;
+ c->scache_idle_time = 20;
+
+ c->resample_method = PA_RESAMPLER_SRC_SINC_FASTEST;
+
+ pa_property_init(c);
+
+ pa_random(&c->cookie, sizeof(c->cookie));
+
+#ifdef SIGPIPE
+ pa_check_signal_is_blocked(SIGPIPE);
+#endif
+ return c;
+}
+
+void pa_core_free(pa_core *c) {
+ assert(c);
+
+ pa_module_unload_all(c);
+ assert(!c->modules);
+
+ assert(pa_idxset_isempty(c->clients));
+ pa_idxset_free(c->clients, NULL, NULL);
+
+ assert(pa_idxset_isempty(c->sinks));
+ pa_idxset_free(c->sinks, NULL, NULL);
+
+ assert(pa_idxset_isempty(c->sources));
+ pa_idxset_free(c->sources, NULL, NULL);
+
+ assert(pa_idxset_isempty(c->source_outputs));
+ pa_idxset_free(c->source_outputs, NULL, NULL);
+
+ assert(pa_idxset_isempty(c->sink_inputs));
+ pa_idxset_free(c->sink_inputs, NULL, NULL);
+
+ pa_scache_free(c);
+ pa_namereg_free(c);
+ pa_autoload_free(c);
+ pa_subscription_free_all(c);
+
+ if (c->quit_event)
+ c->mainloop->time_free(c->quit_event);
+
+ pa_xfree(c->default_source_name);
+ pa_xfree(c->default_sink_name);
+
+ pa_memblock_stat_unref(c->memblock_stat);
+
+ pa_property_cleanup(c);
+
+ pa_xfree(c);
+}
+
+static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+ pa_core *c = userdata;
+ assert(c->quit_event = e);
+
+ m->quit(m, 0);
+}
+
+void pa_core_check_quit(pa_core *c) {
+ assert(c);
+
+ if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ tv.tv_sec+= c->exit_idle_time;
+ c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c);
+ } else if (c->quit_event && pa_idxset_size(c->clients) > 0) {
+ c->mainloop->time_free(c->quit_event);
+ c->quit_event = NULL;
+ }
+}
+
diff --git a/src/polypcore/core.h b/src/polypcore/core.h
new file mode 100644
index 00000000..f7a90169
--- /dev/null
+++ b/src/polypcore/core.h
@@ -0,0 +1,82 @@
+#ifndef foocorehfoo
+#define foocorehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct pa_core pa_core;
+
+#include "idxset.h"
+#include "hashmap.h"
+#include <polyp/mainloop-api.h>
+#include <polyp/sample.h>
+#include "memblock.h"
+#include "resampler.h"
+#include "queue.h"
+#include "subscribe.h"
+
+/* The core structure of polypaudio. Every polypaudio daemon contains
+ * exactly one of these. It is used for storing kind of global
+ * variables for the daemon. */
+
+struct pa_core {
+ /* A random value which may be used to identify this instance of
+ * polypaudio. Not cryptographically secure in any way. */
+ uint32_t cookie;
+
+ pa_mainloop_api *mainloop;
+
+ /* idxset of all kinds of entities */
+ pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset;
+
+ /* Some hashmaps for all sorts of entities */
+ pa_hashmap *namereg, *autoload_hashmap, *properties;
+
+ /* The name of the default sink/source */
+ char *default_source_name, *default_sink_name;
+
+ pa_sample_spec default_sample_spec;
+ pa_time_event *module_auto_unload_event;
+ pa_defer_event *module_defer_unload_event;
+
+ pa_defer_event *subscription_defer_event;
+ pa_queue *subscription_event_queue;
+ pa_subscription *subscriptions;
+
+ pa_memblock_stat *memblock_stat;
+
+ int disallow_module_loading, running_as_daemon;
+ int exit_idle_time, module_idle_time, scache_idle_time;
+
+ pa_time_event *quit_event;
+
+ pa_time_event *scache_auto_unload_event;
+
+ pa_resample_method_t resample_method;
+};
+
+pa_core* pa_core_new(pa_mainloop_api *m);
+void pa_core_free(pa_core*c);
+
+/* Check whether noone is connected to this core */
+void pa_core_check_quit(pa_core *c);
+
+#endif
diff --git a/src/polypcore/dllmain.c b/src/polypcore/dllmain.c
new file mode 100644
index 00000000..d1d120ab
--- /dev/null
+++ b/src/polypcore/dllmain.c
@@ -0,0 +1,46 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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
+
+#ifdef OS_IS_WIN32
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <windows.h>
+
+extern pa_set_root(HANDLE handle);
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
+ if (fdwReason != DLL_PROCESS_ATTACH)
+ return TRUE;
+
+ if (!pa_set_root(hinstDLL))
+ return FALSE;
+
+ return TRUE;
+}
+
+#endif /* OS_IS_WIN32 */
diff --git a/src/polypcore/dynarray.c b/src/polypcore/dynarray.c
new file mode 100644
index 00000000..435fd768
--- /dev/null
+++ b/src/polypcore/dynarray.c
@@ -0,0 +1,102 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <assert.h>
+#include <stdlib.h>
+
+#include "dynarray.h"
+#include "xmalloc.h"
+
+/* If the array becomes to small, increase its size by 100 entries */
+#define INCREASE_BY 100
+
+struct pa_dynarray {
+ void **data;
+ unsigned n_allocated, n_entries;
+};
+
+pa_dynarray* pa_dynarray_new(void) {
+ pa_dynarray *a;
+ a = pa_xnew(pa_dynarray, 1);
+ a->data = NULL;
+ a->n_entries = 0;
+ a->n_allocated = 0;
+ return a;
+}
+
+void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) {
+ unsigned i;
+ assert(a);
+
+ if (func)
+ for (i = 0; i < a->n_entries; i++)
+ if (a->data[i])
+ func(a->data[i], userdata);
+
+ pa_xfree(a->data);
+ pa_xfree(a);
+}
+
+void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) {
+ assert(a);
+
+ if (i >= a->n_allocated) {
+ unsigned n;
+
+ if (!p)
+ return;
+
+ n = i+INCREASE_BY;
+ a->data = pa_xrealloc(a->data, sizeof(void*)*n);
+ memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated));
+ a->n_allocated = n;
+ }
+
+ a->data[i] = p;
+
+ if (i >= a->n_entries)
+ a->n_entries = i+1;
+}
+
+unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
+ unsigned i = a->n_entries;
+ pa_dynarray_put(a, i, p);
+ return i;
+}
+
+void *pa_dynarray_get(pa_dynarray*a, unsigned i) {
+ assert(a);
+ if (i >= a->n_allocated)
+ return NULL;
+
+ assert(a->data);
+ return a->data[i];
+}
+
+unsigned pa_dynarray_size(pa_dynarray*a) {
+ assert(a);
+ return a->n_entries;
+}
diff --git a/src/polypcore/dynarray.h b/src/polypcore/dynarray.h
new file mode 100644
index 00000000..9b1601ba
--- /dev/null
+++ b/src/polypcore/dynarray.h
@@ -0,0 +1,49 @@
+#ifndef foodynarrayhfoo
+#define foodynarrayhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct pa_dynarray pa_dynarray;
+
+/* Implementation of a simple dynamically sized array. The array
+ * expands if required, but doesn't shrink if possible. Memory
+ * management of the array's entries is the user's job. */
+
+pa_dynarray* pa_dynarray_new(void);
+
+/* Free the array calling the specified function for every entry in
+ * the array. The function may be NULL. */
+void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata);
+
+/* Store p at position i in the array */
+void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p);
+
+/* Store p a the first free position in the array. Returns the index
+ * of that entry. If entries are removed from the array their position
+ * are not filled any more by this function. */
+unsigned pa_dynarray_append(pa_dynarray*a, void *p);
+
+void *pa_dynarray_get(pa_dynarray*a, unsigned i);
+
+unsigned pa_dynarray_size(pa_dynarray*a);
+
+#endif
diff --git a/src/polypcore/endianmacros.h b/src/polypcore/endianmacros.h
new file mode 100644
index 00000000..3ab1826a
--- /dev/null
+++ b/src/polypcore/endianmacros.h
@@ -0,0 +1,77 @@
+#ifndef fooendianmacroshfoo
+#define fooendianmacroshfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
+#define UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
+#define INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
+#define UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
+
+#define MAYBE_INT32_SWAP(c,x) ((c) ? INT32_SWAP(x) : x)
+#define MAYBE_UINT32_SWAP(c,x) ((c) ? UINT32_SWAP(x) : x)
+
+#ifdef WORDS_BIGENDIAN
+ #define INT16_FROM_LE(x) INT16_SWAP(x)
+ #define INT16_FROM_BE(x) ((int16_t)(x))
+
+ #define INT16_TO_LE(x) INT16_SWAP(x)
+ #define INT16_TO_BE(x) ((int16_t)(x))
+
+ #define UINT16_FROM_LE(x) UINT16_SWAP(x)
+ #define UINT16_FROM_BE(x) ((uint16_t)(x))
+
+ #define INT32_FROM_LE(x) INT32_SWAP(x)
+ #define INT32_FROM_BE(x) ((int32_t)(x))
+
+ #define UINT32_FROM_LE(x) UINT32_SWAP(x)
+ #define UINT32_FROM_BE(x) ((uint32_t)(x))
+
+ #define UINT32_TO_LE(x) UINT32_SWAP(x)
+ #define UINT32_TO_BE(x) ((uint32_t)(x))
+#else
+ #define INT16_FROM_LE(x) ((int16_t)(x))
+ #define INT16_FROM_BE(x) INT16_SWAP(x)
+
+ #define INT16_TO_LE(x) ((int16_t)(x))
+ #define INT16_TO_BE(x) INT16_SWAP(x)
+
+ #define UINT16_FROM_LE(x) ((uint16_t)(x))
+ #define UINT16_FROM_BE(x) UINT16_SWAP(x)
+
+ #define INT32_FROM_LE(x) ((int32_t)(x))
+ #define INT32_FROM_BE(x) INT32_SWAP(x)
+
+ #define UINT32_FROM_LE(x) ((uint32_t)(x))
+ #define UINT32_FROM_BE(x) UINT32_SWAP(x)
+
+ #define UINT32_TO_LE(x) ((uint32_t)(x))
+ #define UINT32_TO_BE(x) UINT32_SWAP(x)
+#endif
+
+#endif
diff --git a/src/polypcore/esound.h b/src/polypcore/esound.h
new file mode 100644
index 00000000..9c507ef9
--- /dev/null
+++ b/src/polypcore/esound.h
@@ -0,0 +1,209 @@
+#ifndef fooesoundhfoo
+#define fooesoundhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/* Most of the following is blatantly stolen from esound. */
+
+
+/* path and name of the default EsounD domain socket */
+#define ESD_UNIX_SOCKET_DIR "/tmp/.esd"
+#define ESD_UNIX_SOCKET_NAME "/tmp/.esd/socket"
+
+/* length of the audio buffer size */
+#define ESD_BUF_SIZE (4 * 1024)
+/* maximum size we can write(). Otherwise we might overflow */
+#define ESD_MAX_WRITE_SIZE (21 * 4096)
+
+/* length of the authorization key, octets */
+#define ESD_KEY_LEN (16)
+
+/* default port for the EsounD server */
+#define ESD_DEFAULT_PORT (16001)
+
+/* default sample rate for the EsounD server */
+#define ESD_DEFAULT_RATE (44100)
+
+/* maximum length of a stream/sample name */
+#define ESD_NAME_MAX (128)
+
+/* a magic number to identify the relative endianness of a client */
+#define ESD_ENDIAN_KEY ((uint32_t) (('E' << 24) + ('N' << 16) + ('D' << 8) + ('N')))
+
+#define ESD_VOLUME_BASE (256)
+
+
+/*************************************/
+/* what can we do to/with the EsounD */
+enum esd_proto {
+ ESD_PROTO_CONNECT, /* implied on inital client connection */
+
+ /* pseudo "security" functionality */
+ ESD_PROTO_LOCK, /* disable "foreign" client connections */
+ ESD_PROTO_UNLOCK, /* enable "foreign" client connections */
+
+ /* stream functionality: play, record, monitor */
+ ESD_PROTO_STREAM_PLAY, /* play all following data as a stream */
+ ESD_PROTO_STREAM_REC, /* record data from card as a stream */
+ ESD_PROTO_STREAM_MON, /* send mixed buffer output as a stream */
+
+ /* sample functionality: cache, free, play, loop, EOL, kill */
+ ESD_PROTO_SAMPLE_CACHE, /* cache a sample in the server */
+ ESD_PROTO_SAMPLE_FREE, /* release a sample in the server */
+ ESD_PROTO_SAMPLE_PLAY, /* play a cached sample */
+ ESD_PROTO_SAMPLE_LOOP, /* loop a cached sample, til eoloop */
+ ESD_PROTO_SAMPLE_STOP, /* stop a looping sample when done */
+ ESD_PROTO_SAMPLE_KILL, /* stop the looping sample immed. */
+
+ /* free and reclaim /dev/dsp functionality */
+ ESD_PROTO_STANDBY, /* release /dev/dsp and ignore all data */
+ ESD_PROTO_RESUME, /* reclaim /dev/dsp and play sounds again */
+
+ /* TODO: move these to a more logical place. NOTE: will break the protocol */
+ ESD_PROTO_SAMPLE_GETID, /* get the ID for an already-cached sample */
+ ESD_PROTO_STREAM_FILT, /* filter mixed buffer output as a stream */
+
+ /* esd remote management */
+ ESD_PROTO_SERVER_INFO, /* get server info (ver, sample rate, format) */
+ ESD_PROTO_ALL_INFO, /* get all info (server info, players, samples) */
+ ESD_PROTO_SUBSCRIBE, /* track new and removed players and samples */
+ ESD_PROTO_UNSUBSCRIBE, /* stop tracking updates */
+
+ /* esd remote control */
+ ESD_PROTO_STREAM_PAN, /* set stream panning */
+ ESD_PROTO_SAMPLE_PAN, /* set default sample panning */
+
+ /* esd status */
+ ESD_PROTO_STANDBY_MODE, /* see if server is in standby, autostandby, etc */
+
+ /* esd latency */
+ ESD_PROTO_LATENCY, /* retrieve latency between write()'s and output */
+
+ ESD_PROTO_MAX /* for bounds checking */
+};
+
+/******************/
+/* The EsounD api */
+
+/* the properties of a sound buffer are logically or'd */
+
+/* bits of stream/sample data */
+#define ESD_MASK_BITS ( 0x000F )
+#define ESD_BITS8 ( 0x0000 )
+#define ESD_BITS16 ( 0x0001 )
+
+/* how many interleaved channels of data */
+#define ESD_MASK_CHAN ( 0x00F0 )
+#define ESD_MONO ( 0x0010 )
+#define ESD_STEREO ( 0x0020 )
+
+/* whether it's a stream or a sample */
+#define ESD_MASK_MODE ( 0x0F00 )
+#define ESD_STREAM ( 0x0000 )
+#define ESD_SAMPLE ( 0x0100 )
+#define ESD_ADPCM ( 0x0200 ) /* TODO: anyone up for this? =P */
+
+/* the function of the stream/sample, and common functions */
+#define ESD_MASK_FUNC ( 0xF000 )
+#define ESD_PLAY ( 0x1000 )
+/* functions for streams only */
+#define ESD_MONITOR ( 0x0000 )
+#define ESD_RECORD ( 0x2000 )
+/* functions for samples only */
+#define ESD_STOP ( 0x0000 )
+#define ESD_LOOP ( 0x2000 )
+
+typedef int esd_format_t;
+typedef int esd_proto_t;
+
+/*******************************************************************/
+/* esdmgr.c - functions to implement a "sound manager" for esd */
+
+/* structures to retrieve information about streams/samples from the server */
+typedef struct esd_server_info {
+
+ int version; /* server version encoded as an int */
+ esd_format_t format; /* magic int with the format info */
+ int rate; /* sample rate */
+
+} esd_server_info_t;
+
+typedef struct esd_player_info {
+
+ struct esd_player_info *next; /* point to next entry in list */
+ esd_server_info_t *server; /* the server that contains this stream */
+
+ int source_id; /* either a stream fd or sample id */
+ char name[ ESD_NAME_MAX ]; /* name of stream for remote control */
+ int rate; /* sample rate */
+ int left_vol_scale; /* volume scaling */
+ int right_vol_scale;
+
+ esd_format_t format; /* magic int with the format info */
+
+} esd_player_info_t;
+
+typedef struct esd_sample_info {
+
+ struct esd_sample_info *next; /* point to next entry in list */
+ esd_server_info_t *server; /* the server that contains this sample */
+
+ int sample_id; /* either a stream fd or sample id */
+ char name[ ESD_NAME_MAX ]; /* name of stream for remote control */
+ int rate; /* sample rate */
+ int left_vol_scale; /* volume scaling */
+ int right_vol_scale;
+
+ esd_format_t format; /* magic int with the format info */
+ int length; /* total buffer length */
+
+} esd_sample_info_t;
+
+typedef struct esd_info {
+
+ esd_server_info_t *server;
+ esd_player_info_t *player_list;
+ esd_sample_info_t *sample_list;
+
+} esd_info_t;
+
+enum esd_standby_mode {
+ ESM_ERROR, ESM_ON_STANDBY, ESM_ON_AUTOSTANDBY, ESM_RUNNING
+};
+typedef int esd_standby_mode_t;
+
+enum esd_client_state {
+ ESD_STREAMING_DATA, /* data from here on is streamed data */
+ ESD_CACHING_SAMPLE, /* midway through caching a sample */
+ ESD_NEEDS_REQDATA, /* more data needed to complere request */
+ ESD_NEXT_REQUEST, /* proceed to next request */
+ ESD_CLIENT_STATE_MAX /* place holder */
+};
+typedef int esd_client_state_t;
+
+/* the endian key is transferred in binary, if it's read into int, */
+/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */
+/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */
+#define ESD_SWAP_ENDIAN_KEY (UINT32_SWAP(ESD_ENDIAN_KEY))
+
+
+#endif
diff --git a/src/polypcore/g711.c b/src/polypcore/g711.c
new file mode 100644
index 00000000..55a82396
--- /dev/null
+++ b/src/polypcore/g711.c
@@ -0,0 +1,2531 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+
+/*
+ * December 30, 1994:
+ * Functions linear2alaw, linear2ulaw have been updated to correctly
+ * convert unquantized 16 bit values.
+ * Tables for direct u- to A-law and A- to u-law conversions have been
+ * corrected.
+ * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
+ * bli@cpk.auc.dk
+ *
+ */
+
+#include "g711.h"
+
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+#if !defined(FAST_ALAW_CONVERSION) || !defined(FAST_ULAW_CONVERSION)
+static int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
+ 0x1FF, 0x3FF, 0x7FF, 0xFFF};
+static int16_t seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
+ 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+
+static int16_t search(
+ int16_t val,
+ int16_t *table,
+ int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+#endif /* !FAST_*_CONVERSION */
+
+#ifndef FAST_ALAW_CONVERSION
+/*
+ * linear2alaw() accepts an 13-bit signed integer and encodes it as A-law data
+ * stored in a unsigned char. This function should only be called with
+ * the data shifted such that it only contains information in the lower
+ * 13-bits.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char st_13linear2alaw(
+ int16_t pcm_val) /* 2's complement (13-bit range) */
+{
+ int16_t mask;
+ short seg;
+ unsigned char aval;
+
+ /* Have calling software do it since its already doing a shift
+ * from 32-bits down to 16-bits.
+ */
+ /* pcm_val = pcm_val >> 3; */
+
+ /* A-law using even bit inversion */
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 1;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_aend, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ aval = (unsigned char) seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 1) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> seg) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit signed linear PCM
+ *
+ */
+int16_t st_alaw2linear16(
+ unsigned char a_val)
+{
+ int16_t t;
+ int16_t seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+#endif /* !FAST_ALAW_CONVERSION */
+
+#define BIAS (0x84) /* Bias for linear code. */
+#define CLIP 8159
+
+#ifndef FAST_ULAW_CONVERSION
+/*
+ * linear2ulaw() accepts a 14-bit signed integer and encodes it as u-law data
+ * stored in a unsigned char. This function should only be called with
+ * the data shifted such that it only contains information in the lower
+ * 14-bits.
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char st_14linear2ulaw(
+ int16_t pcm_val) /* 2's complement (14-bit range) */
+{
+ int16_t mask;
+ int16_t seg;
+ unsigned char uval;
+
+ /* Have calling software do it since its already doing a shift
+ * from 32-bits down to 16-bits.
+ */
+ /* pcm_val = pcm_val >> 2; */
+
+ /* u-law inverts all bits */
+ /* Get the sign and the magnitude of the value. */
+ if (pcm_val < 0) {
+ pcm_val = -pcm_val;
+ mask = 0x7F;
+ } else {
+ mask = 0xFF;
+ }
+ if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
+ pcm_val += (BIAS >> 2);
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_uend, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+ return (uval ^ mask);
+ }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+int16_t st_ulaw2linear16(
+ unsigned char u_val)
+{
+ int16_t t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+#endif /* !FAST_ULAW_CONVERSION */
+
+#ifdef FAST_ALAW_CONVERSION
+
+int16_t _st_alaw2linear16[256] = {
+ -5504, -5248, -6016, -5760, -4480, -4224, -4992,
+ -4736, -7552, -7296, -8064, -7808, -6528, -6272,
+ -7040, -6784, -2752, -2624, -3008, -2880, -2240,
+ -2112, -2496, -2368, -3776, -3648, -4032, -3904,
+ -3264, -3136, -3520, -3392, -22016, -20992, -24064,
+ -23040, -17920, -16896, -19968, -18944, -30208, -29184,
+ -32256, -31232, -26112, -25088, -28160, -27136, -11008,
+ -10496, -12032, -11520, -8960, -8448, -9984, -9472,
+ -15104, -14592, -16128, -15616, -13056, -12544, -14080,
+ -13568, -344, -328, -376, -360, -280, -264,
+ -312, -296, -472, -456, -504, -488, -408,
+ -392, -440, -424, -88, -72, -120, -104,
+ -24, -8, -56, -40, -216, -200, -248,
+ -232, -152, -136, -184, -168, -1376, -1312,
+ -1504, -1440, -1120, -1056, -1248, -1184, -1888,
+ -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+ -688, -656, -752, -720, -560, -528, -624,
+ -592, -944, -912, -1008, -976, -816, -784,
+ -880, -848, 5504, 5248, 6016, 5760, 4480,
+ 4224, 4992, 4736, 7552, 7296, 8064, 7808,
+ 6528, 6272, 7040, 6784, 2752, 2624, 3008,
+ 2880, 2240, 2112, 2496, 2368, 3776, 3648,
+ 4032, 3904, 3264, 3136, 3520, 3392, 22016,
+ 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+ 30208, 29184, 32256, 31232, 26112, 25088, 28160,
+ 27136, 11008, 10496, 12032, 11520, 8960, 8448,
+ 9984, 9472, 15104, 14592, 16128, 15616, 13056,
+ 12544, 14080, 13568, 344, 328, 376, 360,
+ 280, 264, 312, 296, 472, 456, 504,
+ 488, 408, 392, 440, 424, 88, 72,
+ 120, 104, 24, 8, 56, 40, 216,
+ 200, 248, 232, 152, 136, 184, 168,
+ 1376, 1312, 1504, 1440, 1120, 1056, 1248,
+ 1184, 1888, 1824, 2016, 1952, 1632, 1568,
+ 1760, 1696, 688, 656, 752, 720, 560,
+ 528, 624, 592, 944, 912, 1008, 976,
+ 816, 784, 880, 848
+};
+
+uint8_t _st_13linear2alaw[0x2000] = {
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b,
+ 0x6b, 0x6b, 0x6b, 0x6b, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6e, 0x6e, 0x6e, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
+ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d,
+ 0x6d, 0x6d, 0x6d, 0x6d, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x60, 0x60, 0x60, 0x60,
+ 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67,
+ 0x67, 0x67, 0x67, 0x67, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
+ 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x7a, 0x7a, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
+ 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7c, 0x7c, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7d, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73,
+ 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, 0x76, 0x76, 0x76, 0x76,
+ 0x77, 0x77, 0x77, 0x77, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75,
+ 0x4a, 0x4a, 0x4b, 0x4b, 0x48, 0x48, 0x49, 0x49, 0x4e, 0x4e, 0x4f, 0x4f,
+ 0x4c, 0x4c, 0x4d, 0x4d, 0x42, 0x42, 0x43, 0x43, 0x40, 0x40, 0x41, 0x41,
+ 0x46, 0x46, 0x47, 0x47, 0x44, 0x44, 0x45, 0x45, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x58, 0x58, 0x59, 0x59, 0x5e, 0x5e, 0x5f, 0x5f, 0x5c, 0x5c, 0x5d, 0x5d,
+ 0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, 0x56, 0x56, 0x57, 0x57,
+ 0x54, 0x54, 0x55, 0x55, 0xd5, 0xd5, 0xd4, 0xd4, 0xd7, 0xd7, 0xd6, 0xd6,
+ 0xd1, 0xd1, 0xd0, 0xd0, 0xd3, 0xd3, 0xd2, 0xd2, 0xdd, 0xdd, 0xdc, 0xdc,
+ 0xdf, 0xdf, 0xde, 0xde, 0xd9, 0xd9, 0xd8, 0xd8, 0xdb, 0xdb, 0xda, 0xda,
+ 0xc5, 0xc5, 0xc4, 0xc4, 0xc7, 0xc7, 0xc6, 0xc6, 0xc1, 0xc1, 0xc0, 0xc0,
+ 0xc3, 0xc3, 0xc2, 0xc2, 0xcd, 0xcd, 0xcc, 0xcc, 0xcf, 0xcf, 0xce, 0xce,
+ 0xc9, 0xc9, 0xc8, 0xc8, 0xcb, 0xcb, 0xca, 0xca, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+#endif /* FAST_ALAW_CONVERSION */
+
+#ifdef FAST_ULAW_CONVERSION
+
+int16_t _st_ulaw2linear16[256] = {
+ -32124, -31100, -30076, -29052, -28028, -27004, -25980,
+ -24956, -23932, -22908, -21884, -20860, -19836, -18812,
+ -17788, -16764, -15996, -15484, -14972, -14460, -13948,
+ -13436, -12924, -12412, -11900, -11388, -10876, -10364,
+ -9852, -9340, -8828, -8316, -7932, -7676, -7420,
+ -7164, -6908, -6652, -6396, -6140, -5884, -5628,
+ -5372, -5116, -4860, -4604, -4348, -4092, -3900,
+ -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+ -2876, -2748, -2620, -2492, -2364, -2236, -2108,
+ -1980, -1884, -1820, -1756, -1692, -1628, -1564,
+ -1500, -1436, -1372, -1308, -1244, -1180, -1116,
+ -1052, -988, -924, -876, -844, -812, -780,
+ -748, -716, -684, -652, -620, -588, -556,
+ -524, -492, -460, -428, -396, -372, -356,
+ -340, -324, -308, -292, -276, -260, -244,
+ -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72,
+ -64, -56, -48, -40, -32, -24, -16,
+ -8, 0, 32124, 31100, 30076, 29052, 28028,
+ 27004, 25980, 24956, 23932, 22908, 21884, 20860,
+ 19836, 18812, 17788, 16764, 15996, 15484, 14972,
+ 14460, 13948, 13436, 12924, 12412, 11900, 11388,
+ 10876, 10364, 9852, 9340, 8828, 8316, 7932,
+ 7676, 7420, 7164, 6908, 6652, 6396, 6140,
+ 5884, 5628, 5372, 5116, 4860, 4604, 4348,
+ 4092, 3900, 3772, 3644, 3516, 3388, 3260,
+ 3132, 3004, 2876, 2748, 2620, 2492, 2364,
+ 2236, 2108, 1980, 1884, 1820, 1756, 1692,
+ 1628, 1564, 1500, 1436, 1372, 1308, 1244,
+ 1180, 1116, 1052, 988, 924, 876, 844,
+ 812, 780, 748, 716, 684, 652, 620,
+ 588, 556, 524, 492, 460, 428, 396,
+ 372, 356, 340, 324, 308, 292, 276,
+ 260, 244, 228, 212, 196, 180, 164,
+ 148, 132, 120, 112, 104, 96, 88,
+ 80, 72, 64, 56, 48, 40, 32,
+ 24, 16, 8, 0
+};
+
+uint8_t _st_14linear2ulaw[0x4000] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+ 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+ 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+ 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+ 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+ 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43,
+ 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
+ 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
+ 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46,
+ 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
+ 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
+ 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
+ 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b,
+ 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d,
+ 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+ 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51,
+ 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52,
+ 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54,
+ 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b,
+ 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,
+ 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
+ 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63,
+ 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66,
+ 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c,
+ 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
+ 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0xff, 0xfe, 0xfe, 0xfd,
+ 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7,
+ 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1,
+ 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed,
+ 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea,
+ 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
+ 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4,
+ 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80
+};
+
+#endif /* FAST_ULAW_CONVERSION */
+
+/* The following code was used to generate the lookup tables */
+#if 0
+int main()
+{
+ int x, y, find2a = 0;
+
+ y = 0;
+ printf("int16_t _st_alaw2linear16[256] = {\n ");
+ for (x = 0; x < 256; x++)
+ {
+ printf("%8d,", st_alaw2linear16(x));
+ y++;
+ if (y == 7)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+
+ printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n ");
+ y = 0;
+ for (x = 0; x < 0x2000; x++)
+ {
+ printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x));
+ y++;
+ if (y == 12)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+
+ printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n ");
+ y = 0;
+ for (x = 0; x < 256; x++)
+ {
+ printf("%8d,", st_ulaw2linear16(x));
+ y++;
+ if (y == 7)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+
+ printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n ");
+ y = 0;
+ for (x = 0; x < 0x4000; x++)
+ {
+ printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x));
+ y++;
+ if (y == 12)
+ {
+ y = 0;
+ printf("\n ");
+ }
+ }
+ printf("\n};\n");
+
+}
+#endif
+
+/* The following is not used by SoX but kept for reference */
+#if 0
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+/* corrected:
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ should be: */
+ 80, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
+
+unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+/* corrected:
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ should be: */
+ 73, 74, 75, 76, 77, 78, 79, 80,
+
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+/* A-law to u-law conversion */
+unsigned char st_alaw2ulaw(
+ unsigned char aval)
+{
+ aval &= 0xff;
+ return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char st_ulaw2alaw(
+ unsigned char uval)
+{
+ uval &= 0xff;
+ return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+#endif
diff --git a/src/polypcore/g711.h b/src/polypcore/g711.h
new file mode 100644
index 00000000..97cedf81
--- /dev/null
+++ b/src/polypcore/g711.h
@@ -0,0 +1,40 @@
+#ifndef foog711hfoo
+#define foog711hfoo
+
+/* g711.h - include for G711 u-law and a-law conversion routines
+**
+** Copyright (C) 2001 Chris Bagwell
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation. This software is provided "as is" without express or
+** implied warranty.
+*/
+
+/** Copied from sox -- Lennart Poettring*/
+
+#include <inttypes.h>
+
+#ifdef FAST_ALAW_CONVERSION
+extern uint8_t _st_13linear2alaw[0x2000];
+extern int16_t _st_alaw2linear16[256];
+#define st_13linear2alaw(sw) (_st_13linear2alaw[(sw + 0x1000)])
+#define st_alaw2linear16(uc) (_st_alaw2linear16[uc])
+#else
+unsigned char st_13linear2alaw(int16_t pcm_val);
+int16_t st_alaw2linear16(unsigned char);
+#endif
+
+#ifdef FAST_ULAW_CONVERSION
+extern uint8_t _st_14linear2ulaw[0x4000];
+extern int16_t _st_ulaw2linear16[256];
+#define st_14linear2ulaw(sw) (_st_14linear2ulaw[(sw + 0x2000)])
+#define st_ulaw2linear16(uc) (_st_ulaw2linear16[uc])
+#else
+unsigned char st_14linear2ulaw(int16_t pcm_val);
+int16_t st_ulaw2linear16(unsigned char);
+#endif
+
+#endif
diff --git a/src/polypcore/gccmacro.h b/src/polypcore/gccmacro.h
new file mode 100644
index 00000000..9e212f2e
--- /dev/null
+++ b/src/polypcore/gccmacro.h
@@ -0,0 +1,53 @@
+#ifndef foogccmacrohfoo
+#define foogccmacrohfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef __GNUC__
+#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
+#else
+/** If we're in GNU C, use some magic for detecting invalid format strings */
+#define PA_GCC_PRINTF_ATTR(a,b)
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ >= 4)
+#define PA_GCC_SENTINEL __attribute__ ((sentinel))
+#else
+/** Macro for usage of GCC's sentinel compilation warnings */
+#define PA_GCC_SENTINEL
+#endif
+
+#ifdef __GNUC__
+#define PA_GCC_NORETURN __attribute__((noreturn))
+#else
+/** Macro for no-return functions */
+#define PA_GCC_NORETURN
+#endif
+
+#ifdef __GNUC__
+#define PA_GCC_UNUSED __attribute__ ((unused))
+#else
+/** Macro for not used parameter */
+#define PA_GCC_UNUSED
+#endif
+
+#endif
diff --git a/src/polypcore/hashmap.c b/src/polypcore/hashmap.c
new file mode 100644
index 00000000..a37decb8
--- /dev/null
+++ b/src/polypcore/hashmap.c
@@ -0,0 +1,194 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "hashmap.h"
+#include "idxset.h"
+#include "xmalloc.h"
+#include "log.h"
+
+#define BUCKETS 1023
+
+struct hashmap_entry {
+ struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous;
+ unsigned hash;
+ const void *key;
+ void *value;
+};
+
+struct pa_hashmap {
+ unsigned size;
+ struct hashmap_entry **data;
+ struct hashmap_entry *first_entry;
+
+ unsigned n_entries;
+ unsigned (*hash_func) (const void *p);
+ int (*compare_func) (const void*a, const void*b);
+};
+
+pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
+ pa_hashmap *h;
+ h = pa_xmalloc(sizeof(pa_hashmap));
+ h->data = pa_xmalloc0(sizeof(struct hashmap_entry*)*(h->size = BUCKETS));
+ h->first_entry = NULL;
+ h->n_entries = 0;
+ h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
+ h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
+ return h;
+}
+
+static void remove(pa_hashmap *h, struct hashmap_entry *e) {
+ assert(e);
+
+ if (e->next)
+ e->next->previous = e->previous;
+ if (e->previous)
+ e->previous->next = e->next;
+ else
+ h->first_entry = e->next;
+
+ if (e->bucket_next)
+ e->bucket_next->bucket_previous = e->bucket_previous;
+ if (e->bucket_previous)
+ e->bucket_previous->bucket_next = e->bucket_next;
+ else {
+ assert(e->hash < h->size);
+ h->data[e->hash] = e->bucket_next;
+ }
+
+ pa_xfree(e);
+ h->n_entries--;
+}
+
+void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) {
+ assert(h);
+
+ while (h->first_entry) {
+ if (free_func)
+ free_func(h->first_entry->value, userdata);
+ remove(h, h->first_entry);
+ }
+
+ pa_xfree(h->data);
+ pa_xfree(h);
+}
+
+static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) {
+ struct hashmap_entry *e;
+ assert(h && hash < h->size);
+
+ for (e = h->data[hash]; e; e = e->bucket_next)
+ if (h->compare_func(e->key, key) == 0)
+ return e;
+
+ return NULL;
+}
+
+int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) {
+ struct hashmap_entry *e;
+ unsigned hash;
+ assert(h);
+
+ hash = h->hash_func(key) % h->size;
+
+ if ((e = get(h, hash, key)))
+ return -1;
+
+ e = pa_xmalloc(sizeof(struct hashmap_entry));
+ e->hash = hash;
+ e->key = key;
+ e->value = value;
+
+ e->previous = NULL;
+ e->next = h->first_entry;
+ if (h->first_entry)
+ h->first_entry->previous = e;
+ h->first_entry = e;
+
+ e->bucket_previous = NULL;
+ e->bucket_next = h->data[hash];
+ if (h->data[hash])
+ h->data[hash]->bucket_previous = e;
+ h->data[hash] = e;
+
+ h->n_entries ++;
+ return 0;
+}
+
+void* pa_hashmap_get(pa_hashmap *h, const void *key) {
+ unsigned hash;
+ struct hashmap_entry *e;
+ assert(h && key);
+
+ hash = h->hash_func(key) % h->size;
+
+ if (!(e = get(h, hash, key)))
+ return NULL;
+
+ return e->value;
+}
+
+void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
+ struct hashmap_entry *e;
+ unsigned hash;
+ void *data;
+ assert(h && key);
+
+ hash = h->hash_func(key) % h->size;
+
+ if (!(e = get(h, hash, key)))
+ return NULL;
+
+ data = e->value;
+ remove(h, e);
+ return data;
+}
+
+unsigned pa_hashmap_size(pa_hashmap *h) {
+ return h->n_entries;
+}
+
+void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
+ assert(h && state);
+
+ if (!*state)
+ *state = h->first_entry;
+ else
+ *state = ((struct hashmap_entry*) *state)->next;
+
+ if (!*state) {
+ if (key)
+ *key = NULL;
+ return NULL;
+ }
+
+ if (key)
+ *key = ((struct hashmap_entry*) *state)->key;
+
+ return ((struct hashmap_entry*) *state)->value;
+}
diff --git a/src/polypcore/hashmap.h b/src/polypcore/hashmap.h
new file mode 100644
index 00000000..14f82705
--- /dev/null
+++ b/src/polypcore/hashmap.h
@@ -0,0 +1,53 @@
+#ifndef foohashmaphfoo
+#define foohashmaphfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/* Simple Implementation of a hash table. Memory management is the
+ * user's job. It's a good idea to have the key pointer point to a
+ * string in the value data. */
+
+typedef struct pa_hashmap pa_hashmap;
+
+/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */
+pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
+
+/* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */
+void pa_hashmap_free(pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata);
+
+/* Returns non-zero when the entry already exists */
+int pa_hashmap_put(pa_hashmap *h, const void *key, void *value);
+void* pa_hashmap_get(pa_hashmap *h, const void *key);
+
+/* Returns the data of the entry while removing */
+void* pa_hashmap_remove(pa_hashmap *h, const void *key);
+
+unsigned pa_hashmap_size(pa_hashmap *h);
+
+/* May be used to iterate through the hashmap. Initially the opaque
+ pointer *state has to be set to NULL. The hashmap may not be
+ modified during iteration. The key of the entry is returned in
+ *key, if key is non-NULL. After the last entry in the hashmap NULL
+ is returned. */
+void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
+
+#endif
diff --git a/src/polypcore/howl-wrap.c b/src/polypcore/howl-wrap.c
new file mode 100644
index 00000000..77d096ac
--- /dev/null
+++ b/src/polypcore/howl-wrap.c
@@ -0,0 +1,116 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <assert.h>
+
+#include "howl-wrap.h"
+#include "log.h"
+#include "xmalloc.h"
+#include "props.h"
+
+#define HOWL_PROPERTY "howl"
+
+pa_howl_wrapper {
+ pa_core *core;
+ int ref;
+
+ pa_io_event *io_event;
+ sw_discovery discovery;
+
+};
+
+static void howl_io_event(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags f, void *userdata) {
+ pa_howl_wrapper *w = userdata;
+ assert(m && e && fd >= 0 && w && w->ref >= 1);
+
+ if (f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR))
+ goto fail;
+
+ if (sw_discovery_read_socket(w->discovery) != SW_OKAY)
+ goto fail;
+
+ return;
+
+fail:
+ pa_log(__FILE__": howl connection died.\n");
+ w->core->mainloop->io_free(w->io_event);
+ w->io_event = NULL;
+}
+
+static pa_howl_wrapper* howl_wrapper_new(pa_core *c) {
+ pa_howl_wrapper *h;
+ sw_discovery session;
+ assert(c);
+
+ if (sw_discovery_init(&session) != SW_OKAY) {
+ pa_log("sw_discovery_init() failed.\n");
+ return NULL;
+ }
+
+ h = pa_xmalloc(sizeof(pa_howl_wrapper));
+ h->core = c;
+ h->ref = 1;
+ h->discovery = session;
+
+ h->io_event = c->mainloop->io_new(c->mainloop, sw_discovery_socket(session), PA_IO_EVENT_INPUT, howl_io_event, h);
+
+ return h;
+}
+
+static void howl_wrapper_free(pa_howl_wrapper *h) {
+ assert(h);
+
+ sw_discovery_fina(h->discovery);
+
+ if (h->io_event)
+ h->core->mainloop->io_free(h->io_event);
+
+ pa_xfree(h);
+}
+
+pa_howl_wrapper* pa_howl_wrapper_get(pa_core *c) {
+ pa_howl_wrapper *h;
+ assert(c);
+
+ if ((h = pa_property_get(c, HOWL_PROPERTY)))
+ return pa_howl_wrapper_ref(h);
+
+ return howl_wrapper_new(c);
+}
+
+pa_howl_wrapper* pa_howl_wrapper_ref(pa_howl_wrapper *h) {
+ assert(h && h->ref >= 1);
+ h->ref++;
+ return h;
+}
+
+void pa_howl_wrapper_unref(pa_howl_wrapper *h) {
+ assert(h && h->ref >= 1);
+ if (!(--h->ref))
+ howl_wrapper_free(h);
+}
+
+sw_discovery pa_howl_wrapper_get_discovery(pa_howl_wrapper *h) {
+ assert(h && h->ref >= 1);
+
+ return h->discovery;
+}
+
diff --git a/src/polypcore/howl-wrap.h b/src/polypcore/howl-wrap.h
new file mode 100644
index 00000000..a670b082
--- /dev/null
+++ b/src/polypcore/howl-wrap.h
@@ -0,0 +1,37 @@
+#ifndef foohowlwrapperhfoo
+#define foohowlwrapperhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <howl.h>
+
+#include "core.h"
+
+pa_howl_wrapper;
+
+pa_howl_wrapper* pa_howl_wrapper_get(pa_core *c);
+pa_howl_wrapper* pa_howl_wrapper_ref(pa_howl_wrapper *h);
+void pa_howl_wrapper_unref(pa_howl_wrapper *h);
+
+sw_discovery pa_howl_wrapper_get_discovery(pa_howl_wrapper *h);
+
+#endif
diff --git a/src/polypcore/idxset.c b/src/polypcore/idxset.c
new file mode 100644
index 00000000..409d1fab
--- /dev/null
+++ b/src/polypcore/idxset.c
@@ -0,0 +1,390 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "idxset.h"
+#include "xmalloc.h"
+
+typedef struct idxset_entry {
+ void *data;
+ uint32_t index;
+ unsigned hash_value;
+
+ struct idxset_entry *hash_prev, *hash_next;
+ struct idxset_entry* iterate_prev, *iterate_next;
+} idxset_entry;
+
+struct pa_idxset {
+ unsigned (*hash_func) (const void *p);
+ int (*compare_func)(const void *a, const void *b);
+
+ unsigned hash_table_size, n_entries;
+ idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail;
+ uint32_t index, start_index, array_size;
+};
+
+unsigned pa_idxset_string_hash_func(const void *p) {
+ unsigned hash = 0;
+ const char *c;
+
+ for (c = p; *c; c++)
+ hash = 31 * hash + *c;
+
+ return hash;
+}
+
+int pa_idxset_string_compare_func(const void *a, const void *b) {
+ return strcmp(a, b);
+}
+
+unsigned pa_idxset_trivial_hash_func(const void *p) {
+ return (unsigned) p;
+}
+
+int pa_idxset_trivial_compare_func(const void *a, const void *b) {
+ return a != b;
+}
+
+pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
+ pa_idxset *s;
+
+ s = pa_xnew(pa_idxset, 1);
+ s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
+ s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
+ s->hash_table_size = 1023;
+ s->hash_table = pa_xmalloc0(sizeof(idxset_entry*)*s->hash_table_size);
+ s->array = NULL;
+ s->array_size = 0;
+ s->index = 0;
+ s->start_index = 0;
+ s->n_entries = 0;
+
+ s->iterate_list_head = s->iterate_list_tail = NULL;
+
+ return s;
+}
+
+void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) {
+ assert(s);
+
+ while (s->iterate_list_head) {
+ idxset_entry *e = s->iterate_list_head;
+ s->iterate_list_head = s->iterate_list_head->iterate_next;
+
+ if (free_func)
+ free_func(e->data, userdata);
+ pa_xfree(e);
+ }
+
+ pa_xfree(s->hash_table);
+ pa_xfree(s->array);
+ pa_xfree(s);
+}
+
+static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) {
+ assert(p);
+
+ assert(s->compare_func);
+ for (; e; e = e->hash_next)
+ if (s->compare_func(e->data, p) == 0)
+ return e;
+
+ return NULL;
+}
+
+static void extend_array(pa_idxset *s, uint32_t idx) {
+ uint32_t i, j, l;
+ idxset_entry** n;
+ assert(idx >= s->start_index);
+
+ if (idx < s->start_index + s->array_size)
+ return;
+
+ for (i = 0; i < s->array_size; i++)
+ if (s->array[i])
+ break;
+
+ l = idx - s->start_index - i + 100;
+ n = pa_xnew0(idxset_entry*, l);
+
+ for (j = 0; j < s->array_size-i; j++)
+ n[j] = s->array[i+j];
+
+ pa_xfree(s->array);
+
+ s->array = n;
+ s->array_size = l;
+ s->start_index += i;
+}
+
+static idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
+ if (idx >= s->start_index + s->array_size)
+ return NULL;
+
+ if (idx < s->start_index)
+ return NULL;
+
+ return s->array + (idx - s->start_index);
+}
+
+int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
+ unsigned h;
+ idxset_entry *e, **a;
+ assert(s && p);
+
+ assert(s->hash_func);
+ h = s->hash_func(p) % s->hash_table_size;
+
+ assert(s->hash_table);
+ if ((e = hash_scan(s, s->hash_table[h], p))) {
+ if (idx)
+ *idx = e->index;
+
+ return -1;
+ }
+
+ e = pa_xmalloc(sizeof(idxset_entry));
+ e->data = p;
+ e->index = s->index++;
+ e->hash_value = h;
+
+ /* Insert into hash table */
+ e->hash_next = s->hash_table[h];
+ e->hash_prev = NULL;
+ if (s->hash_table[h])
+ s->hash_table[h]->hash_prev = e;
+ s->hash_table[h] = e;
+
+ /* Insert into array */
+ extend_array(s, e->index);
+ a = array_index(s, e->index);
+ assert(a && !*a);
+ *a = e;
+
+ /* Insert into linked list */
+ e->iterate_next = NULL;
+ e->iterate_prev = s->iterate_list_tail;
+ if (s->iterate_list_tail) {
+ assert(s->iterate_list_head);
+ s->iterate_list_tail->iterate_next = e;
+ } else {
+ assert(!s->iterate_list_head);
+ s->iterate_list_head = e;
+ }
+ s->iterate_list_tail = e;
+
+ s->n_entries++;
+ assert(s->n_entries >= 1);
+
+ if (idx)
+ *idx = e->index;
+
+ return 0;
+}
+
+void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
+ idxset_entry **a;
+ assert(s);
+
+ if (!(a = array_index(s, idx)))
+ return NULL;
+
+ if (!*a)
+ return NULL;
+
+ return (*a)->data;
+}
+
+void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
+ unsigned h;
+ idxset_entry *e;
+ assert(s && p);
+
+ assert(s->hash_func);
+ h = s->hash_func(p) % s->hash_table_size;
+
+ assert(s->hash_table);
+ if (!(e = hash_scan(s, s->hash_table[h], p)))
+ return NULL;
+
+ if (idx)
+ *idx = e->index;
+
+ return e->data;
+}
+
+static void remove_entry(pa_idxset *s, idxset_entry *e) {
+ idxset_entry **a;
+ assert(s && e);
+
+ /* Remove from array */
+ a = array_index(s, e->index);
+ assert(a && *a && *a == e);
+ *a = NULL;
+
+ /* Remove from linked list */
+ if (e->iterate_next)
+ e->iterate_next->iterate_prev = e->iterate_prev;
+ else
+ s->iterate_list_tail = e->iterate_prev;
+
+ if (e->iterate_prev)
+ e->iterate_prev->iterate_next = e->iterate_next;
+ else
+ s->iterate_list_head = e->iterate_next;
+
+ /* Remove from hash table */
+ if (e->hash_next)
+ e->hash_next->hash_prev = e->hash_prev;
+
+ if (e->hash_prev)
+ e->hash_prev->hash_next = e->hash_next;
+ else
+ s->hash_table[e->hash_value] = e->hash_next;
+
+ pa_xfree(e);
+
+ assert(s->n_entries >= 1);
+ s->n_entries--;
+}
+
+void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
+ idxset_entry **a;
+ void *data;
+
+ assert(s);
+
+ if (!(a = array_index(s, idx)))
+ return NULL;
+
+ data = (*a)->data;
+ remove_entry(s, *a);
+
+ return data;
+}
+
+void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
+ idxset_entry *e;
+ unsigned h;
+ void *r;
+
+ assert(s->hash_func);
+ h = s->hash_func(data) % s->hash_table_size;
+
+ assert(s->hash_table);
+ if (!(e = hash_scan(s, s->hash_table[h], data)))
+ return NULL;
+
+ r = e->data;
+ if (idx)
+ *idx = e->index;
+
+ remove_entry(s, e);
+
+ return r;
+}
+
+void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
+ idxset_entry **a, *e = NULL;
+ assert(s && idx);
+
+ if ((a = array_index(s, *idx)) && *a)
+ e = (*a)->iterate_next;
+
+ if (!e)
+ e = s->iterate_list_head;
+
+ if (!e)
+ return NULL;
+
+ *idx = e->index;
+ return e->data;
+}
+
+void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
+ assert(s);
+
+ if (!s->iterate_list_head)
+ return NULL;
+
+ if (idx)
+ *idx = s->iterate_list_head->index;
+ return s->iterate_list_head->data;
+}
+
+void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
+ idxset_entry **a, *e = NULL;
+ assert(s && idx);
+
+ if ((a = array_index(s, *idx)) && *a)
+ e = (*a)->iterate_next;
+
+ if (e) {
+ *idx = e->index;
+ return e->data;
+ } else {
+ *idx = PA_IDXSET_INVALID;
+ return NULL;
+ }
+}
+
+
+int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) {
+ idxset_entry *e;
+ assert(s && func);
+
+ e = s->iterate_list_head;
+ while (e) {
+ int del = 0, r;
+ idxset_entry *n = e->iterate_next;
+
+ r = func(e->data, e->index, &del, userdata);
+
+ if (del)
+ remove_entry(s, e);
+
+ if (r < 0)
+ return r;
+
+ e = n;
+ }
+
+ return 0;
+}
+
+unsigned pa_idxset_size(pa_idxset*s) {
+ assert(s);
+ return s->n_entries;
+}
+
+int pa_idxset_isempty(pa_idxset *s) {
+ assert(s);
+ return s->n_entries == 0;
+}
+
diff --git a/src/polypcore/idxset.h b/src/polypcore/idxset.h
new file mode 100644
index 00000000..17ae16cb
--- /dev/null
+++ b/src/polypcore/idxset.h
@@ -0,0 +1,94 @@
+#ifndef fooidxsethfoo
+#define fooidxsethfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+/* A combination of a set and a dynamic array. Entries are indexable
+ * both through a numeric automatically generated index and the entry's
+ * data pointer. As usual, memory management is the user's job. */
+
+/* A special index value denoting the invalid index. */
+#define PA_IDXSET_INVALID ((uint32_t) -1)
+
+/* Generic implementations for hash and comparison functions. Just
+ * compares the pointer or calculates the hash value directly from the
+ * pointer value. */
+unsigned pa_idxset_trivial_hash_func(const void *p);
+int pa_idxset_trivial_compare_func(const void *a, const void *b);
+
+/* Generic implementations for hash and comparison functions for strings. */
+unsigned pa_idxset_string_hash_func(const void *p);
+int pa_idxset_string_compare_func(const void *a, const void *b);
+
+typedef struct pa_idxset pa_idxset;
+
+/* Instantiate a new idxset with the specified hash and comparison functions */
+pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
+
+/* Free the idxset. When the idxset is not empty the specified function is called for every entry contained */
+void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata);
+
+/* Store a new item in the idxset. The index of the item is returned in *idx */
+int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx);
+
+/* Get the entry by its idx */
+void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx);
+
+/* Get the entry by its data. The idx is returned in *index */
+void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx);
+
+/* Similar to pa_idxset_get_by_index(), but removes the entry from the idxset. */
+void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx);
+
+/* Similar to pa_idxset_get_by_data(), but removes the entry from the idxset */
+void* pa_idxset_remove_by_data(pa_idxset*s, const void *p, uint32_t *idx);
+
+/* This may be used to iterate through all entries. When called with
+ an invalid index value it returns the first entry, otherwise the
+ next following. The function is best called with *idx =
+ PA_IDXSET_VALID first. It is safe to manipulate the idxset between
+ the calls. It is not guaranteed that all entries have already been
+ returned before the an entry is returned the second time.*/
+void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx);
+
+/* Return the oldest entry in the idxset. Fill in its index in *idx. */
+void* pa_idxset_first(pa_idxset *s, uint32_t *idx);
+
+/* Return the entry following the entry indexed by *idx. After the
+ * call *index contains the index of the returned
+ * object. pa_idxset_first() and pa_idxset_next() may be used to
+ * iterate through the set.*/
+void *pa_idxset_next(pa_idxset *s, uint32_t *idx);
+
+/* Call a function for every item in the set. If the callback function
+ returns -1, the loop is terminated. If *del is set to non-zero that
+ specific item is removed. It is not safe to call any other
+ functions on the idxset while pa_idxset_foreach is executed. */
+int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata);
+
+unsigned pa_idxset_size(pa_idxset*s);
+
+int pa_idxset_isempty(pa_idxset *s);
+
+#endif
diff --git a/src/polypcore/inet_ntop.c b/src/polypcore/inet_ntop.c
new file mode 100644
index 00000000..a25c3c95
--- /dev/null
+++ b/src/polypcore/inet_ntop.c
@@ -0,0 +1,80 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <stdio.h>
+#include <errno.h>
+#include <assert.h>
+
+#ifndef HAVE_INET_NTOP
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "winsock.h"
+
+#include "inet_ntop.h"
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
+ struct in_addr *in = (struct in_addr*)src;
+ struct in6_addr *in6 = (struct in6_addr*)src;
+
+ assert(src && dst);
+
+ switch (af) {
+ case AF_INET:
+ snprintf(dst, cnt, "%d.%d.%d.%d",
+#ifdef WORDS_BIGENDIAN
+ (int)(in->s_addr >> 24) & 0xff,
+ (int)(in->s_addr >> 16) & 0xff,
+ (int)(in->s_addr >> 8) & 0xff,
+ (int)(in->s_addr >> 0) & 0xff);
+#else
+ (int)(in->s_addr >> 0) & 0xff,
+ (int)(in->s_addr >> 8) & 0xff,
+ (int)(in->s_addr >> 16) & 0xff,
+ (int)(in->s_addr >> 24) & 0xff);
+#endif
+ break;
+ case AF_INET6:
+ snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
+ in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1],
+ in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3],
+ in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5],
+ in6->s6_addr[ 6] << 8 | in6->s6_addr[ 7],
+ in6->s6_addr[ 8] << 8 | in6->s6_addr[ 9],
+ in6->s6_addr[10] << 8 | in6->s6_addr[11],
+ in6->s6_addr[12] << 8 | in6->s6_addr[13],
+ in6->s6_addr[14] << 8 | in6->s6_addr[15]);
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ return NULL;
+ }
+
+ return dst;
+}
+
+#endif /* INET_NTOP */
diff --git a/src/polypcore/inet_ntop.h b/src/polypcore/inet_ntop.h
new file mode 100644
index 00000000..7fb67b44
--- /dev/null
+++ b/src/polypcore/inet_ntop.h
@@ -0,0 +1,12 @@
+#ifndef fooinet_ntophfoo
+#define fooinet_ntophfoo
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "winsock.h"
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
+
+#endif
diff --git a/src/polypcore/iochannel.c b/src/polypcore/iochannel.c
new file mode 100644
index 00000000..273d47e0
--- /dev/null
+++ b/src/polypcore/iochannel.c
@@ -0,0 +1,292 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <stdlib.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "winsock.h"
+
+#include "iochannel.h"
+#include "util.h"
+#include "socket-util.h"
+#include "xmalloc.h"
+
+struct pa_iochannel {
+ int ifd, ofd;
+ pa_mainloop_api* mainloop;
+
+ pa_iochannel_callback_t callback;
+ void*userdata;
+
+ int readable;
+ int writable;
+ int hungup;
+
+ int no_close;
+
+ pa_io_event* input_event, *output_event;
+};
+
+static void enable_mainloop_sources(pa_iochannel *io) {
+ assert(io);
+
+ if (io->input_event == io->output_event && io->input_event) {
+ pa_io_event_flags_t f = PA_IO_EVENT_NULL;
+ assert(io->input_event);
+
+ if (!io->readable)
+ f |= PA_IO_EVENT_INPUT;
+ if (!io->writable)
+ f |= PA_IO_EVENT_OUTPUT;
+
+ io->mainloop->io_enable(io->input_event, f);
+ } else {
+ if (io->input_event)
+ io->mainloop->io_enable(io->input_event, io->readable ? PA_IO_EVENT_NULL : PA_IO_EVENT_INPUT);
+ if (io->output_event)
+ io->mainloop->io_enable(io->output_event, io->writable ? PA_IO_EVENT_NULL : PA_IO_EVENT_OUTPUT);
+ }
+}
+
+static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+ pa_iochannel *io = userdata;
+ int changed = 0;
+
+ assert(m);
+ assert(e);
+ assert(fd >= 0);
+ assert(userdata);
+
+ if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
+ io->hungup = 1;
+ changed = 1;
+
+ if (e == io->input_event) {
+ io->mainloop->io_free(io->input_event);
+ io->input_event = NULL;
+
+ if (io->output_event == e)
+ io->output_event = NULL;
+ } else if (e == io->output_event) {
+ io->mainloop->io_free(io->output_event);
+ io->output_event = NULL;
+ }
+ } else {
+
+ if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
+ io->readable = 1;
+ changed = 1;
+ assert(e == io->input_event);
+ }
+
+ if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
+ io->writable = 1;
+ changed = 1;
+ assert(e == io->output_event);
+ }
+ }
+
+ if (changed) {
+ enable_mainloop_sources(io);
+
+ if (io->callback)
+ io->callback(io, io->userdata);
+ }
+}
+
+pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
+ pa_iochannel *io;
+
+ assert(m);
+ assert(ifd >= 0 || ofd >= 0);
+
+ io = pa_xnew(pa_iochannel, 1);
+ io->ifd = ifd;
+ io->ofd = ofd;
+ io->mainloop = m;
+
+ io->userdata = NULL;
+ io->callback = NULL;
+ io->readable = 0;
+ io->writable = 0;
+ io->hungup = 0;
+ io->no_close = 0;
+
+ io->input_event = io->output_event = NULL;
+
+ if (ifd == ofd) {
+ assert(ifd >= 0);
+ pa_make_nonblock_fd(io->ifd);
+ io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io);
+ } else {
+
+ if (ifd >= 0) {
+ pa_make_nonblock_fd(io->ifd);
+ io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io);
+ }
+
+ if (ofd >= 0) {
+ pa_make_nonblock_fd(io->ofd);
+ io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io);
+ }
+ }
+
+ return io;
+}
+
+void pa_iochannel_free(pa_iochannel*io) {
+ assert(io);
+
+ if (io->input_event)
+ io->mainloop->io_free(io->input_event);
+
+ if (io->output_event && (io->output_event != io->input_event))
+ io->mainloop->io_free(io->output_event);
+
+ if (!io->no_close) {
+ if (io->ifd >= 0)
+
+ close(io->ifd);
+ if (io->ofd >= 0 && io->ofd != io->ifd)
+ close(io->ofd);
+ }
+
+ pa_xfree(io);
+}
+
+int pa_iochannel_is_readable(pa_iochannel*io) {
+ assert(io);
+
+ return io->readable || io->hungup;
+}
+
+int pa_iochannel_is_writable(pa_iochannel*io) {
+ assert(io);
+
+ return io->writable && !io->hungup;
+}
+
+int pa_iochannel_is_hungup(pa_iochannel*io) {
+ assert(io);
+
+ return io->hungup;
+}
+
+ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
+ ssize_t r;
+
+ assert(io);
+ assert(data);
+ assert(l);
+ assert(io->ofd >= 0);
+
+#ifdef OS_IS_WIN32
+ r = send(io->ofd, data, l, 0);
+ if (r < 0) {
+ if (WSAGetLastError() != WSAENOTSOCK) {
+ errno = WSAGetLastError();
+ return r;
+ }
+ }
+
+ if (r < 0)
+#endif
+ r = write(io->ofd, data, l);
+ if (r >= 0) {
+ io->writable = 0;
+ enable_mainloop_sources(io);
+ }
+
+ return r;
+}
+
+ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
+ ssize_t r;
+
+ assert(io);
+ assert(data);
+ assert(io->ifd >= 0);
+
+#ifdef OS_IS_WIN32
+ r = recv(io->ifd, data, l, 0);
+ if (r < 0) {
+ if (WSAGetLastError() != WSAENOTSOCK) {
+ errno = WSAGetLastError();
+ return r;
+ }
+ }
+
+ if (r < 0)
+#endif
+ r = read(io->ifd, data, l);
+
+ if (r >= 0) {
+ io->readable = 0;
+ enable_mainloop_sources(io);
+ }
+
+ return r;
+}
+
+void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_callback_t _callback, void *userdata) {
+ assert(io);
+
+ io->callback = _callback;
+ io->userdata = userdata;
+}
+
+void pa_iochannel_set_noclose(pa_iochannel*io, int b) {
+ assert(io);
+
+ io->no_close = b;
+}
+
+void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) {
+ assert(io);
+ assert(s);
+ assert(l);
+
+ pa_socket_peer_to_string(io->ifd, s, l);
+}
+
+int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
+ assert(io);
+
+ return pa_socket_set_rcvbuf(io->ifd, l);
+}
+
+int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
+ assert(io);
+
+ return pa_socket_set_sndbuf(io->ofd, l);
+}
+
+pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
+ assert(io);
+
+ return io->mainloop;
+}
diff --git a/src/polypcore/iochannel.h b/src/polypcore/iochannel.h
new file mode 100644
index 00000000..7d355d8f
--- /dev/null
+++ b/src/polypcore/iochannel.h
@@ -0,0 +1,72 @@
+#ifndef fooiochannelhfoo
+#define fooiochannelhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <polyp/mainloop-api.h>
+
+/* A wrapper around UNIX file descriptors for attaching them to the a
+ main event loop. Everytime new data may be read or be written to
+ the channel a callback function is called. It is safe to destroy
+ the calling iochannel object from the callback */
+
+/* When pa_iochannel_is_readable() returns non-zero, the user has to
+ * call this function in a loop until it is no longer set or EOF
+ * reached. Otherwise strange things may happen when an EOF is
+ * reached. */
+
+typedef struct pa_iochannel pa_iochannel;
+
+/* Create a new IO channel for the specified file descriptors for
+input resp. output. It is safe to pass the same file descriptor for
+both parameters (in case of full-duplex channels). For a simplex
+channel specify -1 for the other direction. */
+
+pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd);
+void pa_iochannel_free(pa_iochannel*io);
+
+ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l);
+ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l);
+
+int pa_iochannel_is_readable(pa_iochannel*io);
+int pa_iochannel_is_writable(pa_iochannel*io);
+int pa_iochannel_is_hungup(pa_iochannel*io);
+
+/* Don't close the file descirptors when the io channel is freed. By
+ * default the file descriptors are closed. */
+void pa_iochannel_set_noclose(pa_iochannel*io, int b);
+
+/* Set the callback function that is called whenever data becomes available for read or write */
+typedef void (*pa_iochannel_callback_t)(pa_iochannel*io, void *userdata);
+void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_callback_t callback, void *userdata);
+
+/* In case the file descriptor is a socket, return a pretty-printed string in *s which describes the peer connected */
+void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l);
+
+/* Use setsockopt() to tune the recieve and send buffers of TCP sockets */
+int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l);
+int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l);
+
+pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io);
+
+#endif
diff --git a/src/polypcore/ioline.c b/src/polypcore/ioline.c
new file mode 100644
index 00000000..5b669f5c
--- /dev/null
+++ b/src/polypcore/ioline.c
@@ -0,0 +1,368 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <errno.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ioline.h"
+#include "xmalloc.h"
+#include "log.h"
+
+#define BUFFER_LIMIT (64*1024)
+#define READ_SIZE (1024)
+
+struct pa_ioline {
+ pa_iochannel *io;
+ pa_defer_event *defer_event;
+ pa_mainloop_api *mainloop;
+ int ref;
+ int dead;
+
+ char *wbuf;
+ size_t wbuf_length, wbuf_index, wbuf_valid_length;
+
+ char *rbuf;
+ size_t rbuf_length, rbuf_index, rbuf_valid_length;
+
+ void (*callback)(pa_ioline*io, const char *s, void *userdata);
+ void *userdata;
+
+ int defer_close;
+};
+
+static void io_callback(pa_iochannel*io, void *userdata);
+static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata);
+
+pa_ioline* pa_ioline_new(pa_iochannel *io) {
+ pa_ioline *l;
+ assert(io);
+
+ l = pa_xmalloc(sizeof(pa_ioline));
+ l->io = io;
+ l->dead = 0;
+
+ l->wbuf = NULL;
+ l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
+
+ l->rbuf = NULL;
+ l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0;
+
+ l->callback = NULL;
+ l->userdata = NULL;
+ l->ref = 1;
+
+ l->mainloop = pa_iochannel_get_mainloop_api(io);
+
+ l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
+ l->mainloop->defer_enable(l->defer_event, 0);
+
+ l->defer_close = 0;
+
+ pa_iochannel_set_callback(io, io_callback, l);
+
+ return l;
+}
+
+static void ioline_free(pa_ioline *l) {
+ assert(l);
+
+ if (l->io)
+ pa_iochannel_free(l->io);
+
+ if (l->defer_event)
+ l->mainloop->defer_free(l->defer_event);
+
+ pa_xfree(l->wbuf);
+ pa_xfree(l->rbuf);
+ pa_xfree(l);
+}
+
+void pa_ioline_unref(pa_ioline *l) {
+ assert(l && l->ref >= 1);
+
+ if ((--l->ref) <= 0)
+ ioline_free(l);
+}
+
+pa_ioline* pa_ioline_ref(pa_ioline *l) {
+ assert(l && l->ref >= 1);
+
+ l->ref++;
+ return l;
+}
+
+void pa_ioline_close(pa_ioline *l) {
+ assert(l && l->ref >= 1);
+
+ l->dead = 1;
+ if (l->io) {
+ pa_iochannel_free(l->io);
+ l->io = NULL;
+ }
+
+ if (l->defer_event) {
+ l->mainloop->defer_free(l->defer_event);
+ l->defer_event = NULL;
+ }
+}
+
+void pa_ioline_puts(pa_ioline *l, const char *c) {
+ size_t len;
+ assert(l && c && l->ref >= 1 && !l->dead);
+
+ pa_ioline_ref(l);
+
+ len = strlen(c);
+ if (len > BUFFER_LIMIT - l->wbuf_valid_length)
+ len = BUFFER_LIMIT - l->wbuf_valid_length;
+
+ if (len) {
+ assert(l->wbuf_length >= l->wbuf_valid_length);
+
+ /* In case the allocated buffer is too small, enlarge it. */
+ if (l->wbuf_valid_length + len > l->wbuf_length) {
+ size_t n = l->wbuf_valid_length+len;
+ char *new = pa_xmalloc(n);
+ if (l->wbuf) {
+ memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
+ pa_xfree(l->wbuf);
+ }
+ l->wbuf = new;
+ l->wbuf_length = n;
+ l->wbuf_index = 0;
+ } else if (l->wbuf_index + l->wbuf_valid_length + len > l->wbuf_length) {
+
+ /* In case the allocated buffer fits, but the current index is too far from the start, move it to the front. */
+ memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
+ l->wbuf_index = 0;
+ }
+
+ assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
+
+ /* Append the new string */
+ memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len);
+ l->wbuf_valid_length += len;
+
+ l->mainloop->defer_enable(l->defer_event, 1);
+ }
+
+ pa_ioline_unref(l);
+}
+
+void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) {
+ assert(l && l->ref >= 1);
+ l->callback = callback;
+ l->userdata = userdata;
+}
+
+static void failure(pa_ioline *l) {
+ assert(l && l->ref >= 1 && !l->dead);
+
+ pa_ioline_close(l);
+
+ if (l->callback) {
+ l->callback(l, NULL, l->userdata);
+ l->callback = NULL;
+ }
+}
+
+static void scan_for_lines(pa_ioline *l, size_t skip) {
+ assert(l && l->ref >= 1 && skip < l->rbuf_valid_length);
+
+ while (!l->dead && l->rbuf_valid_length > skip) {
+ char *e, *p;
+ size_t m;
+
+ if (!(e = memchr(l->rbuf + l->rbuf_index + skip, '\n', l->rbuf_valid_length - skip)))
+ break;
+
+ *e = 0;
+
+ p = l->rbuf + l->rbuf_index;
+ m = strlen(p);
+
+ l->rbuf_index += m+1;
+ l->rbuf_valid_length -= m+1;
+
+ /* A shortcut for the next time */
+ if (l->rbuf_valid_length == 0)
+ l->rbuf_index = 0;
+
+ if (l->callback)
+ l->callback(l, p, l->userdata);
+
+ skip = 0;
+ }
+
+ /* If the buffer became too large and still no newline was found, drop it. */
+ if (l->rbuf_valid_length >= BUFFER_LIMIT)
+ l->rbuf_index = l->rbuf_valid_length = 0;
+}
+
+static int do_write(pa_ioline *l);
+
+static int do_read(pa_ioline *l) {
+ assert(l && l->ref >= 1);
+
+ while (!l->dead && pa_iochannel_is_readable(l->io)) {
+ ssize_t r;
+ size_t len;
+
+ len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
+
+ /* Check if we have to enlarge the read buffer */
+ if (len < READ_SIZE) {
+ size_t n = l->rbuf_valid_length+READ_SIZE;
+
+ if (n >= BUFFER_LIMIT)
+ n = BUFFER_LIMIT;
+
+ if (l->rbuf_length >= n) {
+ /* The current buffer is large enough, let's just move the data to the front */
+ if (l->rbuf_valid_length)
+ memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
+ } else {
+ /* Enlarge the buffer */
+ char *new = pa_xmalloc(n);
+ if (l->rbuf_valid_length)
+ memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
+ pa_xfree(l->rbuf);
+ l->rbuf = new;
+ l->rbuf_length = n;
+ }
+
+ l->rbuf_index = 0;
+ }
+
+ len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
+
+ assert(len >= READ_SIZE);
+
+ /* Read some data */
+ r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len);
+ if (r == 0) {
+ /* Got an EOF, so fake an exit command. */
+ l->rbuf_index = 0;
+ snprintf (l->rbuf, l->rbuf_length, "exit\n");
+ r = 5;
+ pa_ioline_puts(l, "\nExiting.\n");
+ do_write(l);
+ } else if (r < 0) {
+ pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+ failure(l);
+ return -1;
+ }
+
+ l->rbuf_valid_length += r;
+
+ /* Look if a line has been terminated in the newly read data */
+ scan_for_lines(l, l->rbuf_valid_length - r);
+ }
+
+ return 0;
+}
+
+/* Try to flush the buffer */
+static int do_write(pa_ioline *l) {
+ ssize_t r;
+ assert(l && l->ref >= 1);
+
+ while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) {
+
+ if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) {
+ pa_log(__FILE__": write() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
+ failure(l);
+ return -1;
+ }
+
+ l->wbuf_index += r;
+ l->wbuf_valid_length -= r;
+
+ /* A shortcut for the next time */
+ if (l->wbuf_valid_length == 0)
+ l->wbuf_index = 0;
+ }
+
+ return 0;
+}
+
+/* Try to flush read/write data */
+static void do_work(pa_ioline *l) {
+ assert(l && l->ref >= 1);
+
+ pa_ioline_ref(l);
+
+ l->mainloop->defer_enable(l->defer_event, 0);
+
+ if (!l->dead)
+ do_write(l);
+
+ if (!l->dead)
+ do_read(l);
+
+ if (l->defer_close && !l->wbuf_valid_length)
+ failure(l);
+
+ pa_ioline_unref(l);
+}
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+ pa_ioline *l = userdata;
+ assert(io && l && l->ref >= 1);
+
+ do_work(l);
+}
+
+static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) {
+ pa_ioline *l = userdata;
+ assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e);
+
+ do_work(l);
+}
+
+void pa_ioline_defer_close(pa_ioline *l) {
+ assert(l);
+
+ l->defer_close = 1;
+
+ if (!l->wbuf_valid_length)
+ l->mainloop->defer_enable(l->defer_event, 1);
+}
+
+void pa_ioline_printf(pa_ioline *s, const char *format, ...) {
+ char *t;
+ va_list ap;
+
+
+ va_start(ap, format);
+ t = pa_vsprintf_malloc(format, ap);
+ va_end(ap);
+
+ pa_ioline_puts(s, t);
+ pa_xfree(t);
+}
diff --git a/src/polypcore/ioline.h b/src/polypcore/ioline.h
new file mode 100644
index 00000000..84ccb47a
--- /dev/null
+++ b/src/polypcore/ioline.h
@@ -0,0 +1,51 @@
+#ifndef fooiolinehfoo
+#define fooiolinehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "iochannel.h"
+#include "util.h"
+
+/* An ioline wraps an iochannel for line based communication. A
+ * callback function is called whenever a new line has been recieved
+ * from the client */
+
+typedef struct pa_ioline pa_ioline;
+
+pa_ioline* pa_ioline_new(pa_iochannel *io);
+void pa_ioline_unref(pa_ioline *l);
+pa_ioline* pa_ioline_ref(pa_ioline *l);
+void pa_ioline_close(pa_ioline *l);
+
+/* Write a string to the channel */
+void pa_ioline_puts(pa_ioline *s, const char *c);
+
+/* Write a string to the channel */
+void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
+
+/* Set the callback function that is called for every recieved line */
+void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata);
+
+/* Make sure to close the ioline object as soon as the send buffer is emptied */
+void pa_ioline_defer_close(pa_ioline *io);
+
+#endif
diff --git a/src/polypcore/llist.h b/src/polypcore/llist.h
new file mode 100644
index 00000000..eb8cd017
--- /dev/null
+++ b/src/polypcore/llist.h
@@ -0,0 +1,69 @@
+#ifndef foollistfoo
+#define foollistfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/* Some macros for maintaining doubly linked lists */
+
+/* The head of the linked list. Use this in the structure that shall
+ * contain the head of the linked list */
+#define PA_LLIST_HEAD(t,name) t *name
+
+/* The pointers in the linked list's items. Use this in the item structure */
+#define PA_LLIST_FIELDS(t) t *next, *prev
+
+/* Initialize the list's head */
+#define PA_LLIST_HEAD_INIT(t,item) do { (item) = NULL; } while(0)
+
+/* Initialize a list item */
+#define PA_LLIST_INIT(t,item) do { \
+ t *_item = (item); \
+ assert(_item); \
+ _item->prev = _item->next = NULL; \
+ } while(0)
+
+/* Prepend an item to the list */
+#define PA_LLIST_PREPEND(t,head,item) do { \
+ t **_head = &(head), *_item = (item); \
+ assert(_item); \
+ if ((_item->next = *_head)) \
+ _item->next->prev = _item; \
+ _item->prev = NULL; \
+ *_head = _item; \
+ } while (0)
+
+/* Remove an item from the list */
+#define PA_LLIST_REMOVE(t,head,item) do { \
+ t **_head = &(head), *_item = (item); \
+ assert(_item); \
+ if (_item->next) \
+ _item->next->prev = _item->prev; \
+ if (_item->prev) \
+ _item->prev->next = _item->next; \
+ else {\
+ assert(*_head == _item); \
+ *_head = _item->next; \
+ } \
+ _item->next = _item->prev = NULL; \
+ } while(0)
+
+#endif
diff --git a/src/polypcore/log.c b/src/polypcore/log.c
new file mode 100644
index 00000000..97406f79
--- /dev/null
+++ b/src/polypcore/log.c
@@ -0,0 +1,150 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; 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 <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#include "log.h"
+#include "xmalloc.h"
+#include "util.h"
+
+#define ENV_LOGLEVEL "POLYP_LOG"
+
+static char *log_ident = NULL;
+static pa_log_target_t log_target = PA_LOG_STDERR;
+static void (*user_log_func)(pa_log_level_t l, const char *s) = NULL;
+static pa_log_level_t maximal_level = PA_LOG_NOTICE;
+
+#ifdef HAVE_SYSLOG_H
+static const int level_to_syslog[] = {
+ [PA_LOG_ERROR] = LOG_ERR,
+ [PA_LOG_WARN] = LOG_WARNING,
+ [PA_LOG_NOTICE] = LOG_NOTICE,
+ [PA_LOG_INFO] = LOG_INFO,
+ [PA_LOG_DEBUG] = LOG_DEBUG
+};
+#endif
+
+void pa_log_set_ident(const char *p) {
+ if (log_ident)
+ pa_xfree(log_ident);
+
+ log_ident = pa_xstrdup(p);
+}
+
+void pa_log_set_maximal_level(pa_log_level_t l) {
+ assert(l < PA_LOG_LEVEL_MAX);
+ maximal_level = l;
+}
+
+void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) {
+ assert(t == PA_LOG_USER || !func);
+ log_target = t;
+ user_log_func = func;
+}
+
+void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
+ const char *e;
+ assert(level < PA_LOG_LEVEL_MAX);
+
+ if ((e = getenv(ENV_LOGLEVEL)))
+ maximal_level = atoi(e);
+
+ if (level > maximal_level)
+ return;
+
+ switch (log_target) {
+ case PA_LOG_STDERR:
+ vfprintf(stderr, format, ap);
+ break;
+
+#ifdef HAVE_SYSLOG_H
+ case PA_LOG_SYSLOG:
+ openlog(log_ident ? log_ident : "???", LOG_PID, LOG_USER);
+ vsyslog(level_to_syslog[level], format, ap);
+ closelog();
+ break;
+#endif
+
+ case PA_LOG_USER: {
+ char *t = pa_vsprintf_malloc(format, ap);
+ assert(user_log_func);
+ user_log_func(level, t);
+ pa_xfree(t);
+ }
+
+ case PA_LOG_NULL:
+ default:
+ break;
+ }
+
+}
+
+void pa_log_level(pa_log_level_t level, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ pa_log_levelv(level, format, ap);
+ va_end(ap);
+}
+
+void pa_log_debug(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ pa_log_levelv(PA_LOG_DEBUG, format, ap);
+ va_end(ap);
+}
+
+void pa_log_info(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ pa_log_levelv(PA_LOG_INFO, format, ap);
+ va_end(ap);
+}
+
+void pa_log_notice(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ pa_log_levelv(PA_LOG_INFO, format, ap);
+ va_end(ap);
+}
+
+void pa_log_warn(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ pa_log_levelv(PA_LOG_WARN, format, ap);
+ va_end(ap);
+}
+
+void pa_log_error(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ pa_log_levelv(PA_LOG_ERROR, format, ap);
+ va_end(ap);
+}
diff --git a/src/polypcore/log.h b/src/polypcore/log.h
new file mode 100644
index 00000000..ce8aea98
--- /dev/null
+++ b/src/polypcore/log.h
@@ -0,0 +1,69 @@
+#ifndef foologhfoo
+#define foologhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <stdarg.h>
+#include "gccmacro.h"
+
+/* A simple logging subsystem */
+
+/* Where to log to */
+typedef enum pa_log_target {
+ PA_LOG_STDERR, /* default */
+ PA_LOG_SYSLOG,
+ PA_LOG_USER, /* to user specified function */
+ PA_LOG_NULL /* to /dev/null */
+} pa_log_target_t;
+
+typedef enum pa_log_level {
+ PA_LOG_ERROR = 0, /* Error messages */
+ PA_LOG_WARN = 1, /* Warning messages */
+ PA_LOG_NOTICE = 2, /* Notice messages */
+ PA_LOG_INFO = 3, /* Info messages */
+ PA_LOG_DEBUG = 4, /* debug message */
+ PA_LOG_LEVEL_MAX
+} pa_log_level_t;
+
+/* Set an identification for the current daemon. Used when logging to syslog. */
+void pa_log_set_ident(const char *p);
+
+/* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */
+void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t t, const char*s));
+
+/* Minimal log level */
+void pa_log_set_maximal_level(pa_log_level_t l);
+
+/* Do a log line */
+void pa_log_debug(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
+void pa_log_info(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
+void pa_log_notice(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
+void pa_log_warn(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
+void pa_log_error(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
+
+void pa_log_level(pa_log_level_t level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
+
+void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap);
+
+#define pa_log pa_log_error
+
+#endif
diff --git a/src/polypcore/mcalign.c b/src/polypcore/mcalign.c
new file mode 100644
index 00000000..4d765625
--- /dev/null
+++ b/src/polypcore/mcalign.c
@@ -0,0 +1,188 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "mcalign.h"
+#include "xmalloc.h"
+
+struct pa_mcalign {
+ size_t base;
+ pa_memchunk leftover, current;
+ pa_memblock_stat *memblock_stat;
+};
+
+pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s) {
+ pa_mcalign *m;
+ assert(base);
+
+ m = pa_xnew(pa_mcalign, 1);
+ m->base = base;
+ pa_memchunk_reset(&m->leftover);
+ pa_memchunk_reset(&m->current);
+ m->memblock_stat = s;
+
+ return m;
+}
+
+void pa_mcalign_free(pa_mcalign *m) {
+ assert(m);
+
+ if (m->leftover.memblock)
+ pa_memblock_unref(m->leftover.memblock);
+
+ if (m->current.memblock)
+ pa_memblock_unref(m->current.memblock);
+
+ pa_xfree(m);
+}
+
+void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
+ assert(m && c && c->memblock && c->length);
+
+ /* Append to the leftover memory block */
+ if (m->leftover.memblock) {
+ assert(!m->current.memblock);
+
+ /* Try to merge */
+ if (m->leftover.memblock == c->memblock &&
+ m->leftover.index + m->leftover.length == c->index) {
+
+ /* Merge */
+ m->leftover.length += c->length;
+
+ /* If the new chunk is larger than m->base, move it to current */
+ if (m->leftover.length >= m->base) {
+ m->current = m->leftover;
+ pa_memchunk_reset(&m->leftover);
+ }
+
+ } else {
+ size_t l;
+
+ /* We have to copy */
+ assert(m->leftover.length < m->base);
+ l = m->base - m->leftover.length;
+
+ if (l > c->length)
+ l = c->length;
+
+ /* Can we use the current block? */
+ pa_memchunk_make_writable(&m->leftover, m->memblock_stat, m->base);
+
+ memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l);
+ m->leftover.length += l;
+
+ assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length);
+
+ if (c->length > l) {
+ /* Save the remainder of the memory block */
+ m->current = *c;
+ m->current.index += l;
+ m->current.length -= l;
+ pa_memblock_ref(m->current.memblock);
+ }
+ }
+ } else {
+ assert(!m->leftover.memblock && !m->current.memblock);
+
+ /* Nothing to merge or copy, just store it */
+
+ if (c->length >= m->base)
+ m->current = *c;
+ else
+ m->leftover = *c;
+
+ pa_memblock_ref(c->memblock);
+ }
+}
+
+int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
+ assert(m && c);
+
+ /* First test if there's a leftover memory block available */
+ if (m->leftover.memblock) {
+ assert(m->leftover.length > 0 && m->leftover.length <= m->base);
+
+ /* The leftover memory block is not yet complete */
+ if (m->leftover.length < m->base)
+ return -1;
+
+ /* Return the leftover memory block */
+ *c = m->leftover;
+ pa_memchunk_reset(&m->leftover);
+
+ /* If the current memblock is too small move it the leftover */
+ if (m->current.memblock && m->current.length < m->base) {
+ m->leftover = m->current;
+ pa_memchunk_reset(&m->current);
+ }
+
+ return 0;
+ }
+
+ /* Now let's see if there is other data available */
+ if (m->current.memblock) {
+ size_t l;
+ assert(m->current.length >= m->base);
+
+ /* The length of the returned memory block */
+ l = m->current.length;
+ l /= m->base;
+ l *= m->base;
+ assert(l > 0);
+
+ /* Prepare the returned block */
+ *c = m->current;
+ pa_memblock_ref(c->memblock);
+ c->length = l;
+
+ /* Drop that from the current memory block */
+ assert(l <= m->current.length);
+ m->current.index += l;
+ m->current.length -= l;
+
+ /* In case the whole block was dropped ... */
+ if (m->current.length == 0)
+ pa_memblock_unref(m->current.memblock);
+ else {
+ /* Move the raimainder to leftover */
+ assert(m->current.length < m->base && !m->leftover.memblock);
+
+ m->leftover = m->current;
+ }
+
+ pa_memchunk_reset(&m->current);
+
+ return 0;
+ }
+
+ /* There's simply nothing */
+ return -1;
+
+}
diff --git a/src/polypcore/mcalign.h b/src/polypcore/mcalign.h
new file mode 100644
index 00000000..5de75bc7
--- /dev/null
+++ b/src/polypcore/mcalign.h
@@ -0,0 +1,77 @@
+#ifndef foomcalignhfoo
+#define foomcalignhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "memblock.h"
+#include "memchunk.h"
+
+/* An alignment object, used for aligning memchunks to multiples of
+ * the frame size. */
+
+/* Method of operation: the user creates a new mcalign object by
+ * calling pa_mcalign_new() with the appropriate aligning
+ * granularity. After that he may call pa_mcalign_push() for an input
+ * memchunk. After exactly one memchunk the user has to call
+ * pa_mcalign_pop() until it returns -1. If pa_mcalign_pop() returns
+ * 0, the memchunk *c is valid and aligned to the granularity. Some
+ * pseudocode illustrating this:
+ *
+ * pa_mcalign *a = pa_mcalign_new(4, NULL);
+ *
+ * for (;;) {
+ * pa_memchunk input;
+ *
+ * ... fill input ...
+ *
+ * pa_mcalign_push(m, &input);
+ * pa_memblock_unref(input.memblock);
+ *
+ * for (;;) {
+ * pa_memchunk output;
+ *
+ * if (pa_mcalign_pop(m, &output) < 0)
+ * break;
+ *
+ * ... consume output ...
+ *
+ * pa_memblock_unref(output.memblock);
+ * }
+ * }
+ *
+ * pa_memchunk_free(a);
+ * */
+
+typedef struct pa_mcalign pa_mcalign;
+
+pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s);
+void pa_mcalign_free(pa_mcalign *m);
+
+/* Push a new memchunk into the aligner. The caller of this routine
+ * has to free the memchunk by himself. */
+void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c);
+
+/* Pop a new memchunk from the aligner. Returns 0 when sucessful,
+ * nonzero otherwise. */
+int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c);
+
+#endif
diff --git a/src/polypcore/memblock.c b/src/polypcore/memblock.c
new file mode 100644
index 00000000..8da53525
--- /dev/null
+++ b/src/polypcore/memblock.c
@@ -0,0 +1,169 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "memblock.h"
+#include "xmalloc.h"
+
+static void stat_add(pa_memblock*m, pa_memblock_stat *s) {
+ assert(m);
+
+ if (!s) {
+ m->stat = NULL;
+ return;
+ }
+
+ m->stat = pa_memblock_stat_ref(s);
+ s->total++;
+ s->allocated++;
+ s->total_size += m->length;
+ s->allocated_size += m->length;
+}
+
+static void stat_remove(pa_memblock *m) {
+ assert(m);
+
+ if (!m->stat)
+ return;
+
+ m->stat->total--;
+ m->stat->total_size -= m->length;
+
+ pa_memblock_stat_unref(m->stat);
+ m->stat = NULL;
+}
+
+pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s) {
+ pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)+length);
+ b->type = PA_MEMBLOCK_APPENDED;
+ b->ref = 1;
+ b->length = length;
+ b->data = b+1;
+ b->free_cb = NULL;
+ b->read_only = 0;
+ stat_add(b, s);
+ return b;
+}
+
+pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, pa_memblock_stat*s) {
+ pa_memblock *b = pa_xmalloc(sizeof(pa_memblock));
+ b->type = PA_MEMBLOCK_DYNAMIC;
+ b->ref = 1;
+ b->length = length;
+ b->data = d;
+ b->free_cb = NULL;
+ b->read_only = 0;
+ stat_add(b, s);
+ return b;
+}
+
+pa_memblock *pa_memblock_new_fixed(void *d, size_t length, int read_only, pa_memblock_stat*s) {
+ pa_memblock *b = pa_xmalloc(sizeof(pa_memblock));
+ b->type = PA_MEMBLOCK_FIXED;
+ b->ref = 1;
+ b->length = length;
+ b->data = d;
+ b->free_cb = NULL;
+ b->read_only = read_only;
+ stat_add(b, s);
+ return b;
+}
+
+pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s) {
+ pa_memblock *b;
+ assert(d && length && free_cb);
+ b = pa_xmalloc(sizeof(pa_memblock));
+ b->type = PA_MEMBLOCK_USER;
+ b->ref = 1;
+ b->length = length;
+ b->data = d;
+ b->free_cb = free_cb;
+ b->read_only = read_only;
+ stat_add(b, s);
+ return b;
+}
+
+pa_memblock* pa_memblock_ref(pa_memblock*b) {
+ assert(b && b->ref >= 1);
+ b->ref++;
+ return b;
+}
+
+void pa_memblock_unref(pa_memblock*b) {
+ assert(b && b->ref >= 1);
+
+ if ((--(b->ref)) == 0) {
+ stat_remove(b);
+
+ if (b->type == PA_MEMBLOCK_USER) {
+ assert(b->free_cb);
+ b->free_cb(b->data);
+ } else if (b->type == PA_MEMBLOCK_DYNAMIC)
+ pa_xfree(b->data);
+
+ pa_xfree(b);
+ }
+}
+
+void pa_memblock_unref_fixed(pa_memblock *b) {
+ assert(b && b->ref >= 1 && b->type == PA_MEMBLOCK_FIXED);
+
+ if (b->ref == 1)
+ pa_memblock_unref(b);
+ else {
+ b->data = pa_xmemdup(b->data, b->length);
+ b->type = PA_MEMBLOCK_DYNAMIC;
+ b->ref--;
+ }
+}
+
+pa_memblock_stat* pa_memblock_stat_new(void) {
+ pa_memblock_stat *s;
+
+ s = pa_xmalloc(sizeof(pa_memblock_stat));
+ s->ref = 1;
+ s->total = s->total_size = s->allocated = s->allocated_size = 0;
+
+ return s;
+}
+
+void pa_memblock_stat_unref(pa_memblock_stat *s) {
+ assert(s && s->ref >= 1);
+
+ if (!(--(s->ref))) {
+ assert(!s->total);
+ pa_xfree(s);
+ }
+}
+
+pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s) {
+ assert(s);
+ s->ref++;
+ return s;
+}
diff --git a/src/polypcore/memblock.h b/src/polypcore/memblock.h
new file mode 100644
index 00000000..c5751406
--- /dev/null
+++ b/src/polypcore/memblock.h
@@ -0,0 +1,87 @@
+#ifndef foomemblockhfoo
+#define foomemblockhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+/* A pa_memblock is a reference counted memory block. Polypaudio
+ * passed references to pa_memblocks around instead of copying
+ * data. See pa_memchunk for a structure that describes parts of
+ * memory blocks. */
+
+/* The type of memory this block points to */
+typedef enum pa_memblock_type {
+ PA_MEMBLOCK_FIXED, /* data is a pointer to fixed memory that needs not to be freed */
+ PA_MEMBLOCK_APPENDED, /* The most common kind: the data is appended to the memory block */
+ PA_MEMBLOCK_DYNAMIC, /* data is a pointer to some memory allocated with pa_xmalloc() */
+ PA_MEMBLOCK_USER /* User supplied memory, to be freed with free_cb */
+} pa_memblock_type_t;
+
+/* A structure of keeping memory block statistics */
+/* Maintains statistics about memory blocks */
+typedef struct pa_memblock_stat {
+ int ref;
+ unsigned total;
+ unsigned total_size;
+ unsigned allocated;
+ unsigned allocated_size;
+} pa_memblock_stat;
+
+typedef struct pa_memblock {
+ pa_memblock_type_t type;
+ unsigned ref; /* the reference counter */
+ int read_only; /* boolean */
+ size_t length;
+ void *data;
+ void (*free_cb)(void *p); /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
+ pa_memblock_stat *stat;
+} pa_memblock;
+
+/* Allocate a new memory block of type PA_MEMBLOCK_APPENDED */
+pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s);
+
+/* Allocate a new memory block of type PA_MEMBLOCK_DYNAMIC. The pointer data is to be maintained be the memory block */
+pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, pa_memblock_stat*s);
+
+/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */
+pa_memblock *pa_memblock_new_fixed(void *data, size_t length, int read_only, pa_memblock_stat*s);
+
+/* Allocate a new memory block of type PA_MEMBLOCK_USER */
+pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s);
+
+void pa_memblock_unref(pa_memblock*b);
+pa_memblock* pa_memblock_ref(pa_memblock*b);
+
+/* This special unref function has to be called by the owner of the
+memory of a static memory block when he wants to release all
+references to the memory. This causes the memory to be copied and
+converted into a PA_MEMBLOCK_DYNAMIC type memory block */
+void pa_memblock_unref_fixed(pa_memblock*b);
+
+
+pa_memblock_stat* pa_memblock_stat_new(void);
+void pa_memblock_stat_unref(pa_memblock_stat *s);
+pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s);
+
+#endif
diff --git a/src/polypcore/memblockq.c b/src/polypcore/memblockq.c
new file mode 100644
index 00000000..ba6b76ea
--- /dev/null
+++ b/src/polypcore/memblockq.c
@@ -0,0 +1,343 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "memblockq.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "mcalign.h"
+
+struct memblock_list {
+ struct memblock_list *next, *prev;
+ pa_memchunk chunk;
+};
+
+struct pa_memblockq {
+ struct memblock_list *blocks, *blocks_tail;
+ unsigned n_blocks;
+ size_t current_length, maxlength, tlength, base, prebuf, orig_prebuf, minreq;
+ pa_mcalign *mcalign;
+ pa_memblock_stat *memblock_stat;
+};
+
+pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq, pa_memblock_stat *s) {
+ pa_memblockq* bq;
+ assert(maxlength && base && maxlength);
+
+ bq = pa_xmalloc(sizeof(pa_memblockq));
+ bq->blocks = bq->blocks_tail = 0;
+ bq->n_blocks = 0;
+
+ bq->current_length = 0;
+
+ pa_log_debug(__FILE__": memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq);
+
+ bq->base = base;
+
+ bq->maxlength = ((maxlength+base-1)/base)*base;
+ assert(bq->maxlength >= base);
+
+ bq->tlength = ((tlength+base-1)/base)*base;
+ if (!bq->tlength || bq->tlength >= bq->maxlength)
+ bq->tlength = bq->maxlength;
+
+ bq->minreq = (minreq/base)*base;
+ if (bq->minreq == 0)
+ bq->minreq = 1;
+
+ bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf;
+ bq->prebuf = (bq->prebuf/base)*base;
+ if (bq->prebuf > bq->maxlength)
+ bq->prebuf = bq->maxlength;
+
+ if (bq->prebuf > bq->tlength - bq->minreq)
+ bq->prebuf = bq->tlength - bq->minreq;
+
+ bq->orig_prebuf = bq->prebuf;
+
+ pa_log_debug(__FILE__": memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq);
+
+ bq->mcalign = NULL;
+
+ bq->memblock_stat = s;
+
+ return bq;
+}
+
+void pa_memblockq_free(pa_memblockq* bq) {
+ assert(bq);
+
+ pa_memblockq_flush(bq);
+
+ if (bq->mcalign)
+ pa_mcalign_free(bq->mcalign);
+
+ pa_xfree(bq);
+}
+
+void pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta) {
+ struct memblock_list *q;
+ assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0);
+
+ pa_memblockq_seek(bq, delta);
+
+ if (bq->blocks_tail && bq->blocks_tail->chunk.memblock == chunk->memblock) {
+ /* Try to merge memory chunks */
+
+ if (bq->blocks_tail->chunk.index+bq->blocks_tail->chunk.length == chunk->index) {
+ bq->blocks_tail->chunk.length += chunk->length;
+ bq->current_length += chunk->length;
+ return;
+ }
+ }
+
+ q = pa_xmalloc(sizeof(struct memblock_list));
+
+ q->chunk = *chunk;
+ pa_memblock_ref(q->chunk.memblock);
+ assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length);
+ q->next = NULL;
+ if ((q->prev = bq->blocks_tail))
+ bq->blocks_tail->next = q;
+ else
+ bq->blocks = q;
+
+ bq->blocks_tail = q;
+
+ bq->n_blocks++;
+ bq->current_length += chunk->length;
+
+ pa_memblockq_shorten(bq, bq->maxlength);
+}
+
+int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
+ assert(bq && chunk);
+
+ if (!bq->blocks || bq->current_length < bq->prebuf)
+ return -1;
+
+ bq->prebuf = 0;
+
+ *chunk = bq->blocks->chunk;
+ pa_memblock_ref(chunk->memblock);
+
+ return 0;
+}
+
+void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length) {
+ assert(bq && chunk && length);
+
+ if (!bq->blocks || memcmp(&bq->blocks->chunk, chunk, sizeof(pa_memchunk)))
+ return;
+
+ assert(length <= bq->blocks->chunk.length);
+ pa_memblockq_skip(bq, length);
+}
+
+static void remove_block(pa_memblockq *bq, struct memblock_list *q) {
+ assert(bq && q);
+
+ if (q->prev)
+ q->prev->next = q->next;
+ else {
+ assert(bq->blocks == q);
+ bq->blocks = q->next;
+ }
+
+ if (q->next)
+ q->next->prev = q->prev;
+ else {
+ assert(bq->blocks_tail == q);
+ bq->blocks_tail = q->prev;
+ }
+
+ pa_memblock_unref(q->chunk.memblock);
+ pa_xfree(q);
+
+ bq->n_blocks--;
+}
+
+void pa_memblockq_skip(pa_memblockq *bq, size_t length) {
+ assert(bq && length && (length % bq->base) == 0);
+
+ while (length > 0) {
+ size_t l = length;
+ assert(bq->blocks && bq->current_length >= length);
+
+ if (l > bq->blocks->chunk.length)
+ l = bq->blocks->chunk.length;
+
+ bq->blocks->chunk.index += l;
+ bq->blocks->chunk.length -= l;
+ bq->current_length -= l;
+
+ if (!bq->blocks->chunk.length)
+ remove_block(bq, bq->blocks);
+
+ length -= l;
+ }
+}
+
+void pa_memblockq_shorten(pa_memblockq *bq, size_t length) {
+ size_t l;
+ assert(bq);
+
+ if (bq->current_length <= length)
+ return;
+
+ /*pa_log(__FILE__": Warning! pa_memblockq_shorten()\n");*/
+
+ l = bq->current_length - length;
+ l /= bq->base;
+ l *= bq->base;
+
+ pa_memblockq_skip(bq, l);
+}
+
+
+void pa_memblockq_empty(pa_memblockq *bq) {
+ assert(bq);
+ pa_memblockq_shorten(bq, 0);
+}
+
+int pa_memblockq_is_readable(pa_memblockq *bq) {
+ assert(bq);
+
+ return bq->current_length && (bq->current_length >= bq->prebuf);
+}
+
+int pa_memblockq_is_writable(pa_memblockq *bq, size_t length) {
+ assert(bq);
+
+ return bq->current_length + length <= bq->tlength;
+}
+
+uint32_t pa_memblockq_get_length(pa_memblockq *bq) {
+ assert(bq);
+ return bq->current_length;
+}
+
+uint32_t pa_memblockq_missing(pa_memblockq *bq) {
+ size_t l;
+ assert(bq);
+
+ if (bq->current_length >= bq->tlength)
+ return 0;
+
+ l = bq->tlength - bq->current_length;
+ assert(l);
+
+ return (l >= bq->minreq) ? l : 0;
+}
+
+void pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta) {
+ pa_memchunk rchunk;
+ assert(bq && chunk && bq->base);
+
+ if (bq->base == 1) {
+ pa_memblockq_push(bq, chunk, delta);
+ return;
+ }
+
+ if (!bq->mcalign) {
+ bq->mcalign = pa_mcalign_new(bq->base, bq->memblock_stat);
+ assert(bq->mcalign);
+ }
+
+ pa_mcalign_push(bq->mcalign, chunk);
+
+ while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
+ pa_memblockq_push(bq, &rchunk, delta);
+ pa_memblock_unref(rchunk.memblock);
+ delta = 0;
+ }
+}
+
+uint32_t pa_memblockq_get_minreq(pa_memblockq *bq) {
+ assert(bq);
+ return bq->minreq;
+}
+
+void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
+ assert(bq);
+ bq->prebuf = 0;
+}
+
+void pa_memblockq_prebuf_reenable(pa_memblockq *bq) {
+ assert(bq);
+ bq->prebuf = bq->orig_prebuf;
+}
+
+void pa_memblockq_seek(pa_memblockq *bq, size_t length) {
+ assert(bq);
+
+ if (!length)
+ return;
+
+ while (length >= bq->base) {
+ size_t l = length;
+ if (!bq->current_length)
+ return;
+
+ assert(bq->blocks_tail);
+
+ if (l > bq->blocks_tail->chunk.length)
+ l = bq->blocks_tail->chunk.length;
+
+ bq->blocks_tail->chunk.length -= l;
+ bq->current_length -= l;
+
+ if (bq->blocks_tail->chunk.length == 0)
+ remove_block(bq, bq->blocks);
+
+ length -= l;
+ }
+}
+
+void pa_memblockq_flush(pa_memblockq *bq) {
+ struct memblock_list *l;
+ assert(bq);
+
+ while ((l = bq->blocks)) {
+ bq->blocks = l->next;
+ pa_memblock_unref(l->chunk.memblock);
+ pa_xfree(l);
+ }
+
+ bq->blocks_tail = NULL;
+ bq->n_blocks = 0;
+ bq->current_length = 0;
+}
+
+uint32_t pa_memblockq_get_tlength(pa_memblockq *bq) {
+ assert(bq);
+ return bq->tlength;
+}
diff --git a/src/polypcore/memblockq.h b/src/polypcore/memblockq.h
new file mode 100644
index 00000000..1695daba
--- /dev/null
+++ b/src/polypcore/memblockq.h
@@ -0,0 +1,104 @@
+#ifndef foomemblockqhfoo
+#define foomemblockqhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include "memblock.h"
+#include "memchunk.h"
+
+/* A memblockq is a queue of pa_memchunks (yepp, the name is not
+ * perfect). It is similar to the ring buffers used by most other
+ * audio software. In contrast to a ring buffer this memblockq data
+ * type doesn't need to copy any data around, it just maintains
+ * references to reference counted memory blocks. */
+
+typedef struct pa_memblockq pa_memblockq;
+
+/* Parameters:
+ - maxlength: maximum length of queue. If more data is pushed into the queue, data from the front is dropped
+ - length: the target length of the queue.
+ - base: a base value for all metrics. Only multiples of this value are popped from the queue
+ - prebuf: before passing the first byte out, make sure that enough bytes are in the queue
+ - minreq: pa_memblockq_missing() will only return values greater than this value
+*/
+pa_memblockq* pa_memblockq_new(size_t maxlength,
+ size_t tlength,
+ size_t base,
+ size_t prebuf,
+ size_t minreq,
+ pa_memblock_stat *s);
+void pa_memblockq_free(pa_memblockq*bq);
+
+/* Push a new memory chunk into the queue. Optionally specify a value for future cancellation. */
+void pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta);
+
+/* Same as pa_memblockq_push(), however chunks are filtered through a mcalign object, and thus aligned to multiples of base */
+void pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta);
+
+/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */
+int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
+
+/* Drop the specified bytes from the queue, only valid aufter pa_memblockq_peek() */
+void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length);
+
+/* Drop the specified bytes from the queue */
+void pa_memblockq_skip(pa_memblockq *bq, size_t length);
+
+/* Shorten the pa_memblockq to the specified length by dropping data at the end of the queue */
+void pa_memblockq_shorten(pa_memblockq *bq, size_t length);
+
+/* Empty the pa_memblockq */
+void pa_memblockq_empty(pa_memblockq *bq);
+
+/* Test if the pa_memblockq is currently readable, that is, more data than base */
+int pa_memblockq_is_readable(pa_memblockq *bq);
+
+/* Test if the pa_memblockq is currently writable for the specified amount of bytes */
+int pa_memblockq_is_writable(pa_memblockq *bq, size_t length);
+
+/* Return the length of the queue in bytes */
+uint32_t pa_memblockq_get_length(pa_memblockq *bq);
+
+/* Return how many bytes are missing in queue to the specified fill amount */
+uint32_t pa_memblockq_missing(pa_memblockq *bq);
+
+/* Returns the minimal request value */
+uint32_t pa_memblockq_get_minreq(pa_memblockq *bq);
+
+/* Force disabling of pre-buf even when the pre-buffer is not yet filled */
+void pa_memblockq_prebuf_disable(pa_memblockq *bq);
+
+/* Reenable pre-buf to the initial level */
+void pa_memblockq_prebuf_reenable(pa_memblockq *bq);
+
+/* Manipulate the write pointer */
+void pa_memblockq_seek(pa_memblockq *bq, size_t delta);
+
+/* Flush the queue */
+void pa_memblockq_flush(pa_memblockq *bq);
+
+/* Get Target length */
+uint32_t pa_memblockq_get_tlength(pa_memblockq *bq);
+
+#endif
diff --git a/src/polypcore/memchunk.c b/src/polypcore/memchunk.c
new file mode 100644
index 00000000..bfd74f9e
--- /dev/null
+++ b/src/polypcore/memchunk.c
@@ -0,0 +1,58 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "memchunk.h"
+#include "xmalloc.h"
+
+void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min) {
+ pa_memblock *n;
+ size_t l;
+ assert(c && c->memblock && c->memblock->ref >= 1);
+
+ if (c->memblock->ref == 1 && !c->memblock->read_only && c->memblock->length >= c->index+min)
+ return;
+
+ l = c->length;
+ if (l < min)
+ l = min;
+
+ n = pa_memblock_new(l, s);
+ memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length);
+ pa_memblock_unref(c->memblock);
+ c->memblock = n;
+ c->index = 0;
+}
+
+void pa_memchunk_reset(pa_memchunk *c) {
+ assert(c);
+
+ c->memblock = NULL;
+ c->length = c->index = 0;
+}
diff --git a/src/polypcore/memchunk.h b/src/polypcore/memchunk.h
new file mode 100644
index 00000000..4eefc8c1
--- /dev/null
+++ b/src/polypcore/memchunk.h
@@ -0,0 +1,45 @@
+#ifndef foomemchunkhfoo
+#define foomemchunkhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "memblock.h"
+
+/* A memchunk describes a part of a memblock. In contrast to the memblock, a
+ * memchunk is not allocated dynamically or reference counted, instead
+ * it is usually stored on the stack and copied around */
+
+typedef struct pa_memchunk {
+ pa_memblock *memblock;
+ size_t index, length;
+} pa_memchunk;
+
+/* Make a memchunk writable, i.e. make sure that the caller may have
+ * exclusive access to the memblock and it is not read_only. If needed
+ * the memblock in the structure is replaced by a copy. */
+void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min);
+
+/* Invalidate a memchunk. This does not free the cotaining memblock,
+ * but sets all members to zero. */
+void pa_memchunk_reset(pa_memchunk *c);
+
+#endif
diff --git a/src/polypcore/modargs.c b/src/polypcore/modargs.c
new file mode 100644
index 00000000..07062946
--- /dev/null
+++ b/src/polypcore/modargs.c
@@ -0,0 +1,260 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <ctype.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hashmap.h"
+#include "modargs.h"
+#include "idxset.h"
+#include "sample-util.h"
+#include "namereg.h"
+#include "sink.h"
+#include "source.h"
+#include "xmalloc.h"
+#include "util.h"
+
+struct entry {
+ char *key, *value;
+};
+
+static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) {
+ struct entry *e;
+ assert(map && key && value);
+
+ if (valid_keys) {
+ const char*const* v;
+ for (v = valid_keys; *v; v++)
+ if (strcmp(*v, key) == 0)
+ break;
+
+ if (!*v) {
+ pa_xfree(key);
+ pa_xfree(value);
+ return -1;
+ }
+ }
+
+ e = pa_xmalloc(sizeof(struct entry));
+ e->key = key;
+ e->value = value;
+ pa_hashmap_put(map, key, e);
+ return 0;
+}
+
+pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
+ pa_hashmap *map = NULL;
+
+ map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ assert(map);
+
+ if (args) {
+ enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state;
+ const char *p, *key, *value;
+ size_t key_len = 0, value_len = 0;
+
+ key = value = NULL;
+ state = WHITESPACE;
+ for (p = args; *p; p++) {
+ switch (state) {
+ case WHITESPACE:
+ if (*p == '=')
+ goto fail;
+ else if (!isspace(*p)) {
+ key = p;
+ state = KEY;
+ key_len = 1;
+ }
+ break;
+ case KEY:
+ if (*p == '=')
+ state = VALUE_START;
+ else
+ key_len++;
+ break;
+ case VALUE_START:
+ if (*p == '\'') {
+ state = VALUE_TICKS;
+ value = p+1;
+ value_len = 0;
+ } else if (*p == '"') {
+ state = VALUE_DOUBLE_QUOTES;
+ value = p+1;
+ value_len = 0;
+ } else if (isspace(*p)) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
+ goto fail;
+ state = WHITESPACE;
+ } else {
+ state = VALUE_SIMPLE;
+ value = p;
+ value_len = 1;
+ }
+ break;
+ case VALUE_SIMPLE:
+ if (isspace(*p)) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ goto fail;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
+ case VALUE_DOUBLE_QUOTES:
+ if (*p == '"') {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ goto fail;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
+ case VALUE_TICKS:
+ if (*p == '\'') {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ goto fail;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
+ }
+ }
+
+ if (state == VALUE_START) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
+ goto fail;
+ } else if (state == VALUE_SIMPLE) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0)
+ goto fail;
+ } else if (state != WHITESPACE)
+ goto fail;
+ }
+
+ return (pa_modargs*) map;
+
+fail:
+
+ if (map)
+ pa_modargs_free((pa_modargs*) map);
+
+ return NULL;
+}
+
+
+static void free_func(void *p, PA_GCC_UNUSED void*userdata) {
+ struct entry *e = p;
+ assert(e);
+ pa_xfree(e->key);
+ pa_xfree(e->value);
+ pa_xfree(e);
+}
+
+void pa_modargs_free(pa_modargs*ma) {
+ pa_hashmap *map = (pa_hashmap*) ma;
+ pa_hashmap_free(map, free_func, NULL);
+}
+
+const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def) {
+ pa_hashmap *map = (pa_hashmap*) ma;
+ struct entry*e;
+
+ if (!(e = pa_hashmap_get(map, key)))
+ return def;
+
+ return e->value;
+}
+
+int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {
+ const char *v;
+ assert(ma && key && value);
+
+ if (!(v = pa_modargs_get_value(ma, key, NULL)))
+ return 0;
+
+ if (pa_atou(v, value) < 0)
+ return -1;
+
+ return 0;
+}
+
+int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {
+ const char *v;
+ assert(ma && key && value);
+
+ if (!(v = pa_modargs_get_value(ma, key, NULL)))
+ return 0;
+
+ if (pa_atoi(v, value) < 0)
+ return -1;
+
+ return 0;
+}
+
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) {
+ const char *v;
+ int r;
+ assert(ma && key && value);
+
+ if (!(v = pa_modargs_get_value(ma, key, NULL)))
+ return 0;
+
+ if (!*v)
+ return -1;
+
+ if ((r = pa_parse_boolean(v)) < 0)
+ return -1;
+
+ *value = r;
+ return 0;
+}
+
+int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
+ const char *format;
+ uint32_t channels;
+ pa_sample_spec ss;
+ assert(ma && rss);
+
+/* DEBUG_TRAP;*/
+
+ ss = *rss;
+ if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0)
+ return -1;
+
+ channels = ss.channels;
+ if ((pa_modargs_get_value_u32(ma, "channels", &channels)) < 0)
+ return -1;
+ ss.channels = (uint8_t) channels;
+
+ if ((format = pa_modargs_get_value(ma, "format", NULL)))
+ if ((ss.format = pa_parse_sample_format(format)) < 0)
+ return -1;
+
+ if (!pa_sample_spec_valid(&ss))
+ return -1;
+
+ *rss = ss;
+
+ return 0;
+}
diff --git a/src/polypcore/modargs.h b/src/polypcore/modargs.h
new file mode 100644
index 00000000..678ea1a7
--- /dev/null
+++ b/src/polypcore/modargs.h
@@ -0,0 +1,49 @@
+#ifndef foomodargshfoo
+#define foomodargshfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <polyp/sample.h>
+#include "core.h"
+
+typedef struct pa_modargs pa_modargs;
+
+/* A generic parser for module arguments */
+
+/* Parse the string args. The NULL-terminated array keys contains all valid arguments. */
+pa_modargs *pa_modargs_new(const char *args, const char* const keys[]);
+void pa_modargs_free(pa_modargs*ma);
+
+/* Return the module argument for the specified name as a string. If
+ * the argument was not specified, return def instead.*/
+const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def);
+
+/* Return a module argument as unsigned 32bit value in *value */
+int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value);
+int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value);
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value);
+
+/* Return sample spec data from the three arguments "rate", "format" and "channels" */
+int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss);
+
+#endif
diff --git a/src/polypcore/modinfo.c b/src/polypcore/modinfo.c
new file mode 100644
index 00000000..53440612
--- /dev/null
+++ b/src/polypcore/modinfo.c
@@ -0,0 +1,84 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; 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 <ltdl.h>
+#include <assert.h>
+
+#include "xmalloc.h"
+#include "util.h"
+#include "modinfo.h"
+#include "log.h"
+
+#define PA_SYMBOL_AUTHOR "pa__get_author"
+#define PA_SYMBOL_DESCRIPTION "pa__get_description"
+#define PA_SYMBOL_USAGE "pa__get_usage"
+#define PA_SYMBOL_VERSION "pa__get_version"
+
+pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) {
+ pa_modinfo *i;
+ const char* (*func)(void);
+ assert(dl);
+
+ i = pa_xmalloc0(sizeof(pa_modinfo));
+
+ if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_AUTHOR)))
+ i->author = pa_xstrdup(func());
+
+ if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_DESCRIPTION)))
+ i->description = pa_xstrdup(func());
+
+ if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_USAGE)))
+ i->usage = pa_xstrdup(func());
+
+ if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_VERSION)))
+ i->version = pa_xstrdup(func());
+
+ return i;
+}
+
+pa_modinfo *pa_modinfo_get_by_name(const char *name) {
+ lt_dlhandle dl;
+ pa_modinfo *i;
+ assert(name);
+
+ if (!(dl = lt_dlopenext(name))) {
+ pa_log(__FILE__": Failed to open module \"%s\": %s\n", name, lt_dlerror());
+ return NULL;
+ }
+
+ i = pa_modinfo_get_by_handle(dl);
+ lt_dlclose(dl);
+
+ return i;
+}
+
+void pa_modinfo_free(pa_modinfo *i) {
+ assert(i);
+ pa_xfree(i->author);
+ pa_xfree(i->description);
+ pa_xfree(i->usage);
+ pa_xfree(i->version);
+ pa_xfree(i);
+}
diff --git a/src/polypcore/modinfo.h b/src/polypcore/modinfo.h
new file mode 100644
index 00000000..53176147
--- /dev/null
+++ b/src/polypcore/modinfo.h
@@ -0,0 +1,43 @@
+#ifndef foomodinfohfoo
+#define foomodinfohfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/* Some functions for reading module meta data from Polypaudio modules */
+
+typedef struct pa_modinfo {
+ char *author;
+ char *description;
+ char *usage;
+ char *version;
+} pa_modinfo;
+
+/* Read meta data from an libtool handle */
+pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl);
+
+/* Read meta data from a module file */
+pa_modinfo *pa_modinfo_get_by_name(const char *name);
+
+/* Free meta data */
+void pa_modinfo_free(pa_modinfo *i);
+
+#endif
diff --git a/src/polypcore/module.c b/src/polypcore/module.c
new file mode 100644
index 00000000..499ea299
--- /dev/null
+++ b/src/polypcore/module.c
@@ -0,0 +1,275 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include "module.h"
+#include "xmalloc.h"
+#include "subscribe.h"
+#include "log.h"
+#include "util.h"
+
+#define PA_SYMBOL_INIT "pa__init"
+#define PA_SYMBOL_DONE "pa__done"
+
+#define UNLOAD_POLL_TIME 2
+
+static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+ pa_core *c = userdata;
+ struct timeval ntv;
+ assert(c && c->mainloop == m && c->module_auto_unload_event == e);
+
+ pa_module_unload_unused(c);
+
+ pa_gettimeofday(&ntv);
+ ntv.tv_sec += UNLOAD_POLL_TIME;
+ m->time_restart(e, &ntv);
+}
+
+pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
+ pa_module *m = NULL;
+ int r;
+
+ assert(c && name);
+
+ if (c->disallow_module_loading)
+ goto fail;
+
+ m = pa_xmalloc(sizeof(pa_module));
+
+ m->name = pa_xstrdup(name);
+ m->argument = pa_xstrdup(argument);
+
+ if (!(m->dl = lt_dlopenext(name))) {
+ pa_log(__FILE__": Failed to open module \"%s\": %s\n", name, lt_dlerror());
+ goto fail;
+ }
+
+ if (!(m->init = (int (*)(pa_core *_c, pa_module*_m)) lt_dlsym(m->dl, PA_SYMBOL_INIT))) {
+ pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.\n", name);
+ goto fail;
+ }
+
+ if (!(m->done = (void (*)(pa_core *_c, pa_module*_m)) lt_dlsym(m->dl, PA_SYMBOL_DONE))) {
+ pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.\n", name);
+ goto fail;
+ }
+
+ m->userdata = NULL;
+ m->core = c;
+ m->n_used = -1;
+ m->auto_unload = 0;
+ m->unload_requested = 0;
+
+ assert(m->init);
+ if (m->init(c, m) < 0) {
+ pa_log_error(__FILE__": Failed to load module \"%s\" (argument: \"%s\"): initialization failed.\n", name, argument ? argument : "");
+ goto fail;
+ }
+
+ if (!c->modules)
+ c->modules = pa_idxset_new(NULL, NULL);
+
+ if (!c->module_auto_unload_event) {
+ struct timeval ntv;
+ pa_gettimeofday(&ntv);
+ ntv.tv_sec += UNLOAD_POLL_TIME;
+ c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
+ }
+ assert(c->module_auto_unload_event);
+
+ assert(c->modules);
+ r = pa_idxset_put(c->modules, m, &m->index);
+ assert(r >= 0 && m->index != PA_IDXSET_INVALID);
+
+ pa_log_info(__FILE__": Loaded \"%s\" (index: #%u; argument: \"%s\").\n", m->name, m->index, m->argument ? m->argument : "");
+
+ pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
+
+ return m;
+
+fail:
+
+ if (m) {
+ pa_xfree(m->argument);
+ pa_xfree(m->name);
+
+ if (m->dl)
+ lt_dlclose(m->dl);
+
+ pa_xfree(m);
+ }
+
+ return NULL;
+}
+
+static void pa_module_free(pa_module *m) {
+ assert(m && m->done && m->core);
+
+ if (m->core->disallow_module_loading)
+ return;
+
+ pa_log_info(__FILE__": Unloading \"%s\" (index: #%u).\n", m->name, m->index);
+
+ m->done(m->core, m);
+
+ lt_dlclose(m->dl);
+
+ pa_log_info(__FILE__": Unloaded \"%s\" (index: #%u).\n", m->name, m->index);
+
+ pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
+
+ pa_xfree(m->name);
+ pa_xfree(m->argument);
+ pa_xfree(m);
+}
+
+void pa_module_unload(pa_core *c, pa_module *m) {
+ assert(c && m);
+
+ assert(c->modules);
+ if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))
+ return;
+
+ pa_module_free(m);
+}
+
+void pa_module_unload_by_index(pa_core *c, uint32_t idx) {
+ pa_module *m;
+ assert(c && idx != PA_IDXSET_INVALID);
+
+ assert(c->modules);
+ if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
+ return;
+
+ pa_module_free(m);
+}
+
+static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {
+ pa_module *m = p;
+ assert(m);
+ pa_module_free(m);
+}
+
+void pa_module_unload_all(pa_core *c) {
+ assert(c);
+
+ if (!c->modules)
+ return;
+
+ pa_idxset_free(c->modules, free_callback, NULL);
+ c->modules = NULL;
+
+ if (c->module_auto_unload_event) {
+ c->mainloop->time_free(c->module_auto_unload_event);
+ c->module_auto_unload_event = NULL;
+ }
+
+ if (c->module_defer_unload_event) {
+ c->mainloop->defer_free(c->module_defer_unload_event);
+ c->module_defer_unload_event = NULL;
+ }
+}
+
+static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) {
+ pa_module *m = p;
+ time_t *now = userdata;
+ assert(p && del && now);
+
+ if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) {
+ pa_module_free(m);
+ *del = 1;
+ }
+
+ return 0;
+}
+
+void pa_module_unload_unused(pa_core *c) {
+ time_t now;
+ assert(c);
+
+ if (!c->modules)
+ return;
+
+ time(&now);
+ pa_idxset_foreach(c->modules, unused_callback, &now);
+}
+
+static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) {
+ pa_module *m = p;
+ assert(m);
+
+ if (m->unload_requested) {
+ pa_module_free(m);
+ *del = 1;
+ }
+
+ return 0;
+}
+
+static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
+ pa_core *core = userdata;
+ api->defer_enable(e, 0);
+
+ if (!core->modules)
+ return;
+
+ pa_idxset_foreach(core->modules, unload_callback, NULL);
+
+}
+
+void pa_module_unload_request(pa_module *m) {
+ assert(m);
+
+ m->unload_requested = 1;
+
+ if (!m->core->module_defer_unload_event)
+ m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
+
+ m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
+}
+
+void pa_module_set_used(pa_module*m, int used) {
+ assert(m);
+
+ if (m->n_used != used)
+ pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
+
+ if (m->n_used != used && used == 0)
+ time(&m->last_used_time);
+
+ m->n_used = used;
+}
+
+pa_modinfo *pa_module_get_info(pa_module *m) {
+ assert(m);
+
+ return pa_modinfo_get_by_handle(m->dl);
+}
diff --git a/src/polypcore/module.h b/src/polypcore/module.h
new file mode 100644
index 00000000..6f137c15
--- /dev/null
+++ b/src/polypcore/module.h
@@ -0,0 +1,70 @@
+#ifndef foomodulehfoo
+#define foomodulehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <ltdl.h>
+
+#include "core.h"
+#include "modinfo.h"
+
+typedef struct pa_module pa_module;
+
+struct pa_module {
+ pa_core *core;
+ char *name, *argument;
+ uint32_t index;
+
+ lt_dlhandle dl;
+
+ int (*init)(pa_core *c, pa_module*m);
+ void (*done)(pa_core *c, pa_module*m);
+
+ void *userdata;
+
+ int n_used;
+ int auto_unload;
+ time_t last_used_time;
+
+ int unload_requested;
+};
+
+pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
+void pa_module_unload(pa_core *c, pa_module *m);
+void pa_module_unload_by_index(pa_core *c, uint32_t idx);
+
+void pa_module_unload_all(pa_core *c);
+void pa_module_unload_unused(pa_core *c);
+
+void pa_module_unload_request(pa_module *m);
+
+void pa_module_set_used(pa_module*m, int used);
+
+#define PA_MODULE_AUTHOR(s) const char * pa__get_author(void) { return s; }
+#define PA_MODULE_DESCRIPTION(s) const char * pa__get_description(void) { return s; }
+#define PA_MODULE_USAGE(s) const char * pa__get_usage(void) { return s; }
+#define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; }
+
+pa_modinfo *pa_module_get_info(pa_module *m);
+
+#endif
diff --git a/src/polypcore/namereg.c b/src/polypcore/namereg.c
new file mode 100644
index 00000000..07fb485c
--- /dev/null
+++ b/src/polypcore/namereg.c
@@ -0,0 +1,212 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "namereg.h"
+#include "autoload.h"
+#include "source.h"
+#include "sink.h"
+#include "xmalloc.h"
+#include "subscribe.h"
+#include "util.h"
+
+struct namereg_entry {
+ pa_namereg_type_t type;
+ char *name;
+ void *data;
+};
+
+void pa_namereg_free(pa_core *c) {
+ assert(c);
+ if (!c->namereg)
+ return;
+ assert(pa_hashmap_size(c->namereg) == 0);
+ pa_hashmap_free(c->namereg, NULL, NULL);
+}
+
+const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail) {
+ struct namereg_entry *e;
+ char *n = NULL;
+ int r;
+
+ assert(c && name && data);
+
+ if (!c->namereg) {
+ c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ assert(c->namereg);
+ }
+
+ if ((e = pa_hashmap_get(c->namereg, name)) && fail)
+ return NULL;
+
+ if (!e)
+ n = pa_xstrdup(name);
+ else {
+ unsigned i;
+ size_t l = strlen(name);
+ n = pa_xmalloc(l+3);
+
+ for (i = 1; i <= 99; i++) {
+ snprintf(n, l+2, "%s%u", name, i);
+
+ if (!(e = pa_hashmap_get(c->namereg, n)))
+ break;
+ }
+
+ if (e) {
+ pa_xfree(n);
+ return NULL;
+ }
+ }
+
+ assert(n);
+ e = pa_xmalloc(sizeof(struct namereg_entry));
+ e->type = type;
+ e->name = n;
+ e->data = data;
+
+ r = pa_hashmap_put(c->namereg, e->name, e);
+ assert (r >= 0);
+
+ return e->name;
+
+}
+
+void pa_namereg_unregister(pa_core *c, const char *name) {
+ struct namereg_entry *e;
+ assert(c && name);
+
+ e = pa_hashmap_remove(c->namereg, name);
+ assert(e);
+
+ pa_xfree(e->name);
+ pa_xfree(e);
+}
+
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) {
+ struct namereg_entry *e;
+ uint32_t idx;
+ assert(c);
+
+ if (!name) {
+ if (type == PA_NAMEREG_SOURCE)
+ name = pa_namereg_get_default_source_name(c);
+ else if (type == PA_NAMEREG_SINK)
+ name = pa_namereg_get_default_sink_name(c);
+ }
+
+ if (!name)
+ return NULL;
+
+ if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
+ if (e->type == e->type)
+ return e->data;
+
+ if (pa_atou(name, &idx) < 0) {
+
+ if (autoload) {
+ pa_autoload_request(c, name, type);
+
+ if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
+ if (e->type == e->type)
+ return e->data;
+ }
+
+ return NULL;
+ }
+
+ if (type == PA_NAMEREG_SINK)
+ return pa_idxset_get_by_index(c->sinks, idx);
+ else if (type == PA_NAMEREG_SOURCE)
+ return pa_idxset_get_by_index(c->sources, idx);
+ else if (type == PA_NAMEREG_SAMPLE && c->scache)
+ return pa_idxset_get_by_index(c->scache, idx);
+
+ return NULL;
+}
+
+void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) {
+ char **s;
+ assert(c && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
+
+ s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name;
+ assert(s);
+
+ if (!name && !*s)
+ return;
+
+ if (name && *s && !strcmp(name, *s))
+ return;
+
+ pa_xfree(*s);
+ *s = pa_xstrdup(name);
+ pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+}
+
+const char *pa_namereg_get_default_sink_name(pa_core *c) {
+ pa_sink *s;
+ assert(c);
+
+ if (c->default_sink_name)
+ return c->default_sink_name;
+
+ if ((s = pa_idxset_first(c->sinks, NULL)))
+ pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK);
+
+ if (c->default_sink_name)
+ return c->default_sink_name;
+
+ return NULL;
+}
+
+const char *pa_namereg_get_default_source_name(pa_core *c) {
+ pa_source *s;
+ uint32_t idx;
+
+ assert(c);
+
+ if (c->default_source_name)
+ return c->default_source_name;
+
+ for (s = pa_idxset_first(c->sources, &idx); s; s = pa_idxset_next(c->sources, &idx))
+ if (!s->monitor_of) {
+ pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
+ break;
+ }
+
+ if (!c->default_source_name)
+ if ((s = pa_idxset_first(c->sources, NULL)))
+ pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
+
+ if (c->default_source_name)
+ return c->default_source_name;
+
+ return NULL;
+}
diff --git a/src/polypcore/namereg.h b/src/polypcore/namereg.h
new file mode 100644
index 00000000..961fd44b
--- /dev/null
+++ b/src/polypcore/namereg.h
@@ -0,0 +1,43 @@
+#ifndef foonamereghfoo
+#define foonamereghfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "core.h"
+
+typedef enum pa_namereg_type {
+ PA_NAMEREG_SINK,
+ PA_NAMEREG_SOURCE,
+ PA_NAMEREG_SAMPLE
+} pa_namereg_type_t;
+
+void pa_namereg_free(pa_core *c);
+
+const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail);
+void pa_namereg_unregister(pa_core *c, const char *name);
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload);
+void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
+
+const char *pa_namereg_get_default_sink_name(pa_core *c);
+const char *pa_namereg_get_default_source_name(pa_core *c);
+
+#endif
diff --git a/src/polypcore/native-common.h b/src/polypcore/native-common.h
new file mode 100644
index 00000000..78ae721e
--- /dev/null
+++ b/src/polypcore/native-common.h
@@ -0,0 +1,114 @@
+#ifndef foonativecommonhfoo
+#define foonativecommonhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/cdecl.h>
+#include <polyp/polyplib-def.h>
+
+PA_C_DECL_BEGIN
+
+enum {
+ PA_COMMAND_ERROR,
+ PA_COMMAND_TIMEOUT, /* pseudo command */
+ PA_COMMAND_REPLY,
+ PA_COMMAND_CREATE_PLAYBACK_STREAM,
+ PA_COMMAND_DELETE_PLAYBACK_STREAM,
+ PA_COMMAND_CREATE_RECORD_STREAM,
+ PA_COMMAND_DELETE_RECORD_STREAM,
+ PA_COMMAND_EXIT,
+ PA_COMMAND_REQUEST,
+ PA_COMMAND_AUTH,
+ PA_COMMAND_SET_CLIENT_NAME,
+ PA_COMMAND_LOOKUP_SINK,
+ PA_COMMAND_LOOKUP_SOURCE,
+ PA_COMMAND_DRAIN_PLAYBACK_STREAM,
+ PA_COMMAND_PLAYBACK_STREAM_KILLED,
+ PA_COMMAND_RECORD_STREAM_KILLED,
+ PA_COMMAND_STAT,
+ PA_COMMAND_GET_PLAYBACK_LATENCY,
+ PA_COMMAND_CREATE_UPLOAD_STREAM,
+ PA_COMMAND_DELETE_UPLOAD_STREAM,
+ PA_COMMAND_FINISH_UPLOAD_STREAM,
+ PA_COMMAND_PLAY_SAMPLE,
+ PA_COMMAND_REMOVE_SAMPLE,
+
+ PA_COMMAND_GET_SERVER_INFO,
+ PA_COMMAND_GET_SINK_INFO,
+ PA_COMMAND_GET_SINK_INFO_LIST,
+ PA_COMMAND_GET_SOURCE_INFO,
+ PA_COMMAND_GET_SOURCE_INFO_LIST,
+ PA_COMMAND_GET_MODULE_INFO,
+ PA_COMMAND_GET_MODULE_INFO_LIST,
+ PA_COMMAND_GET_CLIENT_INFO,
+ PA_COMMAND_GET_CLIENT_INFO_LIST,
+ PA_COMMAND_GET_SINK_INPUT_INFO,
+ PA_COMMAND_GET_SINK_INPUT_INFO_LIST,
+ PA_COMMAND_GET_SOURCE_OUTPUT_INFO,
+ PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST,
+ PA_COMMAND_GET_SAMPLE_INFO,
+ PA_COMMAND_GET_SAMPLE_INFO_LIST,
+ PA_COMMAND_SUBSCRIBE,
+ PA_COMMAND_SUBSCRIBE_EVENT,
+
+ PA_COMMAND_SET_SINK_VOLUME,
+ PA_COMMAND_SET_SINK_INPUT_VOLUME,
+ PA_COMMAND_CORK_PLAYBACK_STREAM,
+ PA_COMMAND_FLUSH_PLAYBACK_STREAM,
+ PA_COMMAND_TRIGGER_PLAYBACK_STREAM,
+
+ PA_COMMAND_SET_DEFAULT_SINK,
+ PA_COMMAND_SET_DEFAULT_SOURCE,
+
+ PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
+ PA_COMMAND_SET_RECORD_STREAM_NAME,
+
+ PA_COMMAND_KILL_CLIENT,
+ PA_COMMAND_KILL_SINK_INPUT,
+ PA_COMMAND_KILL_SOURCE_OUTPUT,
+ PA_COMMAND_LOAD_MODULE,
+ PA_COMMAND_UNLOAD_MODULE,
+ PA_COMMAND_ADD_AUTOLOAD,
+ PA_COMMAND_REMOVE_AUTOLOAD,
+ PA_COMMAND_GET_AUTOLOAD_INFO,
+ PA_COMMAND_GET_AUTOLOAD_INFO_LIST,
+ PA_COMMAND_GET_RECORD_LATENCY,
+ PA_COMMAND_CORK_RECORD_STREAM,
+ PA_COMMAND_FLUSH_RECORD_STREAM,
+ PA_COMMAND_PREBUF_PLAYBACK_STREAM,
+ PA_COMMAND_MAX
+};
+
+#define PA_NATIVE_COOKIE_LENGTH 256
+#define PA_NATIVE_COOKIE_FILE ".polypaudio-cookie"
+
+#define PA_NATIVE_DEFAULT_PORT 4713
+
+#define PA_NATIVE_COOKIE_PROPERTY_NAME "protocol-native-cookie"
+#define PA_NATIVE_SERVER_PROPERTY_NAME "protocol-native-server"
+
+#define PA_NATIVE_DEFAULT_UNIX_SOCKET "native"
+
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/polypcore/oss-util.c b/src/polypcore/oss-util.c
new file mode 100644
index 00000000..ae6772fd
--- /dev/null
+++ b/src/polypcore/oss-util.c
@@ -0,0 +1,167 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "oss-util.h"
+#include "util.h"
+#include "log.h"
+
+int pa_oss_open(const char *device, int *mode, int* pcaps) {
+ int fd = -1;
+ assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY));
+
+ if (*mode == O_RDWR) {
+ if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) {
+ int dcaps, *tcaps;
+ ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
+
+ tcaps = pcaps ? pcaps : &dcaps;
+
+ if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (*tcaps & DSP_CAP_DUPLEX)
+ return fd;
+
+ goto fail;
+ }
+
+ if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) {
+ if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) {
+ pa_log(__FILE__": open('%s'): %s\n", device, strerror(errno));
+ goto fail;
+ }
+ }
+ } else {
+ if ((fd = open(device, *mode|O_NDELAY)) < 0) {
+ pa_log(__FILE__": open('%s'): %s\n", device, strerror(errno));
+ goto fail;
+ }
+ }
+
+ if (pcaps) {
+ if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
+ goto fail;
+ }
+ }
+
+ pa_fd_set_cloexec(fd, 1);
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+ return -1;
+}
+
+int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
+ int format, channels, speed, reqformat;
+ static const int format_trans[PA_SAMPLE_MAX] = {
+ [PA_SAMPLE_U8] = AFMT_U8,
+ [PA_SAMPLE_ALAW] = AFMT_A_LAW,
+ [PA_SAMPLE_ULAW] = AFMT_MU_LAW,
+ [PA_SAMPLE_S16LE] = AFMT_S16_LE,
+ [PA_SAMPLE_S16BE] = AFMT_S16_BE,
+ [PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
+ };
+
+ assert(fd >= 0 && ss);
+
+ reqformat = format = format_trans[ss->format];
+ if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) {
+ format = AFMT_S16_NE;
+ if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) {
+ int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE;
+ format = f;
+ if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) {
+ format = AFMT_U8;
+ if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) {
+ pa_log(__FILE__": SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno));
+ return -1;
+ } else
+ ss->format = PA_SAMPLE_U8;
+ } else
+ ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
+ } else
+ ss->format = PA_SAMPLE_S16NE;
+ }
+
+ channels = ss->channels;
+ if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_CHANNELS: %s\n", strerror(errno));
+ return -1;
+ }
+ assert(channels);
+ ss->channels = channels;
+
+ speed = ss->rate;
+ if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_SPEED: %s\n", strerror(errno));
+ return -1;
+ }
+ assert(speed);
+ ss->rate = speed;
+
+ return 0;
+}
+
+static int simple_log2(int v) {
+ int k = 0;
+
+ for (;;) {
+ v >>= 1;
+ if (!v) break;
+ k++;
+ }
+
+ return k;
+}
+
+int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
+ int arg;
+ arg = ((int) nfrags << 16) | simple_log2(frag_size);
+
+ if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
+ pa_log(__FILE__": SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/polypcore/oss-util.h b/src/polypcore/oss-util.h
new file mode 100644
index 00000000..6b2746cc
--- /dev/null
+++ b/src/polypcore/oss-util.h
@@ -0,0 +1,32 @@
+#ifndef fooossutilhfoo
+#define fooossutilhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/sample.h>
+
+int pa_oss_open(const char *device, int *mode, int* pcaps);
+int pa_oss_auto_format(int fd, pa_sample_spec *ss);
+
+int pa_oss_set_fragments(int fd, int frags, int frag_size);
+
+#endif
diff --git a/src/polypcore/packet.c b/src/polypcore/packet.c
new file mode 100644
index 00000000..b3a2e074
--- /dev/null
+++ b/src/polypcore/packet.c
@@ -0,0 +1,69 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <assert.h>
+#include <stdlib.h>
+
+#include "packet.h"
+#include "xmalloc.h"
+
+pa_packet* pa_packet_new(size_t length) {
+ pa_packet *p;
+ assert(length);
+ p = pa_xmalloc(sizeof(pa_packet)+length);
+ p->ref = 1;
+ p->length = length;
+ p->data = (uint8_t*) (p+1);
+ p->type = PA_PACKET_APPENDED;
+ return p;
+}
+
+pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length) {
+ pa_packet *p;
+ assert(data && length);
+ p = pa_xmalloc(sizeof(pa_packet));
+ p->ref = 1;
+ p->length = length;
+ p->data = data;
+ p->type = PA_PACKET_DYNAMIC;
+ return p;
+}
+
+pa_packet* pa_packet_ref(pa_packet *p) {
+ assert(p && p->ref >= 1);
+ p->ref++;
+ return p;
+}
+
+void pa_packet_unref(pa_packet *p) {
+ assert(p && p->ref >= 1);
+ p->ref--;
+
+ if (p->ref == 0) {
+ if (p->type == PA_PACKET_DYNAMIC)
+ pa_xfree(p->data);
+ pa_xfree(p);
+ }
+}
diff --git a/src/polypcore/packet.h b/src/polypcore/packet.h
new file mode 100644
index 00000000..0ac47485
--- /dev/null
+++ b/src/polypcore/packet.h
@@ -0,0 +1,41 @@
+#ifndef foopackethfoo
+#define foopackethfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+typedef struct pa_packet {
+ enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type;
+ unsigned ref;
+ size_t length;
+ uint8_t *data;
+} pa_packet;
+
+pa_packet* pa_packet_new(size_t length);
+pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length);
+
+pa_packet* pa_packet_ref(pa_packet *p);
+void pa_packet_unref(pa_packet *p);
+
+#endif
diff --git a/src/polypcore/parseaddr.c b/src/polypcore/parseaddr.c
new file mode 100644
index 00000000..5e4c689c
--- /dev/null
+++ b/src/polypcore/parseaddr.c
@@ -0,0 +1,112 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <assert.h>
+#include <stdlib.h>
+
+#include "xmalloc.h"
+#include "util.h"
+#include "parseaddr.h"
+
+/* Parse addresses in one of the following forms:
+ * HOSTNAME
+ * HOSTNAME:PORT
+ * [HOSTNAME]
+ * [HOSTNAME]:PORT
+ *
+ * Return a newly allocated string of the hostname and fill in *ret_port if specified */
+
+static char *parse_host(const char *s, uint16_t *ret_port) {
+ assert(s && ret_port);
+ if (*s == '[') {
+ char *e;
+ if (!(e = strchr(s+1, ']')))
+ return NULL;
+
+ if (e[1] == ':')
+ *ret_port = atoi(e+2);
+ else if (e[1] != 0)
+ return NULL;
+
+ return pa_xstrndup(s+1, e-s-1);
+ } else {
+ char *e;
+
+ if (!(e = strrchr(s, ':')))
+ return pa_xstrdup(s);
+
+ *ret_port = atoi(e+1);
+ return pa_xstrndup(s, e-s);
+ }
+}
+
+int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
+ const char *p;
+ assert(name && ret_p);
+ memset(ret_p, 0, sizeof(pa_parsed_address));
+ ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
+
+ if (*name == '{') {
+ char hn[256], *pfx;
+ /* The URL starts with a host specification for detecting local connections */
+
+ if (!pa_get_host_name(hn, sizeof(hn)))
+ return -1;
+
+ pfx = pa_sprintf_malloc("{%s}", hn);
+ if (!pa_startswith(name, pfx)) {
+ pa_xfree(pfx);
+ /* Not local */
+ return -1;
+ }
+
+ p = name + strlen(pfx);
+ pa_xfree(pfx);
+ } else
+ p = name;
+
+ if (*p == '/')
+ ret_p->type = PA_PARSED_ADDRESS_UNIX;
+ else if (pa_startswith(p, "unix:")) {
+ ret_p->type = PA_PARSED_ADDRESS_UNIX;
+ p += sizeof("unix:")-1;
+ } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) {
+ ret_p->type = PA_PARSED_ADDRESS_TCP4;
+ p += sizeof("tcp:")-1;
+ } else if (pa_startswith(p, "tcp6:")) {
+ ret_p->type = PA_PARSED_ADDRESS_TCP6;
+ p += sizeof("tcp6:")-1;
+ }
+
+ if (ret_p->type == PA_PARSED_ADDRESS_UNIX)
+ ret_p->path_or_host = pa_xstrdup(p);
+ else
+ if (!(ret_p->path_or_host = parse_host(p, &ret_p->port)))
+ return -1;
+
+
+ return 0;
+}
diff --git a/src/polypcore/parseaddr.h b/src/polypcore/parseaddr.h
new file mode 100644
index 00000000..eff9dd3b
--- /dev/null
+++ b/src/polypcore/parseaddr.h
@@ -0,0 +1,42 @@
+#ifndef fooparseaddrhfoo
+#define fooparseaddrhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+typedef enum pa_parsed_address_type {
+ PA_PARSED_ADDRESS_UNIX,
+ PA_PARSED_ADDRESS_TCP4,
+ PA_PARSED_ADDRESS_TCP6,
+ PA_PARSED_ADDRESS_TCP_AUTO
+} pa_parsed_address_type_t;
+
+typedef struct pa_parsed_address {
+ pa_parsed_address_type_t type;
+ char *path_or_host;
+ uint16_t port;
+} pa_parsed_address;
+
+int pa_parse_address(const char *a, pa_parsed_address *ret_p);
+
+#endif
diff --git a/src/polypcore/pdispatch.c b/src/polypcore/pdispatch.c
new file mode 100644
index 00000000..5a50a0d5
--- /dev/null
+++ b/src/polypcore/pdispatch.c
@@ -0,0 +1,295 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "pdispatch.h"
+#include "native-common.h"
+#include "xmalloc.h"
+#include "llist.h"
+#include "log.h"
+#include "util.h"
+
+/*#define DEBUG_OPCODES */
+
+#ifdef DEBUG_OPCODES
+
+static const char *command_names[PA_COMMAND_MAX] = {
+ [PA_COMMAND_ERROR] = "ERROR",
+ [PA_COMMAND_TIMEOUT] = "TIMEOUT",
+ [PA_COMMAND_REPLY] = "REPLY",
+ [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
+ [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
+ [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
+ [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
+ [PA_COMMAND_AUTH] = "AUTH",
+ [PA_COMMAND_REQUEST] = "REQUEST",
+ [PA_COMMAND_EXIT] = "EXIT",
+ [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME",
+ [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
+ [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
+ [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
+ [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
+ [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
+ [PA_COMMAND_STAT] = "STAT",
+ [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY",
+ [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
+ [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
+ [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM",
+ [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE",
+ [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE",
+ [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO",
+ [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO",
+ [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST",
+ [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO",
+ [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST",
+ [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO",
+ [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST",
+ [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO",
+ [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST",
+ [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO",
+ [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST",
+ [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO",
+ [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST",
+ [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO",
+ [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST",
+ [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE",
+ [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
+ [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
+ [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
+ [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
+ [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
+ [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
+ [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO",
+ [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST",
+};
+
+#endif
+
+struct reply_info {
+ pa_pdispatch *pdispatch;
+ PA_LLIST_FIELDS(struct reply_info);
+ pa_pdispatch_callback callback;
+ void *userdata;
+ uint32_t tag;
+ pa_time_event *time_event;
+};
+
+struct pa_pdispatch {
+ int ref;
+ pa_mainloop_api *mainloop;
+ const pa_pdispatch_callback *callback_table;
+ unsigned n_commands;
+ PA_LLIST_HEAD(struct reply_info, replies);
+ pa_pdispatch_drain_callback drain_callback;
+ void *drain_userdata;
+};
+
+static void reply_info_free(struct reply_info *r) {
+ assert(r && r->pdispatch && r->pdispatch->mainloop);
+
+ if (r->time_event)
+ r->pdispatch->mainloop->time_free(r->time_event);
+
+ PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
+
+ pa_xfree(r);
+}
+
+pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_callback*table, unsigned entries) {
+ pa_pdispatch *pd;
+ assert(mainloop);
+
+ assert((entries && table) || (!entries && !table));
+
+ pd = pa_xmalloc(sizeof(pa_pdispatch));
+ pd->ref = 1;
+ pd->mainloop = mainloop;
+ pd->callback_table = table;
+ pd->n_commands = entries;
+ PA_LLIST_HEAD_INIT(pa_reply_info, pd->replies);
+ pd->drain_callback = NULL;
+ pd->drain_userdata = NULL;
+
+ return pd;
+}
+
+static void pdispatch_free(pa_pdispatch *pd) {
+ assert(pd);
+
+ while (pd->replies)
+ reply_info_free(pd->replies);
+
+ pa_xfree(pd);
+}
+
+static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
+ pa_pdispatch_callback callback;
+ void *userdata;
+ uint32_t tag;
+ assert(r);
+
+ pa_pdispatch_ref(pd);
+
+ callback = r->callback;
+ userdata = r->userdata;
+ tag = r->tag;
+
+ reply_info_free(r);
+
+ callback(pd, command, tag, ts, userdata);
+
+ if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
+ pd->drain_callback(pd, pd->drain_userdata);
+
+ pa_pdispatch_unref(pd);
+}
+
+int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, void *userdata) {
+ uint32_t tag, command;
+ pa_tagstruct *ts = NULL;
+ int ret = -1;
+ assert(pd && packet && packet->data);
+
+ pa_pdispatch_ref(pd);
+
+ if (packet->length <= 8)
+ goto finish;
+
+ ts = pa_tagstruct_new(packet->data, packet->length);
+ assert(ts);
+
+ if (pa_tagstruct_getu32(ts, &command) < 0 ||
+ pa_tagstruct_getu32(ts, &tag) < 0)
+ goto finish;
+
+#ifdef DEBUG_OPCODES
+{
+ char t[256];
+ char const *p;
+ if (!(p = command_names[command]))
+ snprintf((char*) (p = t), sizeof(t), "%u", command);
+
+ pa_log(__FILE__": Recieved opcode <%s>\n", p);
+}
+#endif
+
+ if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
+ struct reply_info *r;
+
+ for (r = pd->replies; r; r = r->next)
+ if (r->tag == tag)
+ break;
+
+ if (r)
+ run_action(pd, r, command, ts);
+
+ } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
+ const pa_pdispatch_callback *c = pd->callback_table+command;
+
+ (*c)(pd, command, tag, ts, userdata);
+ } else {
+ pa_log(__FILE__": Recieved unsupported command %u\n", command);
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+ if (ts)
+ pa_tagstruct_free(ts);
+
+ pa_pdispatch_unref(pd);
+
+ return ret;
+}
+
+static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+ struct reply_info*r = userdata;
+ assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
+
+ run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
+}
+
+void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_callback cb, void *userdata) {
+ struct reply_info *r;
+ struct timeval tv;
+ assert(pd && pd->ref >= 1 && cb);
+
+ r = pa_xmalloc(sizeof(struct reply_info));
+ r->pdispatch = pd;
+ r->callback = cb;
+ r->userdata = userdata;
+ r->tag = tag;
+
+ pa_gettimeofday(&tv);
+ tv.tv_sec += timeout;
+
+ r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r);
+ assert(r->time_event);
+
+ PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
+}
+
+int pa_pdispatch_is_pending(pa_pdispatch *pd) {
+ assert(pd);
+
+ return !!pd->replies;
+}
+
+void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
+ assert(pd);
+ assert(!cb || pa_pdispatch_is_pending(pd));
+
+ pd->drain_callback = cb;
+ pd->drain_userdata = userdata;
+}
+
+void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
+ struct reply_info *r, *n;
+ assert(pd);
+
+ for (r = pd->replies; r; r = n) {
+ n = r->next;
+
+ if (r->userdata == userdata)
+ reply_info_free(r);
+ }
+}
+
+void pa_pdispatch_unref(pa_pdispatch *pd) {
+ assert(pd && pd->ref >= 1);
+
+ if (!(--(pd->ref)))
+ pdispatch_free(pd);
+}
+
+pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
+ assert(pd && pd->ref >= 1);
+ pd->ref++;
+ return pd;
+}
diff --git a/src/polypcore/pdispatch.h b/src/polypcore/pdispatch.h
new file mode 100644
index 00000000..fdd4d7f5
--- /dev/null
+++ b/src/polypcore/pdispatch.h
@@ -0,0 +1,50 @@
+#ifndef foopdispatchhfoo
+#define foopdispatchhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include "tagstruct.h"
+#include "packet.h"
+#include <polyp/mainloop-api.h>
+
+typedef struct pa_pdispatch pa_pdispatch;
+
+typedef void (*pa_pdispatch_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, const pa_pdispatch_callback*table, unsigned entries);
+void pa_pdispatch_unref(pa_pdispatch *pd);
+pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd);
+
+int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, void *userdata);
+
+void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_callback callback, void *userdata);
+
+int pa_pdispatch_is_pending(pa_pdispatch *pd);
+
+typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata);
+void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata);
+
+/* Remove all reply slots with the give userdata parameter */
+void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata);
+
+#endif
diff --git a/src/polypcore/pid.c b/src/polypcore/pid.c
new file mode 100644
index 00000000..ae3dc7f5
--- /dev/null
+++ b/src/polypcore/pid.c
@@ -0,0 +1,286 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; 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 <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <signal.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include "pid.h"
+#include "util.h"
+#include "log.h"
+
+/* Read the PID data from the file descriptor fd, and return it. If no
+ * pid could be read, return 0, on failure (pid_t) -1 */
+static pid_t read_pid(const char *fn, int fd) {
+ ssize_t r;
+ char t[20], *e;
+ uint32_t pid;
+
+ assert(fn && fd >= 0);
+
+ if ((r = pa_loop_read(fd, t, sizeof(t)-1)) < 0) {
+ pa_log(__FILE__": WARNING: failed to read PID file '%s': %s\n", fn, strerror(errno));
+ return (pid_t) -1;
+ }
+
+ if (r == 0)
+ return (pid_t) 0;
+
+ t[r] = 0;
+ if ((e = strchr(t, '\n')))
+ *e = 0;
+
+ if (pa_atou(t, &pid) < 0) {
+ pa_log(__FILE__": WARNING: failed to parse PID file '%s'\n", fn);
+ return (pid_t) -1;
+ }
+
+ return (pid_t) pid;
+}
+
+static int open_pid_file(const char *fn, int mode) {
+ int fd = -1;
+ int lock = -1;
+
+ for (;;) {
+ struct stat st;
+
+ pa_make_secure_parent_dir(fn);
+
+ if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) {
+ if (mode != O_RDONLY || errno != ENOENT)
+ pa_log(__FILE__": WARNING: failed to open PID file '%s': %s\n", fn, strerror(errno));
+ goto fail;
+ }
+
+ /* Try to lock the file. If that fails, go without */
+ if (pa_lock_fd(fd, 1) < 0)
+ goto fail;
+
+ if (fstat(fd, &st) < 0) {
+ pa_log(__FILE__": Failed to fstat() PID file '%s': %s\n", fn, strerror(errno));
+ goto fail;
+ }
+
+ /* Does the file still exist in the file system? When ye, w're done, otherwise restart */
+ if (st.st_nlink >= 1)
+ break;
+
+ if (pa_lock_fd(fd, 0) < 0)
+ goto fail;
+
+ if (close(fd) < 0) {
+ pa_log(__FILE__": Failed to close file '%s': %s\n", fn, strerror(errno));
+ goto fail;
+ }
+
+ fd = -1;
+ }
+
+ return fd;
+
+fail:
+
+ if (fd < 0) {
+ if (lock >= 0)
+ pa_lock_fd(fd, 0);
+
+ close(fd);
+ }
+
+ return -1;
+}
+
+/* Create a new PID file for the current process. */
+int pa_pid_file_create(void) {
+ int fd = -1;
+ int ret = -1;
+ char fn[PATH_MAX];
+ char t[20];
+ pid_t pid;
+ size_t l;
+
+#ifdef OS_IS_WIN32
+ HANDLE process;
+#endif
+
+ pa_runtime_path("pid", fn, sizeof(fn));
+
+ if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
+ goto fail;
+
+ if ((pid = read_pid(fn, fd)) == (pid_t) -1)
+ pa_log(__FILE__": corrupt PID file, overwriting.\n");
+ else if (pid > 0) {
+#ifdef OS_IS_WIN32
+ if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
+ CloseHandle(process);
+#else
+ if (kill(pid, 0) >= 0 || errno != ESRCH) {
+#endif
+ pa_log(__FILE__": daemon already running.\n");
+ goto fail;
+ }
+
+ pa_log(__FILE__": stale PID file, overwriting.\n");
+ }
+
+ /* Overwrite the current PID file */
+ if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) {
+ pa_log(__FILE__": failed to truncate PID fil: %s.\n", strerror(errno));
+ goto fail;
+ }
+
+ snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
+ l = strlen(t);
+
+ if (pa_loop_write(fd, t, l) != (ssize_t) l) {
+ pa_log(__FILE__": failed to write PID file.\n");
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+ if (fd >= 0) {
+ pa_lock_fd(fd, 0);
+ close(fd);
+ }
+
+ return ret;
+}
+
+/* Remove the PID file, if it is ours */
+int pa_pid_file_remove(void) {
+ int fd = -1;
+ char fn[PATH_MAX];
+ int ret = -1;
+ pid_t pid;
+
+ pa_runtime_path("pid", fn, sizeof(fn));
+
+ if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
+ pa_log(__FILE__": WARNING: failed to open PID file '%s': %s\n", fn, strerror(errno));
+ goto fail;
+ }
+
+ if ((pid = read_pid(fn, fd)) == (pid_t) -1)
+ goto fail;
+
+ if (pid != getpid()) {
+ pa_log(__FILE__": WARNING: PID file '%s' not mine!\n", fn);
+ goto fail;
+ }
+
+ if (ftruncate(fd, 0) < 0) {
+ pa_log(__FILE__": failed to truncate PID file '%s': %s\n", fn, strerror(errno));
+ goto fail;
+ }
+
+#ifdef OS_IS_WIN32
+ pa_lock_fd(fd, 0);
+ close(fd);
+ fd = -1;
+#endif
+
+ if (unlink(fn) < 0) {
+ pa_log(__FILE__": failed to remove PID file '%s': %s\n", fn, strerror(errno));
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+
+ if (fd >= 0) {
+ pa_lock_fd(fd, 0);
+ close(fd);
+ }
+
+ return ret;
+}
+
+/* Check whether the daemon is currently running, i.e. if a PID file
+ * exists and the PID therein too. Returns 0 on succcess, -1
+ * otherwise. If pid is non-NULL and a running daemon was found,
+ * return its PID therein */
+int pa_pid_file_check_running(pid_t *pid) {
+ return pa_pid_file_kill(0, pid);
+}
+
+#ifndef OS_IS_WIN32
+
+/* Kill a current running daemon. Return non-zero on success, -1
+ * otherwise. If successful *pid contains the PID of the daemon
+ * process. */
+int pa_pid_file_kill(int sig, pid_t *pid) {
+ int fd = -1;
+ char fn[PATH_MAX];
+ int ret = -1;
+ pid_t _pid;
+
+ if (!pid)
+ pid = &_pid;
+
+ pa_runtime_path("pid", fn, sizeof(fn));
+
+ if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
+ goto fail;
+
+ if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
+ goto fail;
+
+ ret = kill(*pid, sig);
+
+fail:
+
+ if (fd >= 0) {
+ pa_lock_fd(fd, 0);
+ close(fd);
+ }
+
+ return ret;
+
+}
+
+#else /* OS_IS_WIN32 */
+
+int pa_pid_file_kill(int sig, pid_t *pid) {
+ return -1;
+}
+
+#endif
diff --git a/src/polypcore/pid.h b/src/polypcore/pid.h
new file mode 100644
index 00000000..906ab6da
--- /dev/null
+++ b/src/polypcore/pid.h
@@ -0,0 +1,30 @@
+#ifndef foopidhfoo
+#define foopidhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+int pa_pid_file_create(void);
+int pa_pid_file_remove(void);
+int pa_pid_file_check_running(pid_t *pid);
+int pa_pid_file_kill(int sig, pid_t *pid);
+
+#endif
diff --git a/src/polypcore/play-memchunk.c b/src/polypcore/play-memchunk.c
new file mode 100644
index 00000000..e24d4427
--- /dev/null
+++ b/src/polypcore/play-memchunk.c
@@ -0,0 +1,118 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "play-memchunk.h"
+#include "sink-input.h"
+#include "xmalloc.h"
+#include "gccmacro.h"
+
+static void sink_input_kill(pa_sink_input *i) {
+ pa_memchunk *c;
+ assert(i && i->userdata);
+ c = i->userdata;
+
+ pa_sink_input_disconnect(i);
+ pa_sink_input_unref(i);
+
+ pa_memblock_unref(c->memblock);
+ pa_xfree(c);
+}
+
+static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
+ pa_memchunk *c;
+ assert(i && chunk && i->userdata);
+ c = i->userdata;
+
+ if (c->length <= 0)
+ return -1;
+
+ assert(c->memblock && c->memblock->length);
+ *chunk = *c;
+ pa_memblock_ref(c->memblock);
+
+ return 0;
+}
+
+static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) {
+ sink_input_kill(i);
+}
+
+static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) {
+ pa_memchunk *c;
+ assert(i && length && i->userdata);
+ c = i->userdata;
+
+ assert(!memcmp(chunk, c, sizeof(chunk)));
+ assert(length <= c->length);
+
+ c->length -= length;
+ c->index += length;
+
+ if (c->length <= 0)
+ pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i);
+}
+
+int pa_play_memchunk(
+ pa_sink *sink,
+ const char *name,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const pa_memchunk *chunk,
+ pa_cvolume *cvolume) {
+
+ pa_sink_input *si;
+ pa_memchunk *nchunk;
+
+ assert(sink);
+ assert(ss);
+ assert(chunk);
+
+ if (cvolume && pa_cvolume_is_muted(cvolume))
+ return 0;
+
+ if (!(si = pa_sink_input_new(sink, name, __FILE__, ss, map, 0, PA_RESAMPLER_INVALID)))
+ return -1;
+
+ if (cvolume)
+ si->volume = *cvolume;
+
+ si->peek = sink_input_peek;
+ si->drop = sink_input_drop;
+ si->kill = sink_input_kill;
+
+ si->userdata = nchunk = pa_xnew(pa_memchunk, 1);
+ *nchunk = *chunk;
+
+ pa_memblock_ref(chunk->memblock);
+
+ pa_sink_notify(sink);
+
+ return 0;
+}
diff --git a/src/polypcore/play-memchunk.h b/src/polypcore/play-memchunk.h
new file mode 100644
index 00000000..fc0654a6
--- /dev/null
+++ b/src/polypcore/play-memchunk.h
@@ -0,0 +1,36 @@
+#ifndef fooplaychunkhfoo
+#define fooplaychunkhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "sink.h"
+#include "memchunk.h"
+
+int pa_play_memchunk(
+ pa_sink *sink,
+ const char *name,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const pa_memchunk *chunk,
+ pa_cvolume *cvolume);
+
+#endif
diff --git a/src/polypcore/poll.c b/src/polypcore/poll.c
new file mode 100644
index 00000000..193fc812
--- /dev/null
+++ b/src/polypcore/poll.c
@@ -0,0 +1,190 @@
+/* $Id$ */
+
+/***
+ Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
+ Copyright (C) 2005, Cendio AB.
+ This file is part of polypaudio.
+ Based on work for the GNU C Library.
+
+ polypaudio is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ polypaudio 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with polypaudio; If not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA.
+***/
+
+/* Poll the file descriptors described by the NFDS structures starting at
+ FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
+ an event to occur; if TIMEOUT is -1, block until an event occurs.
+ Returns the number of file descriptors with events, zero if timed out,
+ or -1 for errors. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include "winsock.h"
+
+#ifndef HAVE_SYS_POLL_H
+
+#include "util.h"
+#include "poll.h"
+
+int poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
+ struct timeval tv;
+ fd_set rset, wset, xset;
+ struct pollfd *f;
+ int ready;
+ int maxfd = 0;
+ char data[64];
+
+ FD_ZERO (&rset);
+ FD_ZERO (&wset);
+ FD_ZERO (&xset);
+
+ if (nfds == 0) {
+ if (timeout >= 0) {
+ pa_msleep(timeout);
+ return 0;
+ }
+
+#ifdef OS_IS_WIN32
+ /*
+ * Windows does not support signals properly so waiting for them would
+ * mean a deadlock.
+ */
+ pa_msleep(100);
+ return 0;
+#else
+ return select(0, NULL, NULL, NULL, NULL);
+#endif
+ }
+
+ for (f = fds; f < &fds[nfds]; ++f) {
+ if (f->fd != -1) {
+ if (f->events & POLLIN)
+ FD_SET (f->fd, &rset);
+ if (f->events & POLLOUT)
+ FD_SET (f->fd, &wset);
+ if (f->events & POLLPRI)
+ FD_SET (f->fd, &xset);
+ if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
+ maxfd = f->fd;
+ }
+ }
+
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+
+ ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset,
+ SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
+ SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv));
+ if ((ready == -1) && (errno == EBADF)) {
+ ready = 0;
+
+ FD_ZERO (&rset);
+ FD_ZERO (&wset);
+ FD_ZERO (&xset);
+
+ maxfd = -1;
+
+ for (f = fds; f < &fds[nfds]; ++f) {
+ if (f->fd != -1) {
+ fd_set sngl_rset, sngl_wset, sngl_xset;
+
+ FD_ZERO (&sngl_rset);
+ FD_ZERO (&sngl_wset);
+ FD_ZERO (&sngl_xset);
+
+ if (f->events & POLLIN)
+ FD_SET (f->fd, &sngl_rset);
+ if (f->events & POLLOUT)
+ FD_SET (f->fd, &sngl_wset);
+ if (f->events & POLLPRI)
+ FD_SET (f->fd, &sngl_xset);
+ if (f->events & (POLLIN|POLLOUT|POLLPRI)) {
+ struct timeval singl_tv;
+
+ singl_tv.tv_sec = 0;
+ singl_tv.tv_usec = 0;
+
+ if (select((SELECT_TYPE_ARG1) f->fd, SELECT_TYPE_ARG234 &rset,
+ SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
+ SELECT_TYPE_ARG5 &singl_tv) != -1) {
+ if (f->events & POLLIN)
+ FD_SET (f->fd, &rset);
+ if (f->events & POLLOUT)
+ FD_SET (f->fd, &wset);
+ if (f->events & POLLPRI)
+ FD_SET (f->fd, &xset);
+ if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
+ maxfd = f->fd;
+ ++ready;
+ } else if (errno == EBADF)
+ f->revents |= POLLNVAL;
+ }
+ }
+ }
+
+ if (ready) {
+ /* Linux alters the tv struct... but it shouldn't matter here ...
+ * as we're going to be a little bit out anyway as we've just eaten
+ * more than a couple of cpu cycles above */
+ ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset,
+ SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
+ SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv));
+ }
+ }
+
+#ifdef OS_IS_WIN32
+ errno = WSAGetLastError();
+#endif
+
+ if (ready > 0) {
+ ready = 0;
+ for (f = fds; f < &fds[nfds]; ++f) {
+ f->revents = 0;
+ if (f->fd != -1) {
+ if (FD_ISSET (f->fd, &rset)) {
+ /* support for POLLHUP. An hung up descriptor does not
+ increase the return value! */
+ if (recv (f->fd, data, 64, MSG_PEEK) == -1) {
+ if (errno == ESHUTDOWN || errno == ECONNRESET ||
+ errno == ECONNABORTED || errno == ENETRESET) {
+ fprintf(stderr, "Hangup\n");
+ f->revents |= POLLHUP;
+ }
+ }
+
+ if (f->revents == 0)
+ f->revents |= POLLIN;
+ }
+ if (FD_ISSET (f->fd, &wset))
+ f->revents |= POLLOUT;
+ if (FD_ISSET (f->fd, &xset))
+ f->revents |= POLLPRI;
+ }
+ if (f->revents)
+ ready++;
+ }
+ }
+
+ return ready;
+}
+
+#endif /* HAVE_SYS_POLL_H */
diff --git a/src/polypcore/poll.h b/src/polypcore/poll.h
new file mode 100644
index 00000000..c201014e
--- /dev/null
+++ b/src/polypcore/poll.h
@@ -0,0 +1,57 @@
+/* $Id$ */
+
+/***
+ Compatibility definitions for System V `poll' interface.
+ Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc.
+ Copyright (C) 2005, Cendio AB.
+ This file is part of polypaudio.
+ Based on work for the GNU C Library.
+
+ polypaudio is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ polypaudio 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with polypaudio; If not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA.
+***/
+
+/* Event types that can be polled for. These bits may be set in `events'
+ to indicate the interesting event types; they will appear in `revents'
+ to indicate the status of the file descriptor. */
+#define POLLIN 0x001 /* There is data to read. */
+#define POLLPRI 0x002 /* There is urgent data to read. */
+#define POLLOUT 0x004 /* Writing now will not block. */
+
+/* Event types always implicitly polled for. These bits need not be set in
+ `events', but they will appear in `revents' to indicate the status of
+ the file descriptor. */
+#define POLLERR 0x008 /* Error condition. */
+#define POLLHUP 0x010 /* Hung up. */
+#define POLLNVAL 0x020 /* Invalid polling request. */
+
+
+/* Type used for the number of file descriptors. */
+typedef unsigned long int nfds_t;
+
+/* Data structure describing a polling request. */
+struct pollfd
+ {
+ int fd; /* File descriptor to poll. */
+ short int events; /* Types of events poller cares about. */
+ short int revents; /* Types of events that actually occurred. */
+ };
+
+/* Poll the file descriptors described by the NFDS structures starting at
+ FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
+ an event to occur; if TIMEOUT is -1, block until an event occurs.
+ Returns the number of file descriptors with events, zero if timed out,
+ or -1 for errors. */
+extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);
diff --git a/src/polypcore/props.c b/src/polypcore/props.c
new file mode 100644
index 00000000..df748c1e
--- /dev/null
+++ b/src/polypcore/props.c
@@ -0,0 +1,119 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <assert.h>
+
+#include "xmalloc.h"
+#include "props.h"
+#include "log.h"
+
+typedef struct pa_property {
+ char *name; /* Points to memory allocated by the property subsystem */
+ void *data; /* Points to memory maintained by the caller */
+} pa_property;
+
+/* Allocate a new property object */
+static pa_property* property_new(const char *name, void *data) {
+ pa_property* p;
+ assert(name && data);
+
+ p = pa_xmalloc(sizeof(pa_property));
+ p->name = pa_xstrdup(name);
+ p->data = data;
+
+ return p;
+}
+
+/* Free a property object */
+static void property_free(pa_property *p) {
+ assert(p);
+
+ pa_xfree(p->name);
+ pa_xfree(p);
+}
+
+void* pa_property_get(pa_core *c, const char *name) {
+ pa_property *p;
+ assert(c && name && c->properties);
+
+ if (!(p = pa_hashmap_get(c->properties, name)))
+ return NULL;
+
+ return p->data;
+}
+
+int pa_property_set(pa_core *c, const char *name, void *data) {
+ pa_property *p;
+ assert(c && name && data && c->properties);
+
+ if (pa_hashmap_get(c->properties, name))
+ return -1;
+
+ p = property_new(name, data);
+ pa_hashmap_put(c->properties, p->name, p);
+ return 0;
+}
+
+int pa_property_remove(pa_core *c, const char *name) {
+ pa_property *p;
+ assert(c && name && c->properties);
+
+ if (!(p = pa_hashmap_remove(c->properties, name)))
+ return -1;
+
+ property_free(p);
+ return 0;
+}
+
+void pa_property_init(pa_core *c) {
+ assert(c);
+
+ c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+}
+
+void pa_property_cleanup(pa_core *c) {
+ assert(c);
+
+ if (!c->properties)
+ return;
+
+ assert(!pa_hashmap_size(c->properties));
+
+ pa_hashmap_free(c->properties, NULL, NULL);
+ c->properties = NULL;
+
+}
+
+void pa_property_dump(pa_core *c, pa_strbuf *s) {
+ void *state = NULL;
+ pa_property *p;
+ assert(c && s);
+
+ while ((p = pa_hashmap_iterate(c->properties, &state, NULL)))
+ pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data);
+}
+
+int pa_property_replace(pa_core *c, const char *name, void *data) {
+ assert(c && name);
+
+ pa_property_remove(c, name);
+ return pa_property_set(c, name, data);
+}
diff --git a/src/polypcore/props.h b/src/polypcore/props.h
new file mode 100644
index 00000000..5abf8787
--- /dev/null
+++ b/src/polypcore/props.h
@@ -0,0 +1,58 @@
+#ifndef foopropshfoo
+#define foopropshfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "core.h"
+#include "strbuf.h"
+
+/* The property subsystem is to be used to share data between
+ * modules. Consider them to be kind of "global" variables for a
+ * core. Why not use the hashmap functions directly? The hashmap
+ * functions copy neither the key nor value, while this property
+ * system copies the key. Users of this system have to think about
+ * reference counting themselves. */
+
+/* Return a pointer to the value of the specified property. */
+void* pa_property_get(pa_core *c, const char *name);
+
+/* Set the property 'name' to 'data'. This function fails in case a
+ * property by this name already exists. The property data is not
+ * copied or reference counted. This is the caller's job. */
+int pa_property_set(pa_core *c, const char *name, void *data);
+
+/* Remove the specified property. Return non-zero on failure */
+int pa_property_remove(pa_core *c, const char *name);
+
+/* A combination of pa_property_remove() and pa_property_set() */
+int pa_property_replace(pa_core *c, const char *name, void *data);
+
+/* Free all memory used by the property system */
+void pa_property_cleanup(pa_core *c);
+
+/* Initialize the properties subsystem */
+void pa_property_init(pa_core *c);
+
+/* Dump the current set of properties */
+void pa_property_dump(pa_core *c, pa_strbuf *s);
+
+#endif
diff --git a/src/polypcore/protocol-cli.c b/src/polypcore/protocol-cli.c
new file mode 100644
index 00000000..d2d73550
--- /dev/null
+++ b/src/polypcore/protocol-cli.c
@@ -0,0 +1,95 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdlib.h>
+
+#include "protocol-cli.h"
+#include "cli.h"
+#include "xmalloc.h"
+#include "log.h"
+
+/* Don't allow more than this many concurrent connections */
+#define MAX_CONNECTIONS 25
+
+struct pa_protocol_cli {
+ pa_module *module;
+ pa_core *core;
+ pa_socket_server*server;
+ pa_idxset *connections;
+};
+
+static void cli_eof_cb(pa_cli*c, void*userdata) {
+ pa_protocol_cli *p = userdata;
+ assert(p);
+ pa_idxset_remove_by_data(p->connections, c, NULL);
+ pa_cli_free(c);
+}
+
+static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+ pa_protocol_cli *p = userdata;
+ pa_cli *c;
+ assert(s && io && p);
+
+ if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+ pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
+ pa_iochannel_free(io);
+ return;
+ }
+
+ c = pa_cli_new(p->core, io, p->module);
+ assert(c);
+ pa_cli_set_eof_callback(c, cli_eof_cb, p);
+
+ pa_idxset_put(p->connections, c, NULL);
+}
+
+pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
+ pa_protocol_cli* p;
+ assert(core && server);
+
+ p = pa_xmalloc(sizeof(pa_protocol_cli));
+ p->module = m;
+ p->core = core;
+ p->server = server;
+ p->connections = pa_idxset_new(NULL, NULL);
+
+ pa_socket_server_set_callback(p->server, on_connection, p);
+
+ return p;
+}
+
+static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
+ assert(p);
+ pa_cli_free(p);
+}
+
+void pa_protocol_cli_free(pa_protocol_cli *p) {
+ assert(p);
+
+ pa_idxset_free(p->connections, free_connection, NULL);
+ pa_socket_server_unref(p->server);
+ pa_xfree(p);
+}
diff --git a/src/polypcore/protocol-cli.h b/src/polypcore/protocol-cli.h
new file mode 100644
index 00000000..aed733c1
--- /dev/null
+++ b/src/polypcore/protocol-cli.h
@@ -0,0 +1,35 @@
+#ifndef fooprotocolclihfoo
+#define fooprotocolclihfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "core.h"
+#include "socket-server.h"
+#include "module.h"
+#include "modargs.h"
+
+typedef struct pa_protocol_cli pa_protocol_cli;
+
+pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
+void pa_protocol_cli_free(pa_protocol_cli *n);
+
+#endif
diff --git a/src/polypcore/protocol-esound.c b/src/polypcore/protocol-esound.c
new file mode 100644
index 00000000..14f237c7
--- /dev/null
+++ b/src/polypcore/protocol-esound.c
@@ -0,0 +1,1166 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "protocol-esound.h"
+#include "esound.h"
+#include "memblock.h"
+#include "client.h"
+#include "sink-input.h"
+#include "sink.h"
+#include "source-output.h"
+#include "source.h"
+#include <polyp/sample.h>
+#include "scache.h"
+#include "sample-util.h"
+#include "authkey.h"
+#include "namereg.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "util.h"
+#include "endianmacros.h"
+
+/* Don't accept more connection than this */
+#define MAX_CONNECTIONS 10
+
+/* Kick a client if it doesn't authenticate within this time */
+#define AUTH_TIMEOUT 5
+
+#define DEFAULT_COOKIE_FILE ".esd_auth"
+
+#define PLAYBACK_BUFFER_SECONDS (.5)
+#define PLAYBACK_BUFFER_FRAGMENTS (10)
+#define RECORD_BUFFER_SECONDS (5)
+#define RECORD_BUFFER_FRAGMENTS (100)
+
+#define MAX_CACHE_SAMPLE_SIZE (1024000)
+
+#define SCACHE_PREFIX "esound."
+
+/* This is heavily based on esound's code */
+
+struct connection {
+ uint32_t index;
+ int dead;
+ pa_protocol_esound *protocol;
+ pa_iochannel *io;
+ pa_client *client;
+ int authorized, swap_byte_order;
+ void *write_data;
+ size_t write_data_alloc, write_data_index, write_data_length;
+ void *read_data;
+ size_t read_data_alloc, read_data_length;
+ esd_proto_t request;
+ esd_client_state_t state;
+ pa_sink_input *sink_input;
+ pa_source_output *source_output;
+ pa_memblockq *input_memblockq, *output_memblockq;
+ pa_defer_event *defer_event;
+
+ struct {
+ pa_memblock *current_memblock;
+ size_t memblock_index, fragment_size;
+ } playback;
+
+ struct {
+ pa_memchunk memchunk;
+ char *name;
+ pa_sample_spec sample_spec;
+ } scache;
+
+ pa_time_event *auth_timeout_event;
+};
+
+struct pa_protocol_esound {
+ int public;
+ pa_module *module;
+ pa_core *core;
+ pa_socket_server *server;
+ pa_idxset *connections;
+ char *sink_name, *source_name;
+ unsigned n_player;
+ uint8_t esd_key[ESD_KEY_LEN];
+};
+
+typedef struct proto_handler {
+ size_t data_length;
+ int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length);
+ const char *description;
+} esd_proto_handler_info_t;
+
+static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length);
+static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk);
+static void sink_input_kill_cb(pa_sink_input *i);
+static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i);
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
+static void source_output_kill_cb(pa_source_output *o);
+
+static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_standby_or_resume(struct connection *c, esd_proto_t request, const void *data, size_t length);
+
+/* the big map of protocol handler info */
+static struct proto_handler proto_map[ESD_PROTO_MAX] = {
+ { ESD_KEY_LEN + sizeof(int), esd_proto_connect, "connect" },
+ { ESD_KEY_LEN + sizeof(int), NULL, "lock" },
+ { ESD_KEY_LEN + sizeof(int), NULL, "unlock" },
+
+ { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" },
+ { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" },
+ { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" },
+
+ { ESD_NAME_MAX + 3 * sizeof(int), esd_proto_sample_cache, "sample cache" }, /* 6 */
+ { sizeof(int), esd_proto_sample_free_or_play, "sample free" },
+ { sizeof(int), esd_proto_sample_free_or_play, "sample play" }, /* 8 */
+ { sizeof(int), NULL, "sample loop" },
+ { sizeof(int), NULL, "sample stop" },
+ { -1, NULL, "TODO: sample kill" },
+
+ { ESD_KEY_LEN + sizeof(int), esd_proto_standby_or_resume, "standby" }, /* NOOP! */
+ { ESD_KEY_LEN + sizeof(int), esd_proto_standby_or_resume, "resume" }, /* NOOP! */ /* 13 */
+
+ { ESD_NAME_MAX, esd_proto_sample_get_id, "sample getid" }, /* 14 */
+ { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
+
+ { sizeof(int), esd_proto_server_info, "server info" },
+ { sizeof(int), esd_proto_all_info, "all info" },
+ { -1, NULL, "TODO: subscribe" },
+ { -1, NULL, "TODO: unsubscribe" },
+
+ { 3 * sizeof(int), esd_proto_stream_pan, "stream pan"},
+ { 3 * sizeof(int), NULL, "sample pan" },
+
+ { sizeof(int), NULL, "standby mode" },
+ { 0, esd_proto_get_latency, "get latency" }
+};
+
+
+static void connection_free(struct connection *c) {
+ assert(c);
+ pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+
+ if (c->state == ESD_STREAMING_DATA)
+ c->protocol->n_player--;
+
+ pa_client_free(c->client);
+
+ if (c->sink_input) {
+ pa_sink_input_disconnect(c->sink_input);
+ pa_sink_input_unref(c->sink_input);
+ }
+
+ if (c->source_output) {
+ pa_source_output_disconnect(c->source_output);
+ pa_source_output_unref(c->source_output);
+ }
+
+ if (c->input_memblockq)
+ pa_memblockq_free(c->input_memblockq);
+ if (c->output_memblockq)
+ pa_memblockq_free(c->output_memblockq);
+
+ if (c->playback.current_memblock)
+ pa_memblock_unref(c->playback.current_memblock);
+
+ pa_xfree(c->read_data);
+ pa_xfree(c->write_data);
+
+ if (c->io)
+ pa_iochannel_free(c->io);
+
+ if (c->defer_event)
+ c->protocol->core->mainloop->defer_free(c->defer_event);
+
+ if (c->scache.memchunk.memblock)
+ pa_memblock_unref(c->scache.memchunk.memblock);
+ pa_xfree(c->scache.name);
+
+ if (c->auth_timeout_event)
+ c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+
+ pa_xfree(c);
+}
+
+static void* connection_write(struct connection *c, size_t length) {
+ size_t t, i;
+ assert(c);
+
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+ c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+
+ t = c->write_data_length+length;
+
+ if (c->write_data_alloc < t)
+ c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t);
+
+ assert(c->write_data);
+
+ i = c->write_data_length;
+ c->write_data_length += length;
+
+ return (uint8_t*) c->write_data+i;
+}
+
+static void format_esd2native(int format, int swap_bytes, pa_sample_spec *ss) {
+ assert(ss);
+
+ ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
+ if ((format & ESD_MASK_BITS) == ESD_BITS16)
+ ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE;
+ else
+ ss->format = PA_SAMPLE_U8;
+}
+
+static int format_native2esd(pa_sample_spec *ss) {
+ int format = 0;
+
+ format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
+ format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO;
+
+ return format;
+}
+
+/*** esound commands ***/
+
+static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+ uint32_t ekey;
+ int *ok;
+ assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
+
+ if (!c->authorized) {
+ if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) {
+ pa_log(__FILE__": kicked client with invalid authorization key.\n");
+ return -1;
+ }
+
+ c->authorized = 1;
+ if (c->auth_timeout_event) {
+ c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+ c->auth_timeout_event = NULL;
+ }
+ }
+
+ ekey = *(const uint32_t*)((const uint8_t*) data+ESD_KEY_LEN);
+ if (ekey == ESD_ENDIAN_KEY)
+ c->swap_byte_order = 0;
+ else if (ekey == ESD_SWAP_ENDIAN_KEY)
+ c->swap_byte_order = 1;
+ else {
+ pa_log(__FILE__": client sent invalid endian key\n");
+ return -1;
+ }
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+ *ok = 1;
+ return 0;
+}
+
+static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+ char name[ESD_NAME_MAX];
+ int format, rate;
+ pa_sink *sink;
+ pa_sample_spec ss;
+ size_t l;
+ assert(c && length == (sizeof(int)*2+ESD_NAME_MAX));
+
+ format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data);
+ rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1));
+
+ ss.rate = rate;
+ format_esd2native(format, c->swap_byte_order, &ss);
+
+ if (!pa_sample_spec_valid(&ss)) {
+ pa_log(__FILE__": invalid sample specification\n");
+ return -1;
+ }
+
+ if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
+ pa_log(__FILE__": no such sink\n");
+ return -1;
+ }
+
+ strncpy(name, (const char*) data + sizeof(int)*2, sizeof(name));
+ name[sizeof(name)-1] = 0;
+
+ pa_client_set_name(c->client, name);
+
+ assert(!c->sink_input && !c->input_memblockq);
+
+ if (!(c->sink_input = pa_sink_input_new(sink, __FILE__, name, &ss, NULL, 0, -1))) {
+ pa_log(__FILE__": failed to create sink input.\n");
+ return -1;
+ }
+
+ l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
+ c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS, c->protocol->core->memblock_stat);
+ pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
+ c->playback.fragment_size = l/10;
+
+ c->sink_input->owner = c->protocol->module;
+ c->sink_input->client = c->client;
+ c->sink_input->peek = sink_input_peek_cb;
+ c->sink_input->drop = sink_input_drop_cb;
+ c->sink_input->kill = sink_input_kill_cb;
+ c->sink_input->get_latency = sink_input_get_latency_cb;
+ c->sink_input->userdata = c;
+
+ c->state = ESD_STREAMING_DATA;
+
+ c->protocol->n_player++;
+
+ return 0;
+}
+
+static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+ char name[ESD_NAME_MAX];
+ int format, rate;
+ pa_source *source;
+ pa_sample_spec ss;
+ size_t l;
+ assert(c && length == (sizeof(int)*2+ESD_NAME_MAX));
+
+ format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data);
+ rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1));
+
+ ss.rate = rate;
+ format_esd2native(format, c->swap_byte_order, &ss);
+
+ if (!pa_sample_spec_valid(&ss)) {
+ pa_log(__FILE__": invalid sample specification.\n");
+ return -1;
+ }
+
+ if (request == ESD_PROTO_STREAM_MON) {
+ pa_sink* sink;
+
+ if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
+ pa_log(__FILE__": no such sink.\n");
+ return -1;
+ }
+
+ if (!(source = sink->monitor_source)) {
+ pa_log(__FILE__": no such monitor source.\n");
+ return -1;
+ }
+ } else {
+ assert(request == ESD_PROTO_STREAM_REC);
+
+ if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) {
+ pa_log(__FILE__": no such source.\n");
+ return -1;
+ }
+ }
+
+ strncpy(name, (const char*) data + sizeof(int)*2, sizeof(name));
+ name[sizeof(name)-1] = 0;
+
+ pa_client_set_name(c->client, name);
+
+ assert(!c->output_memblockq && !c->source_output);
+
+ if (!(c->source_output = pa_source_output_new(source, __FILE__, name, &ss, NULL, -1))) {
+ pa_log(__FILE__": failed to create source output\n");
+ return -1;
+ }
+
+ l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
+ c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), 0, 0, c->protocol->core->memblock_stat);
+ pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
+
+ c->source_output->owner = c->protocol->module;
+ c->source_output->client = c->client;
+ c->source_output->push = source_output_push_cb;
+ c->source_output->kill = source_output_kill_cb;
+ c->source_output->get_latency = source_output_get_latency_cb;
+ c->source_output->userdata = c;
+
+ c->state = ESD_STREAMING_DATA;
+
+ c->protocol->n_player++;
+
+ return 0;
+}
+
+static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+ pa_sink *sink;
+ int latency, *lag;
+ assert(c && !data && length == 0);
+
+ if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
+ latency = 0;
+ else {
+ double usec = pa_sink_get_latency(sink);
+ latency = (int) ((usec*44100)/1000000);
+ }
+
+ lag = connection_write(c, sizeof(int));
+ assert(lag);
+ *lag = MAYBE_INT32_SWAP(c->swap_byte_order, latency);
+ return 0;
+}
+
+static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+ int rate = 44100, format = ESD_STEREO|ESD_BITS16;
+ int *response;
+ pa_sink *sink;
+ assert(c && data && length == sizeof(int));
+
+ if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
+ rate = sink->sample_spec.rate;
+ format = format_native2esd(&sink->sample_spec);
+ }
+
+ response = connection_write(c, sizeof(int)*3);
+ assert(response);
+ *(response++) = 0;
+ *(response++) = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ *(response++) = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ return 0;
+}
+
+static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+ uint8_t *response;
+ size_t t, k, s;
+ struct connection *conn;
+ size_t idx = PA_IDXSET_INVALID;
+ unsigned nsamples;
+ assert(c && data && length == sizeof(int));
+
+ if (esd_proto_server_info(c, request, data, length) < 0)
+ return -1;
+
+ k = sizeof(int)*5+ESD_NAME_MAX;
+ s = sizeof(int)*6+ESD_NAME_MAX;
+ nsamples = c->protocol->core->scache ? pa_idxset_size(c->protocol->core->scache) : 0;
+ response = connection_write(c, (t = s*(nsamples+1) + k*(c->protocol->n_player+1)));
+ assert(k);
+
+ for (conn = pa_idxset_first(c->protocol->connections, &idx); conn; conn = pa_idxset_next(c->protocol->connections, &idx)) {
+ int format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE;
+
+ if (conn->state != ESD_STREAMING_DATA)
+ continue;
+
+ assert(t >= s+k+k);
+
+ if (conn->sink_input) {
+ rate = conn->sink_input->sample_spec.rate;
+ lvolume = (conn->sink_input->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
+ rvolume = (conn->sink_input->volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
+ format = format_native2esd(&conn->sink_input->sample_spec);
+ }
+
+ /* id */
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (conn->index+1));
+ response += sizeof(int);
+
+ /* name */
+ assert(conn->client);
+ strncpy((char*) response, conn->client->name, ESD_NAME_MAX);
+ response += ESD_NAME_MAX;
+
+ /* rate */
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ response += sizeof(int);
+
+ /* left */
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);
+ response += sizeof(int);
+
+ /*right*/
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);
+ response += sizeof(int);
+
+ /*format*/
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ response += sizeof(int);
+
+ t-= k;
+ }
+
+ assert(t == s*(nsamples+1)+k);
+ memset(response, 0, k);
+ response += k;
+ t -= k;
+
+ if (nsamples) {
+ pa_scache_entry *ce;
+
+ idx = PA_IDXSET_INVALID;
+ for (ce = pa_idxset_first(c->protocol->core->scache, &idx); ce; ce = pa_idxset_next(c->protocol->core->scache, &idx)) {
+ assert(t >= s*2);
+
+ /* id */
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
+ response += sizeof(int);
+
+ /* name */
+ if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0)
+ strncpy((char*) response, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);
+ else
+ snprintf((char*) response, ESD_NAME_MAX, "native.%s", ce->name);
+ response += ESD_NAME_MAX;
+
+ /* rate */
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);
+ response += sizeof(int);
+
+ /* left */
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ response += sizeof(int);
+
+ /*right*/
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ response += sizeof(int);
+
+ /*format*/
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec));
+ response += sizeof(int);
+
+ /*length*/
+ *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);
+ response += sizeof(int);
+
+ t -= s;
+ }
+ }
+
+ assert(t == s);
+ memset(response, 0, s);
+
+ return 0;
+}
+
+static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+ int *ok;
+ uint32_t idx;
+ pa_volume_t lvolume, rvolume;
+ struct connection *conn;
+ assert(c && data && length == sizeof(int)*3);
+
+ idx = MAYBE_UINT32_SWAP(c->swap_byte_order, *(const int*)data)-1;
+ lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, *((const int*)data + 1));
+ lvolume = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+ rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, *((const int*)data + 2));
+ rvolume = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+
+ if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx))) {
+ assert(conn->sink_input);
+ conn->sink_input->volume.values[0] = lvolume;
+ conn->sink_input->volume.values[1] = rvolume;
+ conn->sink_input->volume.channels = 2;
+ *ok = 1;
+ } else
+ *ok = 0;
+
+ return 0;
+}
+
+static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+ pa_sample_spec ss;
+ int format, rate;
+ size_t sc_length;
+ uint32_t idx;
+ int *ok;
+ char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
+ assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int)));
+
+ format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data);
+ rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1));
+
+ ss.rate = rate;
+ format_esd2native(format, c->swap_byte_order, &ss);
+
+ sc_length = (size_t) MAYBE_INT32_SWAP(c->swap_byte_order, (*((const int*)data + 2)));
+
+ if (sc_length >= MAX_CACHE_SAMPLE_SIZE)
+ return -1;
+
+ strcpy(name, SCACHE_PREFIX);
+ strncpy(name+sizeof(SCACHE_PREFIX)-1, (const char*) data+3*sizeof(int), ESD_NAME_MAX);
+ name[sizeof(name)-1] = 0;
+
+ assert(!c->scache.memchunk.memblock);
+ c->scache.memchunk.memblock = pa_memblock_new(sc_length, c->protocol->core->memblock_stat);
+ c->scache.memchunk.index = 0;
+ c->scache.memchunk.length = sc_length;
+ c->scache.sample_spec = ss;
+ assert(!c->scache.name);
+ c->scache.name = pa_xstrdup(name);
+
+ c->state = ESD_CACHING_SAMPLE;
+
+ pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, &idx);
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+
+ *ok = idx+1;
+
+ return 0;
+}
+
+static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+ int *ok;
+ uint32_t idx;
+ char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
+ assert(c && data && length == ESD_NAME_MAX);
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+
+ *ok = -1;
+
+ strcpy(name, SCACHE_PREFIX);
+ strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
+ name[sizeof(name)-1] = 0;
+
+ if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID)
+ *ok = (int) idx +1;
+
+ return 0;
+}
+
+static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+ int *ok;
+ const char *name;
+ uint32_t idx;
+ assert(c && data && length == sizeof(int));
+
+ idx = (uint32_t) MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data)-1;
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+
+ *ok = 0;
+
+ if ((name = pa_scache_get_name_by_id(c->protocol->core, idx))) {
+ if (request == ESD_PROTO_SAMPLE_PLAY) {
+ pa_sink *sink;
+
+ if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
+ if (pa_scache_play_item(c->protocol->core, name, sink, NULL) >= 0)
+ *ok = (int) idx+1;
+ } else {
+ assert(request == ESD_PROTO_SAMPLE_FREE);
+
+ if (pa_scache_remove_item(c->protocol->core, name) >= 0)
+ *ok = (int) idx+1;
+ }
+ }
+
+ return 0;
+}
+
+static int esd_proto_standby_or_resume(struct connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) {
+ int *ok;
+ ok = connection_write(c, sizeof(int)*2);
+ assert(ok);
+ ok[0] = 1;
+ ok[1] = 1;
+ return 0;
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(pa_client *c) {
+ assert(c && c->userdata);
+ connection_free(c->userdata);
+}
+
+/*** pa_iochannel callbacks ***/
+
+static int do_read(struct connection *c) {
+ assert(c && c->io);
+
+/* pa_log("READ\n"); */
+
+ if (c->state == ESD_NEXT_REQUEST) {
+ ssize_t r;
+ assert(c->read_data_length < sizeof(c->request));
+
+ if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
+ if (r != 0)
+ pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if ((c->read_data_length+= r) >= sizeof(c->request)) {
+ struct proto_handler *handler;
+
+ c->request = MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
+
+ if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
+ pa_log(__FILE__": recieved invalid request.\n");
+ return -1;
+ }
+
+ handler = proto_map+c->request;
+
+/* pa_log(__FILE__": executing request #%u\n", c->request); */
+
+ if (!handler->proc) {
+ pa_log(__FILE__": recieved unimplemented request #%u.\n", c->request);
+ return -1;
+ }
+
+ if (handler->data_length == 0) {
+ c->read_data_length = 0;
+
+ if (handler->proc(c, c->request, NULL, 0) < 0)
+ return -1;
+
+ } else {
+ if (c->read_data_alloc < handler->data_length)
+ c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length);
+ assert(c->read_data);
+
+ c->state = ESD_NEEDS_REQDATA;
+ c->read_data_length = 0;
+ }
+ }
+
+ } else if (c->state == ESD_NEEDS_REQDATA) {
+ ssize_t r;
+ struct proto_handler *handler = proto_map+c->request;
+
+ assert(handler->proc);
+
+ assert(c->read_data && c->read_data_length < handler->data_length);
+
+ if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
+ if (r != 0)
+ pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if ((c->read_data_length+= r) >= handler->data_length) {
+ size_t l = c->read_data_length;
+ assert(handler->proc);
+
+ c->state = ESD_NEXT_REQUEST;
+ c->read_data_length = 0;
+
+ if (handler->proc(c, c->request, c->read_data, l) < 0)
+ return -1;
+ }
+ } else if (c->state == ESD_CACHING_SAMPLE) {
+ ssize_t r;
+
+ assert(c->scache.memchunk.memblock && c->scache.name && c->scache.memchunk.index < c->scache.memchunk.length);
+
+ if ((r = pa_iochannel_read(c->io, (uint8_t*) c->scache.memchunk.memblock->data+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) {
+ if (r!= 0)
+ pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ c->scache.memchunk.index += r;
+ assert(c->scache.memchunk.index <= c->scache.memchunk.length);
+
+ if (c->scache.memchunk.index == c->scache.memchunk.length) {
+ uint32_t idx;
+ int *ok;
+
+ c->scache.memchunk.index = 0;
+ pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, &idx);
+
+ pa_memblock_unref(c->scache.memchunk.memblock);
+ c->scache.memchunk.memblock = NULL;
+ c->scache.memchunk.index = c->scache.memchunk.length = 0;
+
+ pa_xfree(c->scache.name);
+ c->scache.name = NULL;
+
+ c->state = ESD_NEXT_REQUEST;
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+ *ok = idx+1;
+ }
+
+ } else if (c->state == ESD_STREAMING_DATA && c->sink_input) {
+ pa_memchunk chunk;
+ ssize_t r;
+ size_t l;
+
+ assert(c->input_memblockq);
+
+/* pa_log("STREAMING_DATA\n"); */
+
+ if (!(l = pa_memblockq_missing(c->input_memblockq)))
+ return 0;
+
+ if (l > c->playback.fragment_size)
+ l = c->playback.fragment_size;
+
+ if (c->playback.current_memblock)
+ if (c->playback.current_memblock->length - c->playback.memblock_index < l) {
+ pa_memblock_unref(c->playback.current_memblock);
+ c->playback.current_memblock = NULL;
+ c->playback.memblock_index = 0;
+ }
+
+ if (!c->playback.current_memblock) {
+ c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2, c->protocol->core->memblock_stat);
+ assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
+ c->playback.memblock_index = 0;
+ }
+
+ if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
+ if (r != 0)
+ pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+/* pa_log(__FILE__": read %u\n", r); */
+
+ chunk.memblock = c->playback.current_memblock;
+ chunk.index = c->playback.memblock_index;
+ chunk.length = r;
+ assert(chunk.memblock);
+
+ c->playback.memblock_index += r;
+
+ assert(c->input_memblockq);
+ pa_memblockq_push_align(c->input_memblockq, &chunk, 0);
+ assert(c->sink_input);
+ pa_sink_notify(c->sink_input->sink);
+ }
+
+ return 0;
+}
+
+static int do_write(struct connection *c) {
+ assert(c && c->io);
+
+/* pa_log("WRITE\n"); */
+
+ if (c->write_data_length) {
+ ssize_t r;
+
+ assert(c->write_data_index < c->write_data_length);
+ if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
+ pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if ((c->write_data_index +=r) >= c->write_data_length)
+ c->write_data_length = c->write_data_index = 0;
+
+ } else if (c->state == ESD_STREAMING_DATA && c->source_output) {
+ pa_memchunk chunk;
+ ssize_t r;
+
+ assert(c->output_memblockq);
+ if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
+ return 0;
+
+ assert(chunk.memblock && chunk.length);
+
+ if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) {
+ pa_memblock_unref(chunk.memblock);
+ pa_log(__FILE__": write(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ pa_memblockq_drop(c->output_memblockq, &chunk, r);
+ pa_memblock_unref(chunk.memblock);
+ }
+
+ return 0;
+}
+
+static void do_work(struct connection *c) {
+ assert(c);
+
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+ c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
+
+/* pa_log("DOWORK %i\n", pa_iochannel_is_hungup(c->io)); */
+
+ if (!c->dead && pa_iochannel_is_readable(c->io))
+ if (do_read(c) < 0)
+ goto fail;
+
+ if (!c->dead && pa_iochannel_is_writable(c->io))
+ if (do_write(c) < 0)
+ goto fail;
+
+ /* In case the line was hungup, make sure to rerun this function
+ as soon as possible, until all data has been read. */
+
+ if (!c->dead && pa_iochannel_is_hungup(c->io))
+ c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+
+ return;
+
+fail:
+
+ if (c->state == ESD_STREAMING_DATA && c->sink_input) {
+ c->dead = 1;
+ pa_memblockq_prebuf_disable(c->input_memblockq);
+
+ pa_iochannel_free(c->io);
+ c->io = NULL;
+
+ } else
+ connection_free(c);
+}
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+ struct connection *c = userdata;
+ assert(io && c && c->io == io);
+
+/* pa_log("IO\n"); */
+
+ do_work(c);
+}
+
+/*** defer callback ***/
+
+static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
+ struct connection *c = userdata;
+ assert(a && c && c->defer_event == e);
+
+/* pa_log("DEFER\n"); */
+
+ do_work(c);
+}
+
+/*** sink_input callbacks ***/
+
+static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
+ struct connection*c;
+ assert(i && i->userdata && chunk);
+ c = i->userdata;
+
+ if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
+
+ if (c->dead)
+ connection_free(c);
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
+ struct connection*c = i->userdata;
+ assert(i && c && length);
+
+/* pa_log("DROP\n"); */
+
+ pa_memblockq_drop(c->input_memblockq, chunk, length);
+
+ /* do something */
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+
+ if (!c->dead)
+ c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+
+/* assert(pa_memblockq_get_length(c->input_memblockq) > 2048); */
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+ assert(i && i->userdata);
+ connection_free((struct connection *) i->userdata);
+}
+
+static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
+ struct connection*c = i->userdata;
+ assert(i && c);
+ return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+}
+
+/*** source_output callbacks ***/
+
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+ struct connection *c = o->userdata;
+ assert(o && c && chunk);
+
+ pa_memblockq_push(c->output_memblockq, chunk, 0);
+
+ /* do something */
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+
+ if (!c->dead)
+ c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+}
+
+static void source_output_kill_cb(pa_source_output *o) {
+ assert(o && o->userdata);
+ connection_free((struct connection *) o->userdata);
+}
+
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+ struct connection*c = o->userdata;
+ assert(o && c);
+ return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
+}
+
+/*** socket server callback ***/
+
+static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ struct connection *c = userdata;
+ assert(m && tv && c && c->auth_timeout_event == e);
+
+ if (!c->authorized)
+ connection_free(c);
+}
+
+static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+ struct connection *c;
+ pa_protocol_esound *p = userdata;
+ char cname[256];
+ assert(s && io && p);
+
+ if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+ pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
+ pa_iochannel_free(io);
+ return;
+ }
+
+ c = pa_xmalloc(sizeof(struct connection));
+ c->protocol = p;
+ c->io = io;
+ pa_iochannel_set_callback(c->io, io_callback, c);
+
+ pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
+ assert(p->core);
+ c->client = pa_client_new(p->core, __FILE__, cname);
+ assert(c->client);
+ c->client->owner = p->module;
+ c->client->kill = client_kill_cb;
+ c->client->userdata = c;
+
+ c->authorized = p->public;
+ c->swap_byte_order = 0;
+ c->dead = 0;
+
+ c->read_data_length = 0;
+ c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
+
+ c->write_data_length = c->write_data_index = c->write_data_alloc = 0;
+ c->write_data = NULL;
+
+ c->state = ESD_NEEDS_REQDATA;
+ c->request = ESD_PROTO_CONNECT;
+
+ c->sink_input = NULL;
+ c->input_memblockq = NULL;
+
+ c->source_output = NULL;
+ c->output_memblockq = NULL;
+
+ c->playback.current_memblock = NULL;
+ c->playback.memblock_index = 0;
+ c->playback.fragment_size = 0;
+
+ c->scache.memchunk.length = c->scache.memchunk.index = 0;
+ c->scache.memchunk.memblock = NULL;
+ c->scache.name = NULL;
+
+ if (!c->authorized) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ tv.tv_sec += AUTH_TIMEOUT;
+ c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
+ } else
+ c->auth_timeout_event = NULL;
+
+ c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
+ assert(c->defer_event);
+ p->core->mainloop->defer_enable(c->defer_event, 0);
+
+ pa_idxset_put(p->connections, c, &c->index);
+}
+
+/*** entry points ***/
+
+pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
+ pa_protocol_esound *p;
+ int public = 0;
+ assert(core && server && ma);
+
+ p = pa_xmalloc(sizeof(pa_protocol_esound));
+
+ if (pa_modargs_get_value_boolean(ma, "public", &public) < 0) {
+ pa_log(__FILE__": public= expects a boolean argument.\n");
+ return NULL;
+ }
+
+ if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0) {
+ pa_xfree(p);
+ return NULL;
+ }
+
+ p->module = m;
+ p->public = public;
+ p->server = server;
+ pa_socket_server_set_callback(p->server, on_connection, p);
+ p->core = core;
+ p->connections = pa_idxset_new(NULL, NULL);
+ assert(p->connections);
+
+ p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+ p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
+ p->n_player = 0;
+
+ return p;
+}
+
+void pa_protocol_esound_free(pa_protocol_esound *p) {
+ struct connection *c;
+ assert(p);
+
+ while ((c = pa_idxset_first(p->connections, NULL)))
+ connection_free(c);
+
+ pa_idxset_free(p->connections, NULL, NULL);
+ pa_socket_server_unref(p->server);
+ pa_xfree(p);
+}
diff --git a/src/polypcore/protocol-esound.h b/src/polypcore/protocol-esound.h
new file mode 100644
index 00000000..71d58464
--- /dev/null
+++ b/src/polypcore/protocol-esound.h
@@ -0,0 +1,35 @@
+#ifndef fooprotocolesoundhfoo
+#define fooprotocolesoundhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "core.h"
+#include "socket-server.h"
+#include "module.h"
+#include "modargs.h"
+
+typedef struct pa_protocol_esound pa_protocol_esound;
+
+pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
+void pa_protocol_esound_free(pa_protocol_esound *p);
+
+#endif
diff --git a/src/polypcore/protocol-http.c b/src/polypcore/protocol-http.c
new file mode 100644
index 00000000..3e55df03
--- /dev/null
+++ b/src/polypcore/protocol-http.c
@@ -0,0 +1,265 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "protocol-http.h"
+#include "ioline.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "namereg.h"
+#include "cli-text.h"
+
+/* Don't allow more than this many concurrent connections */
+#define MAX_CONNECTIONS 10
+
+#define internal_server_error(c) http_message((c), 500, "Internal Server Error", NULL)
+
+#define URL_ROOT "/"
+#define URL_CSS "/style"
+#define URL_STATUS "/status"
+
+struct connection {
+ pa_protocol_http *protocol;
+ pa_ioline *line;
+ enum { REQUEST_LINE, MIME_HEADER, DATA } state;
+ char *url;
+};
+
+struct pa_protocol_http {
+ pa_module *module;
+ pa_core *core;
+ pa_socket_server*server;
+ pa_idxset *connections;
+};
+
+static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
+ char s[256];
+ assert(c);
+ assert(msg);
+ assert(mime);
+
+ snprintf(s, sizeof(s),
+ "HTTP/1.0 %i %s\n"
+ "Connection: close\n"
+ "Content-Type: %s\n"
+ "Cache-Control: no-cache\n"
+ "Expires: 0\n"
+ "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
+ "\n", code, msg, mime);
+
+ pa_ioline_puts(c->line, s);
+}
+
+static void http_message(struct connection *c, int code, const char *msg, const char *text) {
+ char s[256];
+ assert(c);
+
+ http_response(c, code, msg, "text/html");
+
+ if (!text)
+ text = msg;
+
+ snprintf(s, sizeof(s),
+ "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>%s</title></head>\n"
+ "<body>%s</body></html>\n",
+ text, text);
+
+ pa_ioline_puts(c->line, s);
+ pa_ioline_defer_close(c->line);
+}
+
+
+static void connection_free(struct connection *c, int del) {
+ assert(c);
+
+ if (c->url)
+ pa_xfree(c->url);
+
+ if (del)
+ pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+ pa_ioline_unref(c->line);
+ pa_xfree(c);
+}
+
+static void line_callback(pa_ioline *line, const char *s, void *userdata) {
+ struct connection *c = userdata;
+ assert(line);
+ assert(c);
+
+ if (!s) {
+ /* EOF */
+ connection_free(c, 1);
+ return;
+ }
+
+ switch (c->state) {
+ case REQUEST_LINE: {
+ if (memcmp(s, "GET ", 4))
+ goto fail;
+
+ s +=4;
+
+ c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?"));
+ c->state = MIME_HEADER;
+ break;
+
+ }
+
+ case MIME_HEADER: {
+
+ /* Ignore MIME headers */
+ if (strcspn(s, " \r\n") != 0)
+ break;
+
+ /* We're done */
+ c->state = DATA;
+
+ pa_log_info(__FILE__": request for %s\n", c->url);
+
+ if (!strcmp(c->url, URL_ROOT)) {
+ char txt[256];
+ http_response(c, 200, "OK", "text/html");
+
+ pa_ioline_puts(c->line,
+ "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\"><title>"PACKAGE_NAME" "PACKAGE_VERSION"</title>\n"
+ "<link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/></head><body>\n");
+
+ pa_ioline_puts(c->line,
+ "<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
+ "<table>");
+
+#define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "<tr><td><b>%s</b></td><td>%s</td></tr>\n",(a),(b))
+
+ PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
+ PRINTF_FIELD("Fully Qualified Domain Name:", pa_get_fqdn(txt, sizeof(txt)));
+ PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
+ PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core));
+ PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core));
+
+ pa_ioline_puts(c->line, "</table>");
+
+ pa_ioline_puts(c->line, "<p><a href=\"/status\">Click here</a> for an extensive server status report.</p>");
+
+ pa_ioline_puts(c->line, "</body></html>\n");
+
+ pa_ioline_defer_close(c->line);
+ } else if (!strcmp(c->url, URL_CSS)) {
+ http_response(c, 200, "OK", "text/css");
+
+ pa_ioline_puts(c->line,
+ "body { color: black; background-color: white; margin: 0.5cm; }\n"
+ "a:link, a:visited { color: #900000; }\n"
+ "p { margin-left: 0.5cm; margin-right: 0.5cm; }\n"
+ "h1 { color: #00009F; }\n"
+ "h2 { color: #00009F; }\n"
+ "ul { margin-left: .5cm; }\n"
+ "ol { margin-left: .5cm; }\n"
+ "pre { margin-left: .5cm; background-color: #f0f0f0; padding: 0.4cm;}\n"
+ ".grey { color: #afafaf; }\n"
+ "table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
+ "td { padding-left:10px; padding-right:10px; }\n");
+
+ pa_ioline_defer_close(c->line);
+ } else if (!strcmp(c->url, URL_STATUS)) {
+ char *r;
+
+ http_response(c, 200, "OK", "text/plain");
+ r = pa_full_status_string(c->protocol->core);
+ pa_ioline_puts(c->line, r);
+ pa_xfree(r);
+
+ pa_ioline_defer_close(c->line);
+ } else
+ http_message(c, 404, "Not Found", NULL);
+
+ break;
+ }
+
+ default:
+ ;
+ }
+
+ return;
+
+fail:
+ internal_server_error(c);
+}
+
+static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+ pa_protocol_http *p = userdata;
+ struct connection *c;
+ assert(s && io && p);
+
+ if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+ pa_log_warn(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
+ pa_iochannel_free(io);
+ return;
+ }
+
+ c = pa_xmalloc(sizeof(struct connection));
+ c->protocol = p;
+ c->line = pa_ioline_new(io);
+ c->state = REQUEST_LINE;
+ c->url = NULL;
+
+ pa_ioline_set_callback(c->line, line_callback, c);
+ pa_idxset_put(p->connections, c, NULL);
+}
+
+pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
+ pa_protocol_http* p;
+ assert(core && server);
+
+ p = pa_xmalloc(sizeof(pa_protocol_http));
+ p->module = m;
+ p->core = core;
+ p->server = server;
+ p->connections = pa_idxset_new(NULL, NULL);
+
+ pa_socket_server_set_callback(p->server, on_connection, p);
+
+ return p;
+}
+
+static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
+ assert(p);
+ connection_free(p, 0);
+}
+
+void pa_protocol_http_free(pa_protocol_http *p) {
+ assert(p);
+
+ pa_idxset_free(p->connections, free_connection, NULL);
+ pa_socket_server_unref(p->server);
+ pa_xfree(p);
+}
diff --git a/src/polypcore/protocol-http.h b/src/polypcore/protocol-http.h
new file mode 100644
index 00000000..0a1855e9
--- /dev/null
+++ b/src/polypcore/protocol-http.h
@@ -0,0 +1,35 @@
+#ifndef fooprotocolhttphfoo
+#define fooprotocolhttphfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "core.h"
+#include "socket-server.h"
+#include "module.h"
+#include "modargs.h"
+
+typedef struct pa_protocol_http pa_protocol_http;
+
+pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
+void pa_protocol_http_free(pa_protocol_http *n);
+
+#endif
diff --git a/src/polypcore/protocol-native.c b/src/polypcore/protocol-native.c
new file mode 100644
index 00000000..b94903d9
--- /dev/null
+++ b/src/polypcore/protocol-native.c
@@ -0,0 +1,2216 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdlib.h>
+
+#include "protocol-native.h"
+#include "native-common.h"
+#include "packet.h"
+#include "client.h"
+#include "source-output.h"
+#include "sink-input.h"
+#include "pstream.h"
+#include "tagstruct.h"
+#include "pdispatch.h"
+#include "pstream-util.h"
+#include "authkey.h"
+#include "namereg.h"
+#include "scache.h"
+#include "xmalloc.h"
+#include "util.h"
+#include "subscribe.h"
+#include "log.h"
+#include "autoload.h"
+#include "authkey-prop.h"
+#include "strlist.h"
+#include "props.h"
+
+/* Kick a client if it doesn't authenticate within this time */
+#define AUTH_TIMEOUT 5
+
+/* Don't accept more connection than this */
+#define MAX_CONNECTIONS 10
+
+struct connection;
+struct pa_protocol_native;
+
+struct record_stream {
+ struct connection *connection;
+ uint32_t index;
+ pa_source_output *source_output;
+ pa_memblockq *memblockq;
+ size_t fragment_size;
+};
+
+struct playback_stream {
+ int type;
+ struct connection *connection;
+ uint32_t index;
+ pa_sink_input *sink_input;
+ pa_memblockq *memblockq;
+ size_t requested_bytes;
+ int drain_request;
+ uint32_t drain_tag;
+};
+
+struct upload_stream {
+ int type;
+ struct connection *connection;
+ uint32_t index;
+ pa_memchunk memchunk;
+ size_t length;
+ char *name;
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+};
+
+struct output_stream {
+ int type;
+};
+
+enum {
+ UPLOAD_STREAM,
+ PLAYBACK_STREAM
+};
+
+struct connection {
+ int authorized;
+ pa_protocol_native *protocol;
+ pa_client *client;
+ pa_pstream *pstream;
+ pa_pdispatch *pdispatch;
+ pa_idxset *record_streams, *output_streams;
+ uint32_t rrobin_index;
+ pa_subscription *subscription;
+ pa_time_event *auth_timeout_event;
+};
+
+struct pa_protocol_native {
+ pa_module *module;
+ int public;
+ pa_core *core;
+ pa_socket_server *server;
+ pa_idxset *connections;
+ uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
+ int auth_cookie_in_property;
+};
+
+static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk);
+static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length);
+static void sink_input_kill_cb(pa_sink_input *i);
+static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i);
+
+static void request_bytes(struct playback_stream*s);
+
+static void source_output_kill_cb(pa_source_output *o);
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+
+static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_remove_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_volume(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_flush_or_trigger_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = {
+ [PA_COMMAND_ERROR] = NULL,
+ [PA_COMMAND_TIMEOUT] = NULL,
+ [PA_COMMAND_REPLY] = NULL,
+ [PA_COMMAND_CREATE_PLAYBACK_STREAM] = command_create_playback_stream,
+ [PA_COMMAND_DELETE_PLAYBACK_STREAM] = command_delete_stream,
+ [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = command_drain_playback_stream,
+ [PA_COMMAND_CREATE_RECORD_STREAM] = command_create_record_stream,
+ [PA_COMMAND_DELETE_RECORD_STREAM] = command_delete_stream,
+ [PA_COMMAND_AUTH] = command_auth,
+ [PA_COMMAND_REQUEST] = NULL,
+ [PA_COMMAND_EXIT] = command_exit,
+ [PA_COMMAND_SET_CLIENT_NAME] = command_set_client_name,
+ [PA_COMMAND_LOOKUP_SINK] = command_lookup,
+ [PA_COMMAND_LOOKUP_SOURCE] = command_lookup,
+ [PA_COMMAND_STAT] = command_stat,
+ [PA_COMMAND_GET_PLAYBACK_LATENCY] = command_get_playback_latency,
+ [PA_COMMAND_GET_RECORD_LATENCY] = command_get_record_latency,
+ [PA_COMMAND_CREATE_UPLOAD_STREAM] = command_create_upload_stream,
+ [PA_COMMAND_DELETE_UPLOAD_STREAM] = command_delete_stream,
+ [PA_COMMAND_FINISH_UPLOAD_STREAM] = command_finish_upload_stream,
+ [PA_COMMAND_PLAY_SAMPLE] = command_play_sample,
+ [PA_COMMAND_REMOVE_SAMPLE] = command_remove_sample,
+ [PA_COMMAND_GET_SINK_INFO] = command_get_info,
+ [PA_COMMAND_GET_SOURCE_INFO] = command_get_info,
+ [PA_COMMAND_GET_CLIENT_INFO] = command_get_info,
+ [PA_COMMAND_GET_MODULE_INFO] = command_get_info,
+ [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info,
+ [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info,
+ [PA_COMMAND_GET_SAMPLE_INFO] = command_get_info,
+ [PA_COMMAND_GET_SINK_INFO_LIST] = command_get_info_list,
+ [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list,
+ [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list,
+ [PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list,
+ [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list,
+ [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list,
+ [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list,
+ [PA_COMMAND_GET_SERVER_INFO] = command_get_server_info,
+ [PA_COMMAND_SUBSCRIBE] = command_subscribe,
+
+ [PA_COMMAND_SET_SINK_VOLUME] = command_set_volume,
+ [PA_COMMAND_SET_SINK_INPUT_VOLUME] = command_set_volume,
+
+ [PA_COMMAND_CORK_PLAYBACK_STREAM] = command_cork_playback_stream,
+ [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_flush_or_trigger_playback_stream,
+ [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_flush_or_trigger_playback_stream,
+ [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_flush_or_trigger_playback_stream,
+
+ [PA_COMMAND_CORK_RECORD_STREAM] = command_cork_record_stream,
+ [PA_COMMAND_FLUSH_RECORD_STREAM] = command_flush_record_stream,
+
+ [PA_COMMAND_SET_DEFAULT_SINK] = command_set_default_sink_or_source,
+ [PA_COMMAND_SET_DEFAULT_SOURCE] = command_set_default_sink_or_source,
+ [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = command_set_stream_name,
+ [PA_COMMAND_SET_RECORD_STREAM_NAME] = command_set_stream_name,
+ [PA_COMMAND_KILL_CLIENT] = command_kill,
+ [PA_COMMAND_KILL_SINK_INPUT] = command_kill,
+ [PA_COMMAND_KILL_SOURCE_OUTPUT] = command_kill,
+ [PA_COMMAND_LOAD_MODULE] = command_load_module,
+ [PA_COMMAND_UNLOAD_MODULE] = command_unload_module,
+ [PA_COMMAND_GET_AUTOLOAD_INFO] = command_get_autoload_info,
+ [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = command_get_autoload_info_list,
+ [PA_COMMAND_ADD_AUTOLOAD] = command_add_autoload,
+ [PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload,
+
+};
+
+/* structure management */
+
+static struct upload_stream* upload_stream_new(
+ struct connection *c,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const char *name, size_t length) {
+
+ struct upload_stream *s;
+ assert(c && ss && name && length);
+
+ s = pa_xmalloc(sizeof(struct upload_stream));
+ s->type = UPLOAD_STREAM;
+ s->connection = c;
+ s->sample_spec = *ss;
+ s->channel_map = *map;
+ s->name = pa_xstrdup(name);
+
+ s->memchunk.memblock = NULL;
+ s->memchunk.index = 0;
+ s->memchunk.length = 0;
+
+ s->length = length;
+
+ pa_idxset_put(c->output_streams, s, &s->index);
+ return s;
+}
+
+static void upload_stream_free(struct upload_stream *o) {
+ assert(o && o->connection);
+
+ pa_idxset_remove_by_data(o->connection->output_streams, o, NULL);
+
+ pa_xfree(o->name);
+
+ if (o->memchunk.memblock)
+ pa_memblock_unref(o->memchunk.memblock);
+
+ pa_xfree(o);
+}
+
+static struct record_stream* record_stream_new(
+ struct connection *c,
+ pa_source *source,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const char *name,
+ size_t maxlength,
+ size_t fragment_size) {
+
+ struct record_stream *s;
+ pa_source_output *source_output;
+ size_t base;
+ assert(c && source && ss && name && maxlength);
+
+ if (!(source_output = pa_source_output_new(source, __FILE__, name, ss, map, -1)))
+ return NULL;
+
+ s = pa_xmalloc(sizeof(struct record_stream));
+ s->connection = c;
+ s->source_output = source_output;
+ s->source_output->push = source_output_push_cb;
+ s->source_output->kill = source_output_kill_cb;
+ s->source_output->get_latency = source_output_get_latency_cb;
+ s->source_output->userdata = s;
+ s->source_output->owner = c->protocol->module;
+ s->source_output->client = c->client;
+
+ s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_frame_size(ss), 0, 0, c->protocol->core->memblock_stat);
+ assert(s->memblockq);
+
+ s->fragment_size = (fragment_size/base)*base;
+ if (!s->fragment_size)
+ s->fragment_size = base;
+
+ pa_idxset_put(c->record_streams, s, &s->index);
+ return s;
+}
+
+static void record_stream_free(struct record_stream* r) {
+ assert(r && r->connection);
+
+ pa_idxset_remove_by_data(r->connection->record_streams, r, NULL);
+ pa_source_output_disconnect(r->source_output);
+ pa_source_output_unref(r->source_output);
+ pa_memblockq_free(r->memblockq);
+ pa_xfree(r);
+}
+
+static struct playback_stream* playback_stream_new(
+ struct connection *c,
+ pa_sink *sink,
+ const pa_sample_spec *ss,
+ const pa_channel_map *map,
+ const char *name,
+ size_t maxlength,
+ size_t tlength,
+ size_t prebuf,
+ size_t minreq,
+ pa_cvolume *volume) {
+
+ struct playback_stream *s;
+ pa_sink_input *sink_input;
+ assert(c && sink && ss && name && maxlength);
+
+ if (!(sink_input = pa_sink_input_new(sink, __FILE__, name, ss, map, 0, -1)))
+ return NULL;
+
+ s = pa_xmalloc(sizeof(struct playback_stream));
+ s->type = PLAYBACK_STREAM;
+ s->connection = c;
+ s->sink_input = sink_input;
+
+ s->sink_input->peek = sink_input_peek_cb;
+ s->sink_input->drop = sink_input_drop_cb;
+ s->sink_input->kill = sink_input_kill_cb;
+ s->sink_input->get_latency = sink_input_get_latency_cb;
+ s->sink_input->userdata = s;
+ s->sink_input->owner = c->protocol->module;
+ s->sink_input->client = c->client;
+
+ s->memblockq = pa_memblockq_new(maxlength, tlength, pa_frame_size(ss), prebuf, minreq, c->protocol->core->memblock_stat);
+ assert(s->memblockq);
+
+ s->requested_bytes = 0;
+ s->drain_request = 0;
+
+ s->sink_input->volume = *volume;
+
+ pa_idxset_put(c->output_streams, s, &s->index);
+ return s;
+}
+
+static void playback_stream_free(struct playback_stream* p) {
+ assert(p && p->connection);
+
+ if (p->drain_request)
+ pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERROR_NOENTITY);
+
+ pa_idxset_remove_by_data(p->connection->output_streams, p, NULL);
+ pa_sink_input_disconnect(p->sink_input);
+ pa_sink_input_unref(p->sink_input);
+ pa_memblockq_free(p->memblockq);
+ pa_xfree(p);
+}
+
+static void connection_free(struct connection *c) {
+ struct record_stream *r;
+ struct output_stream *o;
+ assert(c && c->protocol);
+
+ pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+ while ((r = pa_idxset_first(c->record_streams, NULL)))
+ record_stream_free(r);
+ pa_idxset_free(c->record_streams, NULL, NULL);
+
+ while ((o = pa_idxset_first(c->output_streams, NULL)))
+ if (o->type == PLAYBACK_STREAM)
+ playback_stream_free((struct playback_stream*) o);
+ else
+ upload_stream_free((struct upload_stream*) o);
+ pa_idxset_free(c->output_streams, NULL, NULL);
+
+ pa_pdispatch_unref(c->pdispatch);
+ pa_pstream_close(c->pstream);
+ pa_pstream_unref(c->pstream);
+ pa_client_free(c->client);
+
+ if (c->subscription)
+ pa_subscription_free(c->subscription);
+
+ if (c->auth_timeout_event)
+ c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+
+ pa_xfree(c);
+}
+
+static void request_bytes(struct playback_stream *s) {
+ pa_tagstruct *t;
+ size_t l;
+ assert(s);
+
+ if (!(l = pa_memblockq_missing(s->memblockq)))
+ return;
+
+ if (l <= s->requested_bytes)
+ return;
+
+ l -= s->requested_bytes;
+
+ if (l < pa_memblockq_get_minreq(s->memblockq))
+ return;
+
+ s->requested_bytes += l;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, l);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+
+/* pa_log(__FILE__": Requesting %u bytes\n", l); */
+}
+
+static void send_memblock(struct connection *c) {
+ uint32_t start;
+ struct record_stream *r;
+
+ start = PA_IDXSET_INVALID;
+ for (;;) {
+ pa_memchunk chunk;
+
+ if (!(r = pa_idxset_rrobin(c->record_streams, &c->rrobin_index)))
+ return;
+
+ if (start == PA_IDXSET_INVALID)
+ start = c->rrobin_index;
+ else if (start == c->rrobin_index)
+ return;
+
+ if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) {
+ pa_memchunk schunk = chunk;
+
+ if (schunk.length > r->fragment_size)
+ schunk.length = r->fragment_size;
+
+ pa_pstream_send_memblock(c->pstream, r->index, 0, &schunk);
+ pa_memblockq_drop(r->memblockq, &chunk, schunk.length);
+ pa_memblock_unref(schunk.memblock);
+
+ return;
+ }
+ }
+}
+
+static void send_playback_stream_killed(struct playback_stream *p) {
+ pa_tagstruct *t;
+ assert(p);
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, p->index);
+ pa_pstream_send_tagstruct(p->connection->pstream, t);
+}
+
+static void send_record_stream_killed(struct record_stream *r) {
+ pa_tagstruct *t;
+ assert(r);
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, r->index);
+ pa_pstream_send_tagstruct(r->connection->pstream, t);
+}
+
+/*** sinkinput callbacks ***/
+
+static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
+ struct playback_stream *s;
+ assert(i && i->userdata && chunk);
+ s = i->userdata;
+
+ if (pa_memblockq_peek(s->memblockq, chunk) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
+ struct playback_stream *s;
+ assert(i && i->userdata && length);
+ s = i->userdata;
+
+ pa_memblockq_drop(s->memblockq, chunk, length);
+ request_bytes(s);
+
+ if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) {
+ pa_pstream_send_simple_ack(s->connection->pstream, s->drain_tag);
+ s->drain_request = 0;
+ }
+
+/* pa_log(__FILE__": after_drop: %u\n", pa_memblockq_get_length(s->memblockq)); */
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+ assert(i && i->userdata);
+ send_playback_stream_killed((struct playback_stream *) i->userdata);
+ playback_stream_free((struct playback_stream *) i->userdata);
+}
+
+static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
+ struct playback_stream *s;
+ assert(i && i->userdata);
+ s = i->userdata;
+
+ /*pa_log(__FILE__": get_latency: %u\n", pa_memblockq_get_length(s->memblockq));*/
+
+ return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
+}
+
+/*** source_output callbacks ***/
+
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+ struct record_stream *s;
+ assert(o && o->userdata && chunk);
+ s = o->userdata;
+
+ pa_memblockq_push_align(s->memblockq, chunk, 0);
+ if (!pa_pstream_is_pending(s->connection->pstream))
+ send_memblock(s->connection);
+}
+
+static void source_output_kill_cb(pa_source_output *o) {
+ assert(o && o->userdata);
+ send_record_stream_killed((struct record_stream *) o->userdata);
+ record_stream_free((struct record_stream *) o->userdata);
+}
+
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+ struct record_stream *s;
+ assert(o && o->userdata);
+ s = o->userdata;
+
+ /*pa_log(__FILE__": get_latency: %u\n", pa_memblockq_get_length(s->memblockq));*/
+
+ return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
+}
+
+/*** pdispatch callbacks ***/
+
+static void protocol_error(struct connection *c) {
+ pa_log(__FILE__": protocol error, kicking client\n");
+ connection_free(c);
+}
+
+static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ struct playback_stream *s;
+ size_t maxlength, tlength, prebuf, minreq;
+ uint32_t sink_index;
+ const char *name, *sink_name;
+ pa_sample_spec ss;
+ pa_channel_map map;
+ pa_tagstruct *reply;
+ pa_sink *sink;
+ pa_cvolume volume;
+ int corked;
+
+ assert(c && t && c->protocol && c->protocol->core);
+
+ if (pa_tagstruct_get(
+ t,
+ PA_TAG_STRING, &name,
+ PA_TAG_SAMPLE_SPEC, &ss,
+ PA_TAG_CHANNEL_MAP, &map,
+ PA_TAG_U32, &sink_index,
+ PA_TAG_STRING, &sink_name,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_BOOLEAN, &corked,
+ PA_TAG_U32, &tlength,
+ PA_TAG_U32, &prebuf,
+ PA_TAG_U32, &minreq,
+ PA_TAG_CVOLUME, &volume,
+ PA_TAG_INVALID) < 0 ||
+ !pa_tagstruct_eof(t) ||
+ !name) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (sink_index != (uint32_t) -1)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
+ else
+ sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1);
+
+ if (!sink) {
+ pa_log("%s: Can't find a suitable sink.\n", __FILE__);
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ if (!(s = playback_stream_new(c, sink, &ss, &map, name, maxlength, tlength, prebuf, minreq, &volume))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
+ return;
+ }
+
+ pa_sink_input_cork(s->sink_input, corked);
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_putu32(reply, s->index);
+ assert(s->sink_input);
+ pa_tagstruct_putu32(reply, s->sink_input->index);
+ pa_tagstruct_putu32(reply, s->requested_bytes = pa_memblockq_missing(s->memblockq));
+ pa_pstream_send_tagstruct(c->pstream, reply);
+ request_bytes(s);
+}
+
+static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t channel;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (command == PA_COMMAND_DELETE_PLAYBACK_STREAM) {
+ struct playback_stream *s;
+ if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != PLAYBACK_STREAM)) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
+ return;
+ }
+
+ playback_stream_free(s);
+ } else if (command == PA_COMMAND_DELETE_RECORD_STREAM) {
+ struct record_stream *s;
+ if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
+ return;
+ }
+
+ record_stream_free(s);
+ } else {
+ struct upload_stream *s;
+ assert(command == PA_COMMAND_DELETE_UPLOAD_STREAM);
+ if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
+ return;
+ }
+
+ upload_stream_free(s);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ struct record_stream *s;
+ size_t maxlength, fragment_size;
+ uint32_t source_index;
+ const char *name, *source_name;
+ pa_sample_spec ss;
+ pa_channel_map map;
+ pa_tagstruct *reply;
+ pa_source *source;
+ int corked;
+ assert(c && t && c->protocol && c->protocol->core);
+
+ if (pa_tagstruct_gets(t, &name) < 0 || !name ||
+ pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_get_channel_map(t, &map) < 0 ||
+ pa_tagstruct_getu32(t, &source_index) < 0 ||
+ pa_tagstruct_gets(t, &source_name) < 0 ||
+ pa_tagstruct_getu32(t, &maxlength) < 0 ||
+ pa_tagstruct_get_boolean(t, &corked) < 0 ||
+ pa_tagstruct_getu32(t, &fragment_size) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (source_index != (uint32_t) -1)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, source_index);
+ else
+ source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1);
+
+ if (!source) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ if (!(s = record_stream_new(c, source, &ss, &map, name, maxlength, fragment_size))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
+ return;
+ }
+
+ pa_source_output_cork(s->source_output, corked);
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_putu32(reply, s->index);
+ assert(s->source_output);
+ pa_tagstruct_putu32(reply, s->source_output->index);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ assert(c && t);
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop);
+ c->protocol->core->mainloop->quit(c->protocol->core->mainloop, 0);
+ pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
+ return;
+}
+
+static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ const void*cookie;
+ assert(c && t);
+
+ if (pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) {
+ pa_log(__FILE__": Denied access to client with invalid authorization key.\n");
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ c->authorized = 1;
+ if (c->auth_timeout_event) {
+ c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+ c->auth_timeout_event = NULL;
+ }
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+ return;
+}
+
+static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ const char *name;
+ assert(c && t);
+
+ if (pa_tagstruct_gets(t, &name) < 0 || !name ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ pa_client_set_name(c->client, name);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+ return;
+}
+
+static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ const char *name;
+ uint32_t idx = PA_IDXSET_INVALID;
+ assert(c && t);
+
+ if (pa_tagstruct_gets(t, &name) < 0 || !name ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (command == PA_COMMAND_LOOKUP_SINK) {
+ pa_sink *sink;
+ if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1)))
+ idx = sink->index;
+ } else {
+ pa_source *source;
+ assert(command == PA_COMMAND_LOOKUP_SOURCE);
+ if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1)))
+ idx = source->index;
+ }
+
+ if (idx == PA_IDXSET_INVALID)
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ else {
+ pa_tagstruct *reply;
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_putu32(reply, idx);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+ }
+}
+
+static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ struct playback_stream *s;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ s->drain_request = 0;
+
+ pa_memblockq_prebuf_disable(s->memblockq);
+
+ if (!pa_memblockq_is_readable(s->memblockq)) {
+/* pa_log("immediate drain: %u\n", pa_memblockq_get_length(s->memblockq)); */
+ pa_pstream_send_simple_ack(c->pstream, tag);
+ } else {
+/* pa_log("slow drain triggered\n"); */
+ s->drain_request = 1;
+ s->drain_tag = tag;
+
+ pa_sink_notify(s->sink_input->sink);
+ }
+}
+
+static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ pa_tagstruct *reply;
+ assert(c && t);
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->total);
+ pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->total_size);
+ pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->allocated);
+ pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->allocated_size);
+ pa_tagstruct_putu32(reply, pa_scache_total_size(c->protocol->core));
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ pa_tagstruct *reply;
+ struct playback_stream *s;
+ struct timeval tv, now;
+ uint64_t counter;
+ uint32_t idx;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_get_timeval(t, &tv) < 0 ||
+ pa_tagstruct_getu64(t, &counter) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_put_usec(reply, pa_sink_input_get_latency(s->sink_input));
+ pa_tagstruct_put_usec(reply, pa_sink_get_latency(s->sink_input->sink));
+ pa_tagstruct_put_usec(reply, 0);
+ pa_tagstruct_put_boolean(reply, pa_memblockq_is_readable(s->memblockq));
+ pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq));
+ pa_tagstruct_put_timeval(reply, &tv);
+ pa_gettimeofday(&now);
+ pa_tagstruct_put_timeval(reply, &now);
+ pa_tagstruct_putu64(reply, counter);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ pa_tagstruct *reply;
+ struct record_stream *s;
+ struct timeval tv, now;
+ uint64_t counter;
+ uint32_t idx;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_get_timeval(t, &tv) < 0 ||
+ pa_tagstruct_getu64(t, &counter) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_put_usec(reply, pa_source_output_get_latency(s->source_output));
+ pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
+ pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
+ pa_tagstruct_put_boolean(reply, 0);
+ pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq));
+ pa_tagstruct_put_timeval(reply, &tv);
+ pa_gettimeofday(&now);
+ pa_tagstruct_put_timeval(reply, &now);
+ pa_tagstruct_putu64(reply, counter);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+
+static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ struct upload_stream *s;
+ size_t length;
+ const char *name;
+ pa_sample_spec ss;
+ pa_channel_map map;
+ pa_tagstruct *reply;
+ assert(c && t && c->protocol && c->protocol->core);
+
+ if (pa_tagstruct_gets(t, &name) < 0 || !name ||
+ pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_get_channel_map(t, &map) < 0 ||
+ pa_tagstruct_getu32(t, &length) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if ((length % pa_frame_size(&ss)) != 0 || length <= 0 || !*name) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
+ return;
+ }
+
+ if (!(s = upload_stream_new(c, &ss, &map, name, length))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_putu32(reply, s->index);
+ pa_tagstruct_putu32(reply, length);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t channel;
+ struct upload_stream *s;
+ uint32_t idx;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
+ return;
+ }
+
+ pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, &idx);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+ upload_stream_free(s);
+}
+
+static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t sink_index;
+ pa_cvolume volume;
+ pa_sink *sink;
+ const char *name, *sink_name;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
+ pa_tagstruct_gets(t, &sink_name) < 0 ||
+ pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 || !name ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (sink_index != (uint32_t) -1)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
+ else
+ sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1);
+
+ if (!sink) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ if (pa_scache_play_item(c->protocol->core, name, sink, &volume) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ const char *name;
+ assert(c && t);
+
+ if (pa_tagstruct_gets(t, &name) < 0 || !name ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (pa_scache_remove_item(c->protocol->core, name) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) {
+ assert(t && sink);
+ pa_tagstruct_put(
+ t,
+ PA_TAG_U32, sink->index,
+ PA_TAG_STRING, sink->name,
+ PA_TAG_STRING, sink->description,
+ PA_TAG_SAMPLE_SPEC, &sink->sample_spec,
+ PA_TAG_CHANNEL_MAP, &sink->channel_map,
+ PA_TAG_U32, sink->owner ? sink->owner->index : PA_INVALID_INDEX,
+ PA_TAG_CVOLUME, pa_sink_get_volume(sink, PA_MIXER_HARDWARE),
+ PA_TAG_U32, sink->monitor_source->index,
+ PA_TAG_STRING, sink->monitor_source->name,
+ PA_TAG_USEC, pa_sink_get_latency(sink),
+ PA_TAG_STRING, sink->driver,
+ PA_TAG_INVALID);
+}
+
+static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) {
+ assert(t && source);
+ pa_tagstruct_putu32(t, source->index);
+ pa_tagstruct_puts(t, source->name);
+ pa_tagstruct_puts(t, source->description);
+ pa_tagstruct_put_sample_spec(t, &source->sample_spec);
+ pa_tagstruct_put_channel_map(t, &source->channel_map);
+ pa_tagstruct_putu32(t, source->owner ? source->owner->index : (uint32_t) -1);
+ pa_tagstruct_putu32(t, source->monitor_of ? source->monitor_of->index : (uint32_t) -1);
+ pa_tagstruct_puts(t, source->monitor_of ? source->monitor_of->name : NULL);
+ pa_tagstruct_put_usec(t, pa_source_get_latency(source));
+ pa_tagstruct_puts(t, source->driver);
+}
+
+static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) {
+ assert(t && client);
+ pa_tagstruct_putu32(t, client->index);
+ pa_tagstruct_puts(t, client->name);
+ pa_tagstruct_putu32(t, client->owner ? client->owner->index : (uint32_t) -1);
+ pa_tagstruct_puts(t, client->driver);
+}
+
+static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
+ assert(t && module);
+ pa_tagstruct_putu32(t, module->index);
+ pa_tagstruct_puts(t, module->name);
+ pa_tagstruct_puts(t, module->argument);
+ pa_tagstruct_putu32(t, module->n_used);
+ pa_tagstruct_put_boolean(t, module->auto_unload);
+}
+
+static void sink_input_fill_tagstruct(pa_tagstruct *t, pa_sink_input *s) {
+ assert(t && s);
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_puts(t, s->name);
+ pa_tagstruct_putu32(t, s->owner ? s->owner->index : (uint32_t) -1);
+ pa_tagstruct_putu32(t, s->client ? s->client->index : (uint32_t) -1);
+ pa_tagstruct_putu32(t, s->sink->index);
+ pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+ pa_tagstruct_put_channel_map(t, &s->channel_map);
+ pa_tagstruct_put_cvolume(t, &s->volume);
+ pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s));
+ pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink));
+ pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
+ pa_tagstruct_puts(t, s->driver);
+}
+
+static void source_output_fill_tagstruct(pa_tagstruct *t, pa_source_output *s) {
+ assert(t && s);
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_puts(t, s->name);
+ pa_tagstruct_putu32(t, s->owner ? s->owner->index : (uint32_t) -1);
+ pa_tagstruct_putu32(t, s->client ? s->client->index : (uint32_t) -1);
+ pa_tagstruct_putu32(t, s->source->index);
+ pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+ pa_tagstruct_put_channel_map(t, &s->channel_map);
+ pa_tagstruct_put_usec(t, pa_source_output_get_latency(s));
+ pa_tagstruct_put_usec(t, pa_source_get_latency(s->source));
+ pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s)));
+ pa_tagstruct_puts(t, s->driver);
+}
+
+static void scache_fill_tagstruct(pa_tagstruct *t, pa_scache_entry *e) {
+ assert(t && e);
+ pa_tagstruct_putu32(t, e->index);
+ pa_tagstruct_puts(t, e->name);
+ pa_tagstruct_put_cvolume(t, &e->volume);
+ pa_tagstruct_put_usec(t, pa_bytes_to_usec(e->memchunk.length, &e->sample_spec));
+ pa_tagstruct_put_sample_spec(t, &e->sample_spec);
+ pa_tagstruct_put_channel_map(t, &e->channel_map);
+ pa_tagstruct_putu32(t, e->memchunk.length);
+ pa_tagstruct_put_boolean(t, e->lazy);
+ pa_tagstruct_puts(t, e->filename);
+}
+
+static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ pa_sink *sink = NULL;
+ pa_source *source = NULL;
+ pa_client *client = NULL;
+ pa_module *module = NULL;
+ pa_sink_input *si = NULL;
+ pa_source_output *so = NULL;
+ pa_scache_entry *sce = NULL;
+ const char *name;
+ pa_tagstruct *reply;
+ assert(c && t);
+
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ (command != PA_COMMAND_GET_CLIENT_INFO &&
+ command != PA_COMMAND_GET_MODULE_INFO &&
+ command != PA_COMMAND_GET_SINK_INPUT_INFO &&
+ command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO &&
+ pa_tagstruct_gets(t, &name) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (command == PA_COMMAND_GET_SINK_INFO) {
+ if (idx != (uint32_t) -1)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+ } else if (command == PA_COMMAND_GET_SOURCE_INFO) {
+ if (idx != (uint32_t) -1)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+ } else if (command == PA_COMMAND_GET_CLIENT_INFO)
+ client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
+ else if (command == PA_COMMAND_GET_MODULE_INFO)
+ module = pa_idxset_get_by_index(c->protocol->core->modules, idx);
+ else if (command == PA_COMMAND_GET_SINK_INPUT_INFO)
+ si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+ else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO)
+ so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+ else {
+ assert(command == PA_COMMAND_GET_SAMPLE_INFO);
+ if (idx != (uint32_t) -1)
+ sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);
+ else
+ sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE, 0);
+ }
+
+ if (!sink && !source && !client && !module && !si && !so && !sce) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ if (sink)
+ sink_fill_tagstruct(reply, sink);
+ else if (source)
+ source_fill_tagstruct(reply, source);
+ else if (client)
+ client_fill_tagstruct(reply, client);
+ else if (module)
+ module_fill_tagstruct(reply, module);
+ else if (si)
+ sink_input_fill_tagstruct(reply, si);
+ else if (so)
+ source_output_fill_tagstruct(reply, so);
+ else
+ scache_fill_tagstruct(reply, sce);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ pa_idxset *i;
+ uint32_t idx;
+ void *p;
+ pa_tagstruct *reply;
+ assert(c && t);
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+
+ if (command == PA_COMMAND_GET_SINK_INFO_LIST)
+ i = c->protocol->core->sinks;
+ else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
+ i = c->protocol->core->sources;
+ else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
+ i = c->protocol->core->clients;
+ else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
+ i = c->protocol->core->modules;
+ else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
+ i = c->protocol->core->sink_inputs;
+ else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
+ i = c->protocol->core->source_outputs;
+ else {
+ assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+ i = c->protocol->core->scache;
+ }
+
+ if (i) {
+ for (p = pa_idxset_first(i, &idx); p; p = pa_idxset_next(i, &idx)) {
+ if (command == PA_COMMAND_GET_SINK_INFO_LIST)
+ sink_fill_tagstruct(reply, p);
+ else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
+ source_fill_tagstruct(reply, p);
+ else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
+ client_fill_tagstruct(reply, p);
+ else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
+ module_fill_tagstruct(reply, p);
+ else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
+ sink_input_fill_tagstruct(reply, p);
+ else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
+ source_output_fill_tagstruct(reply, p);
+ else {
+ assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+ scache_fill_tagstruct(reply, p);
+ }
+ }
+ }
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ pa_tagstruct *reply;
+ char txt[256];
+ const char *n;
+ assert(c && t);
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_puts(reply, PACKAGE_NAME);
+ pa_tagstruct_puts(reply, PACKAGE_VERSION);
+ pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
+ pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt)));
+ pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec);
+
+ n = pa_namereg_get_default_sink_name(c->protocol->core);
+ pa_tagstruct_puts(reply, n);
+ n = pa_namereg_get_default_source_name(c->protocol->core);
+ pa_tagstruct_puts(reply, n);
+
+ pa_tagstruct_putu32(reply, c->protocol->core->cookie);
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) {
+ pa_tagstruct *t;
+ struct connection *c = userdata;
+ assert(c && core);
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
+ pa_tagstruct_putu32(t, (uint32_t) -1);
+ pa_tagstruct_putu32(t, e);
+ pa_tagstruct_putu32(t, idx);
+ pa_pstream_send_tagstruct(c->pstream, t);
+}
+
+static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ pa_subscription_mask_t m;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &m) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (c->subscription)
+ pa_subscription_free(c->subscription);
+
+ if (m != 0) {
+ c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c);
+ assert(c->subscription);
+ } else
+ c->subscription = NULL;
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_volume(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ pa_cvolume volume;
+ pa_sink *sink = NULL;
+ pa_sink_input *si = NULL;
+ const char *name = NULL;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ (command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
+ pa_tagstruct_get_cvolume(t, &volume) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (command == PA_COMMAND_SET_SINK_VOLUME) {
+ if (idx != (uint32_t) -1)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+ } else {
+ assert(command == PA_COMMAND_SET_SINK_INPUT_VOLUME);
+ si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+ }
+
+ if (!si && !sink) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ if (sink)
+ pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &volume);
+ else if (si)
+ pa_sink_input_set_volume(si, &volume);
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ int b;
+ struct playback_stream *s;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_get_boolean(t, &b) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_sink_input_cork(s->sink_input, b);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_flush_or_trigger_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ struct playback_stream *s;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ if (command == PA_COMMAND_PREBUF_PLAYBACK_STREAM)
+ pa_memblockq_prebuf_reenable(s->memblockq);
+ else if (command == PA_COMMAND_TRIGGER_PLAYBACK_STREAM)
+ pa_memblockq_prebuf_disable(s->memblockq);
+ else {
+ assert(command == PA_COMMAND_FLUSH_PLAYBACK_STREAM);
+ pa_memblockq_flush(s->memblockq);
+ /*pa_log(__FILE__": flush: %u\n", pa_memblockq_get_length(s->memblockq));*/
+ }
+
+ pa_sink_notify(s->sink_input->sink);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+ request_bytes(s);
+}
+
+static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ struct record_stream *s;
+ int b;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_get_boolean(t, &b) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_source_output_cork(s->source_output, b);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ struct record_stream *s;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_memblockq_flush(s->memblockq);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ const char *s;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &s) < 0 || !s ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ const char *name;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 || !name ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) {
+ struct playback_stream *s;
+
+ if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_sink_input_set_name(s->sink_input, name);
+
+ } else {
+ struct record_stream *s;
+
+ if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_source_output_set_name(s->source_output, name);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (command == PA_COMMAND_KILL_CLIENT) {
+ pa_client *client;
+
+ if (!(client = pa_idxset_get_by_index(c->protocol->core->clients, idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_client_kill(client);
+ } else if (command == PA_COMMAND_KILL_SINK_INPUT) {
+ pa_sink_input *s;
+
+ if (!(s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_sink_input_kill(s);
+ } else {
+ pa_source_output *s;
+
+ assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);
+
+ if (!(s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_source_output_kill(s);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ pa_module *m;
+ const char *name, *argument;
+ pa_tagstruct *reply;
+ assert(c && t);
+
+ if (pa_tagstruct_gets(t, &name) < 0 || !name ||
+ pa_tagstruct_gets(t, &argument) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (!(m = pa_module_load(c->protocol->core, name, argument))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_INITFAILED);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_putu32(reply, m->index);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t idx;
+ pa_module *m;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (!(m = pa_idxset_get_by_index(c->protocol->core->modules, idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_module_unload_request(m);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ const char *name, *module, *argument;
+ uint32_t type;
+ uint32_t idx;
+ pa_tagstruct *reply;
+ assert(c && t);
+
+ if (pa_tagstruct_gets(t, &name) < 0 || !name ||
+ pa_tagstruct_getu32(t, &type) < 0 || type > 1 ||
+ pa_tagstruct_gets(t, &module) < 0 || !module ||
+ pa_tagstruct_gets(t, &argument) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (pa_autoload_add(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, module, argument, &idx) < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_putu32(reply, idx);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ const char *name = NULL;
+ uint32_t type, idx = PA_IDXSET_INVALID;
+ int r;
+ assert(c && t);
+
+ if ((pa_tagstruct_getu32(t, &idx) < 0 &&
+ (pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_getu32(t, &type) < 0)) ||
+ (!name && idx == PA_IDXSET_INVALID) ||
+ (name && type > 1) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ if (name)
+ r = pa_autoload_remove_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
+ else
+ r = pa_autoload_remove_by_index(c->protocol->core, idx);
+
+ if (r < 0) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e) {
+ assert(t && e);
+
+ pa_tagstruct_putu32(t, e->index);
+ pa_tagstruct_puts(t, e->name);
+ pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0 : 1);
+ pa_tagstruct_puts(t, e->module);
+ pa_tagstruct_puts(t, e->argument);
+}
+
+static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ const pa_autoload_entry *a = NULL;
+ uint32_t type, idx;
+ const char *name;
+ pa_tagstruct *reply;
+ assert(c && t);
+
+ if ((pa_tagstruct_getu32(t, &idx) < 0 &&
+ (pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_getu32(t, &type) < 0)) ||
+ (!name && idx == PA_IDXSET_INVALID) ||
+ (name && type > 1) ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+
+ if (name)
+ a = pa_autoload_get_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
+ else
+ a = pa_autoload_get_by_index(c->protocol->core, idx);
+
+ if (!a) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ autoload_fill_tagstruct(reply, a);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ pa_tagstruct *reply;
+ assert(c && t);
+
+ if (!pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ if (!c->authorized) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ reply = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+
+ if (c->protocol->core->autoload_hashmap) {
+ pa_autoload_entry *a;
+ void *state = NULL;
+
+ while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state, NULL)))
+ autoload_fill_tagstruct(reply, a);
+ }
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+/*** pstream callbacks ***/
+
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) {
+ struct connection *c = userdata;
+ assert(p && packet && packet->data && c);
+
+ if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) {
+ pa_log(__FILE__": invalid packet.\n");
+ connection_free(c);
+ }
+}
+
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata) {
+ struct connection *c = userdata;
+ struct output_stream *stream;
+ assert(p && chunk && userdata);
+
+ if (!(stream = pa_idxset_get_by_index(c->output_streams, channel))) {
+ pa_log(__FILE__": client sent block for invalid stream.\n");
+ connection_free(c);
+ return;
+ }
+
+ if (stream->type == PLAYBACK_STREAM) {
+ struct playback_stream *ps = (struct playback_stream*) stream;
+ if (chunk->length >= ps->requested_bytes)
+ ps->requested_bytes = 0;
+ else
+ ps->requested_bytes -= chunk->length;
+
+ pa_memblockq_push_align(ps->memblockq, chunk, delta);
+ assert(ps->sink_input);
+/* pa_log(__FILE__": after_recv: %u\n", pa_memblockq_get_length(p->memblockq)); */
+
+ pa_sink_notify(ps->sink_input->sink);
+/* pa_log(__FILE__": Recieved %u bytes.\n", chunk->length); */
+
+ } else {
+ struct upload_stream *u = (struct upload_stream*) stream;
+ size_t l;
+ assert(u->type == UPLOAD_STREAM);
+
+ if (!u->memchunk.memblock) {
+ if (u->length == chunk->length) {
+ u->memchunk = *chunk;
+ pa_memblock_ref(u->memchunk.memblock);
+ u->length = 0;
+ } else {
+ u->memchunk.memblock = pa_memblock_new(u->length, c->protocol->core->memblock_stat);
+ u->memchunk.index = u->memchunk.length = 0;
+ }
+ }
+
+ assert(u->memchunk.memblock);
+
+ l = u->length;
+ if (l > chunk->length)
+ l = chunk->length;
+
+ if (l > 0) {
+ memcpy((uint8_t*) u->memchunk.memblock->data + u->memchunk.index + u->memchunk.length,
+ (uint8_t*) chunk->memblock->data+chunk->index, l);
+ u->memchunk.length += l;
+ u->length -= l;
+ }
+ }
+}
+
+static void pstream_die_callback(pa_pstream *p, void *userdata) {
+ struct connection *c = userdata;
+ assert(p && c);
+ connection_free(c);
+
+/* pa_log(__FILE__": connection died.\n");*/
+}
+
+
+static void pstream_drain_callback(pa_pstream *p, void *userdata) {
+ struct connection *c = userdata;
+ assert(p && c);
+
+ send_memblock(c);
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(pa_client *c) {
+ assert(c && c->userdata);
+ connection_free(c->userdata);
+}
+
+/*** socket server callbacks ***/
+
+static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ struct connection *c = userdata;
+ assert(m && tv && c && c->auth_timeout_event == e);
+
+ if (!c->authorized)
+ connection_free(c);
+}
+
+static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, void *userdata) {
+ pa_protocol_native *p = userdata;
+ struct connection *c;
+ assert(io && p);
+
+ if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+ pa_log_warn(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
+ pa_iochannel_free(io);
+ return;
+ }
+
+ c = pa_xmalloc(sizeof(struct connection));
+
+ c->authorized =!! p->public;
+
+ if (!c->authorized) {
+ struct timeval tv;
+ pa_gettimeofday(&tv);
+ tv.tv_sec += AUTH_TIMEOUT;
+ c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
+ } else
+ c->auth_timeout_event = NULL;
+
+ c->protocol = p;
+ assert(p->core);
+ c->client = pa_client_new(p->core, __FILE__, "Client");
+ assert(c->client);
+ c->client->kill = client_kill_cb;
+ c->client->userdata = c;
+ c->client->owner = p->module;
+
+ c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->memblock_stat);
+ assert(c->pstream);
+
+ pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
+ pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);
+ pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
+ pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c);
+
+ c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX);
+ assert(c->pdispatch);
+
+ c->record_streams = pa_idxset_new(NULL, NULL);
+ c->output_streams = pa_idxset_new(NULL, NULL);
+ assert(c->record_streams && c->output_streams);
+
+ c->rrobin_index = PA_IDXSET_INVALID;
+ c->subscription = NULL;
+
+ pa_idxset_put(p->connections, c, NULL);
+}
+
+/*** module entry points ***/
+
+static int load_key(pa_protocol_native*p, const char*fn) {
+ assert(p);
+
+ p->auth_cookie_in_property = 0;
+
+ if (!fn && pa_authkey_prop_get(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) {
+ pa_log_info(__FILE__": using already loaded auth cookie.\n");
+ pa_authkey_prop_ref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+ p->auth_cookie_in_property = 1;
+ return 0;
+ }
+
+ if (!fn)
+ fn = PA_NATIVE_COOKIE_FILE;
+
+ if (pa_authkey_load_auto(fn, p->auth_cookie, sizeof(p->auth_cookie)) < 0)
+ return -1;
+
+ pa_log_info(__FILE__": loading cookie from disk.\n");
+
+ if (pa_authkey_prop_put(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0)
+ p->auth_cookie_in_property = 1;
+
+ return 0;
+}
+
+static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_modargs *ma) {
+ pa_protocol_native *p;
+ int public = 0;
+ assert(c && ma);
+
+ if (pa_modargs_get_value_boolean(ma, "public", &public) < 0) {
+ pa_log(__FILE__": public= expects a boolean argument.\n");
+ return NULL;
+ }
+
+ p = pa_xmalloc(sizeof(pa_protocol_native));
+ p->core = c;
+ p->module = m;
+ p->public = public;
+ p->server = NULL;
+
+ if (load_key(p, pa_modargs_get_value(ma, "cookie", NULL)) < 0) {
+ pa_xfree(p);
+ return NULL;
+ }
+
+ p->connections = pa_idxset_new(NULL, NULL);
+ assert(p->connections);
+
+ return p;
+}
+
+pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
+ char t[256];
+ pa_protocol_native *p;
+
+ if (!(p = protocol_new_internal(core, m, ma)))
+ return NULL;
+
+ p->server = server;
+ pa_socket_server_set_callback(p->server, on_connection, p);
+
+ if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
+ pa_strlist *l;
+ l = pa_property_get(core, PA_NATIVE_SERVER_PROPERTY_NAME);
+ l = pa_strlist_prepend(l, t);
+ pa_property_replace(core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
+ }
+
+ return p;
+}
+
+void pa_protocol_native_free(pa_protocol_native *p) {
+ struct connection *c;
+ assert(p);
+
+ while ((c = pa_idxset_first(p->connections, NULL)))
+ connection_free(c);
+ pa_idxset_free(p->connections, NULL, NULL);
+
+ if (p->server) {
+ char t[256];
+
+ if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
+ pa_strlist *l;
+ l = pa_property_get(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
+ l = pa_strlist_remove(l, t);
+
+ if (l)
+ pa_property_replace(p->core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
+ else
+ pa_property_remove(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
+ }
+
+ pa_socket_server_unref(p->server);
+ }
+
+ if (p->auth_cookie_in_property)
+ pa_authkey_prop_unref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+
+ pa_xfree(p);
+}
+
+pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma) {
+ pa_protocol_native *p;
+
+ if (!(p = protocol_new_internal(core, m, ma)))
+ return NULL;
+
+ on_connection(NULL, io, p);
+
+ return p;
+}
diff --git a/src/polypcore/protocol-native.h b/src/polypcore/protocol-native.h
new file mode 100644
index 00000000..12e85d0b
--- /dev/null
+++ b/src/polypcore/protocol-native.h
@@ -0,0 +1,37 @@
+#ifndef fooprotocolnativehfoo
+#define fooprotocolnativehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "core.h"
+#include "socket-server.h"
+#include "module.h"
+#include "modargs.h"
+
+typedef struct pa_protocol_native pa_protocol_native;
+
+pa_protocol_native* pa_protocol_native_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
+void pa_protocol_native_free(pa_protocol_native *n);
+
+pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma);
+
+#endif
diff --git a/src/polypcore/protocol-simple.c b/src/polypcore/protocol-simple.c
new file mode 100644
index 00000000..113919b3
--- /dev/null
+++ b/src/polypcore/protocol-simple.c
@@ -0,0 +1,453 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "sink-input.h"
+#include "source-output.h"
+#include "protocol-simple.h"
+#include "client.h"
+#include "sample-util.h"
+#include "namereg.h"
+#include "xmalloc.h"
+#include "log.h"
+
+/* Don't allow more than this many concurrent connections */
+#define MAX_CONNECTIONS 10
+
+struct connection {
+ pa_protocol_simple *protocol;
+ pa_iochannel *io;
+ pa_sink_input *sink_input;
+ pa_source_output *source_output;
+ pa_client *client;
+ pa_memblockq *input_memblockq, *output_memblockq;
+ pa_defer_event *defer_event;
+
+ struct {
+ pa_memblock *current_memblock;
+ size_t memblock_index, fragment_size;
+ } playback;
+};
+
+struct pa_protocol_simple {
+ pa_module *module;
+ pa_core *core;
+ pa_socket_server*server;
+ pa_idxset *connections;
+ enum {
+ RECORD = 1,
+ PLAYBACK = 2,
+ DUPLEX = 3
+ } mode;
+ pa_sample_spec sample_spec;
+ char *source_name, *sink_name;
+};
+
+#define PLAYBACK_BUFFER_SECONDS (.5)
+#define PLAYBACK_BUFFER_FRAGMENTS (10)
+#define RECORD_BUFFER_SECONDS (5)
+#define RECORD_BUFFER_FRAGMENTS (100)
+
+static void connection_free(struct connection *c) {
+ assert(c);
+
+ pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+
+ if (c->playback.current_memblock)
+ pa_memblock_unref(c->playback.current_memblock);
+ if (c->sink_input) {
+ pa_sink_input_disconnect(c->sink_input);
+ pa_sink_input_unref(c->sink_input);
+ }
+ if (c->source_output) {
+ pa_source_output_disconnect(c->source_output);
+ pa_source_output_unref(c->source_output);
+ }
+ if (c->client)
+ pa_client_free(c->client);
+ if (c->io)
+ pa_iochannel_free(c->io);
+ if (c->input_memblockq)
+ pa_memblockq_free(c->input_memblockq);
+ if (c->output_memblockq)
+ pa_memblockq_free(c->output_memblockq);
+ if (c->defer_event)
+ c->protocol->core->mainloop->defer_free(c->defer_event);
+ pa_xfree(c);
+}
+
+static int do_read(struct connection *c) {
+ pa_memchunk chunk;
+ ssize_t r;
+ size_t l;
+
+ if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq)))
+ return 0;
+
+ if (l > c->playback.fragment_size)
+ l = c->playback.fragment_size;
+
+ if (c->playback.current_memblock)
+ if (c->playback.current_memblock->length - c->playback.memblock_index < l) {
+ pa_memblock_unref(c->playback.current_memblock);
+ c->playback.current_memblock = NULL;
+ c->playback.memblock_index = 0;
+ }
+
+ if (!c->playback.current_memblock) {
+ c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2, c->protocol->core->memblock_stat);
+ assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
+ c->playback.memblock_index = 0;
+ }
+
+ if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
+ pa_log(__FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
+ return -1;
+ }
+
+ chunk.memblock = c->playback.current_memblock;
+ chunk.index = c->playback.memblock_index;
+ chunk.length = r;
+ assert(chunk.memblock);
+
+ c->playback.memblock_index += r;
+
+ assert(c->input_memblockq);
+ pa_memblockq_push_align(c->input_memblockq, &chunk, 0);
+ assert(c->sink_input);
+ pa_sink_notify(c->sink_input->sink);
+
+ return 0;
+}
+
+static int do_write(struct connection *c) {
+ pa_memchunk chunk;
+ ssize_t r;
+
+ if (!c->source_output)
+ return 0;
+
+ assert(c->output_memblockq);
+ if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
+ return 0;
+
+ assert(chunk.memblock && chunk.length);
+
+ if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) {
+ pa_memblock_unref(chunk.memblock);
+ pa_log(__FILE__": write(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ pa_memblockq_drop(c->output_memblockq, &chunk, r);
+ pa_memblock_unref(chunk.memblock);
+
+ return 0;
+}
+
+
+static void do_work(struct connection *c) {
+ assert(c);
+
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+ c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
+
+ if (pa_iochannel_is_writable(c->io))
+ if (do_write(c) < 0)
+ goto fail;
+
+ if (pa_iochannel_is_readable(c->io))
+ if (do_read(c) < 0)
+ goto fail;
+
+ if (pa_iochannel_is_hungup(c->io))
+ c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+
+ return;
+
+fail:
+ connection_free(c);
+}
+
+/*** sink_input callbacks ***/
+
+static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
+ struct connection*c;
+ assert(i && i->userdata && chunk);
+ c = i->userdata;
+
+ if (pa_memblockq_peek(c->input_memblockq, chunk) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
+ struct connection*c = i->userdata;
+ assert(i && c && length);
+
+ pa_memblockq_drop(c->input_memblockq, chunk, length);
+
+ /* do something */
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+ c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+ assert(i && i->userdata);
+ connection_free((struct connection *) i->userdata);
+}
+
+
+static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
+ struct connection*c = i->userdata;
+ assert(i && c);
+ return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+}
+
+/*** source_output callbacks ***/
+
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+ struct connection *c = o->userdata;
+ assert(o && c && chunk);
+
+ pa_memblockq_push(c->output_memblockq, chunk, 0);
+
+ /* do something */
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+ c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+}
+
+static void source_output_kill_cb(pa_source_output *o) {
+ assert(o && o->userdata);
+ connection_free((struct connection *) o->userdata);
+}
+
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+ struct connection*c = o->userdata;
+ assert(o && c);
+ return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(pa_client *c) {
+ assert(c && c->userdata);
+ connection_free((struct connection *) c->userdata);
+}
+
+/*** pa_iochannel callbacks ***/
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+ struct connection *c = userdata;
+ assert(io && c && c->io == io);
+
+ do_work(c);
+}
+
+/*** fixed callback ***/
+
+static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
+ struct connection *c = userdata;
+ assert(a && c && c->defer_event == e);
+
+ do_work(c);
+}
+
+/*** socket_server callbacks ***/
+
+static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+ pa_protocol_simple *p = userdata;
+ struct connection *c = NULL;
+ char cname[256];
+ assert(s && io && p);
+
+ if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+ pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
+ pa_iochannel_free(io);
+ return;
+ }
+
+ c = pa_xmalloc(sizeof(struct connection));
+ c->io = io;
+ c->sink_input = NULL;
+ c->source_output = NULL;
+ c->defer_event = NULL;
+ c->input_memblockq = c->output_memblockq = NULL;
+ c->protocol = p;
+ c->playback.current_memblock = NULL;
+ c->playback.memblock_index = 0;
+ c->playback.fragment_size = 0;
+
+ pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
+ c->client = pa_client_new(p->core, __FILE__, cname);
+ assert(c->client);
+ c->client->owner = p->module;
+ c->client->kill = client_kill_cb;
+ c->client->userdata = c;
+
+ if (p->mode & PLAYBACK) {
+ pa_sink *sink;
+ size_t l;
+
+ if (!(sink = pa_namereg_get(p->core, p->sink_name, PA_NAMEREG_SINK, 1))) {
+ pa_log(__FILE__": Failed to get sink.\n");
+ goto fail;
+ }
+
+ if (!(c->sink_input = pa_sink_input_new(sink, __FILE__, c->client->name, &p->sample_spec, NULL, 0, -1))) {
+ pa_log(__FILE__": Failed to create sink input.\n");
+ goto fail;
+ }
+
+ c->sink_input->owner = p->module;
+ c->sink_input->client = c->client;
+
+ c->sink_input->peek = sink_input_peek_cb;
+ c->sink_input->drop = sink_input_drop_cb;
+ c->sink_input->kill = sink_input_kill_cb;
+ c->sink_input->get_latency = sink_input_get_latency_cb;
+ c->sink_input->userdata = c;
+
+ l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
+ c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS, p->core->memblock_stat);
+ assert(c->input_memblockq);
+ pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
+ c->playback.fragment_size = l/10;
+ }
+
+ if (p->mode & RECORD) {
+ pa_source *source;
+ size_t l;
+
+ if (!(source = pa_namereg_get(p->core, p->source_name, PA_NAMEREG_SOURCE, 1))) {
+ pa_log(__FILE__": Failed to get source.\n");
+ goto fail;
+ }
+
+ c->source_output = pa_source_output_new(source, __FILE__, c->client->name, &p->sample_spec, NULL, -1);
+ if (!c->source_output) {
+ pa_log(__FILE__": Failed to create source output.\n");
+ goto fail;
+ }
+ c->source_output->owner = p->module;
+ c->source_output->client = c->client;
+
+ c->source_output->push = source_output_push_cb;
+ c->source_output->kill = source_output_kill_cb;
+ c->source_output->get_latency = source_output_get_latency_cb;
+ c->source_output->userdata = c;
+
+ l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
+ c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), 0, 0, p->core->memblock_stat);
+ pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
+ }
+
+ pa_iochannel_set_callback(c->io, io_callback, c);
+ pa_idxset_put(p->connections, c, NULL);
+
+ c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
+ assert(c->defer_event);
+ p->core->mainloop->defer_enable(c->defer_event, 0);
+
+ return;
+
+fail:
+ if (c)
+ connection_free(c);
+}
+
+pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
+ pa_protocol_simple* p = NULL;
+ int enable;
+ assert(core && server && ma);
+
+ p = pa_xmalloc0(sizeof(pa_protocol_simple));
+ p->module = m;
+ p->core = core;
+ p->server = server;
+ p->connections = pa_idxset_new(NULL, NULL);
+
+ p->sample_spec = core->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) {
+ pa_log(__FILE__": Failed to parse sample type specification.\n");
+ goto fail;
+ }
+
+ p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
+ p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+
+ enable = 0;
+ if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) {
+ pa_log(__FILE__": record= expects a numeric argument.\n");
+ goto fail;
+ }
+ p->mode = enable ? RECORD : 0;
+
+ enable = 1;
+ if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
+ pa_log(__FILE__": playback= expects a numeric argument.\n");
+ goto fail;
+ }
+ p->mode |= enable ? PLAYBACK : 0;
+
+ if ((p->mode & (RECORD|PLAYBACK)) == 0) {
+ pa_log(__FILE__": neither playback nor recording enabled for protocol.\n");
+ goto fail;
+ }
+
+ pa_socket_server_set_callback(p->server, on_connection, p);
+
+ return p;
+
+fail:
+ if (p)
+ pa_protocol_simple_free(p);
+ return NULL;
+}
+
+
+void pa_protocol_simple_free(pa_protocol_simple *p) {
+ struct connection *c;
+ assert(p);
+
+ if (p->connections) {
+ while((c = pa_idxset_first(p->connections, NULL)))
+ connection_free(c);
+
+ pa_idxset_free(p->connections, NULL, NULL);
+ }
+
+ if (p->server)
+ pa_socket_server_unref(p->server);
+ pa_xfree(p);
+}
+
diff --git a/src/polypcore/protocol-simple.h b/src/polypcore/protocol-simple.h
new file mode 100644
index 00000000..63455a53
--- /dev/null
+++ b/src/polypcore/protocol-simple.h
@@ -0,0 +1,35 @@
+#ifndef fooprotocolsimplehfoo
+#define fooprotocolsimplehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "socket-server.h"
+#include "module.h"
+#include "core.h"
+#include "modargs.h"
+
+typedef struct pa_protocol_simple pa_protocol_simple;
+
+pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
+void pa_protocol_simple_free(pa_protocol_simple *n);
+
+#endif
diff --git a/src/polypcore/pstream-util.c b/src/polypcore/pstream-util.c
new file mode 100644
index 00000000..ecd63d15
--- /dev/null
+++ b/src/polypcore/pstream-util.c
@@ -0,0 +1,61 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <assert.h>
+
+#include "native-common.h"
+#include "pstream-util.h"
+
+void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t) {
+ size_t length;
+ uint8_t *data;
+ pa_packet *packet;
+ assert(p);
+ assert(t);
+
+ data = pa_tagstruct_free_data(t, &length);
+ assert(data && length);
+ packet = pa_packet_new_dynamic(data, length);
+ assert(packet);
+ pa_pstream_send_packet(p, packet);
+ pa_packet_unref(packet);
+}
+
+void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
+ pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_ERROR);
+ pa_tagstruct_putu32(t, tag);
+ pa_tagstruct_putu32(t, error);
+ pa_pstream_send_tagstruct(p, t);
+}
+
+void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) {
+ pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(t, tag);
+ pa_pstream_send_tagstruct(p, t);
+}
diff --git a/src/polypcore/pstream-util.h b/src/polypcore/pstream-util.h
new file mode 100644
index 00000000..601a9e99
--- /dev/null
+++ b/src/polypcore/pstream-util.h
@@ -0,0 +1,35 @@
+#ifndef foopstreamutilhfoo
+#define foopstreamutilhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include "pstream.h"
+#include "tagstruct.h"
+
+/* The tagstruct is freed!*/
+void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t);
+
+void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error);
+void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag);
+
+#endif
diff --git a/src/polypcore/pstream.c b/src/polypcore/pstream.c
new file mode 100644
index 00000000..bd00ba4f
--- /dev/null
+++ b/src/polypcore/pstream.c
@@ -0,0 +1,492 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include "winsock.h"
+
+#include "pstream.h"
+#include "queue.h"
+#include "xmalloc.h"
+#include "log.h"
+
+typedef enum pa_pstream_descriptor_index {
+ PA_PSTREAM_DESCRIPTOR_LENGTH,
+ PA_PSTREAM_DESCRIPTOR_CHANNEL,
+ PA_PSTREAM_DESCRIPTOR_DELTA,
+ PA_PSTREAM_DESCRIPTOR_MAX
+} pa_pstream_descriptor_index;
+
+typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX];
+
+#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t))
+#define FRAME_SIZE_MAX (1024*500) /* half a megabyte */
+
+struct item_info {
+ enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type;
+
+ /* memblock info */
+ pa_memchunk chunk;
+ uint32_t channel;
+ uint32_t delta;
+
+ /* packet info */
+ pa_packet *packet;
+};
+
+struct pa_pstream {
+ int ref;
+
+ pa_mainloop_api *mainloop;
+ pa_defer_event *defer_event;
+ pa_iochannel *io;
+ pa_queue *send_queue;
+
+ int dead;
+ void (*die_callback) (pa_pstream *p, void *userdata);
+ void *die_callback_userdata;
+
+ struct {
+ struct item_info* current;
+ pa_pstream_descriptor descriptor;
+ void *data;
+ size_t index;
+ } write;
+
+ struct {
+ pa_memblock *memblock;
+ pa_packet *packet;
+ pa_pstream_descriptor descriptor;
+ void *data;
+ size_t index;
+ } read;
+
+ void (*recieve_packet_callback) (pa_pstream *p, pa_packet *packet, void *userdata);
+ void *recieve_packet_callback_userdata;
+
+ void (*recieve_memblock_callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata);
+ void *recieve_memblock_callback_userdata;
+
+ void (*drain_callback)(pa_pstream *p, void *userdata);
+ void *drain_userdata;
+
+ pa_memblock_stat *memblock_stat;
+};
+
+static void do_write(pa_pstream *p);
+static void do_read(pa_pstream *p);
+
+static void do_something(pa_pstream *p) {
+ assert(p);
+
+ p->mainloop->defer_enable(p->defer_event, 0);
+
+ pa_pstream_ref(p);
+
+ if (!p->dead && pa_iochannel_is_readable(p->io))
+ do_read(p);
+
+ if (!p->dead && pa_iochannel_is_writable(p->io))
+ do_write(p);
+
+ /* In case the line was hungup, make sure to rerun this function
+ as soon as possible, until all data has been read. */
+
+ if (!p->dead && pa_iochannel_is_hungup(p->io))
+ p->mainloop->defer_enable(p->defer_event, 1);
+
+ pa_pstream_unref(p);
+}
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+ pa_pstream *p = userdata;
+ assert(p && p->io == io);
+ do_something(p);
+}
+
+static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) {
+ pa_pstream *p = userdata;
+ assert(p && p->defer_event == e && p->mainloop == m);
+ do_something(p);
+}
+
+pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s) {
+ pa_pstream *p;
+ assert(io);
+
+ p = pa_xmalloc(sizeof(pa_pstream));
+ p->ref = 1;
+ p->io = io;
+ pa_iochannel_set_callback(io, io_callback, p);
+
+ p->dead = 0;
+ p->die_callback = NULL;
+ p->die_callback_userdata = NULL;
+
+ p->mainloop = m;
+ p->defer_event = m->defer_new(m, defer_callback, p);
+ m->defer_enable(p->defer_event, 0);
+
+ p->send_queue = pa_queue_new();
+ assert(p->send_queue);
+
+ p->write.current = NULL;
+ p->write.index = 0;
+
+ p->read.memblock = NULL;
+ p->read.packet = NULL;
+ p->read.index = 0;
+
+ p->recieve_packet_callback = NULL;
+ p->recieve_packet_callback_userdata = NULL;
+
+ p->recieve_memblock_callback = NULL;
+ p->recieve_memblock_callback_userdata = NULL;
+
+ p->drain_callback = NULL;
+ p->drain_userdata = NULL;
+
+ p->memblock_stat = s;
+
+ pa_iochannel_socket_set_rcvbuf(io, 1024*8);
+ pa_iochannel_socket_set_sndbuf(io, 1024*8);
+
+ return p;
+}
+
+static void item_free(void *item, PA_GCC_UNUSED void *p) {
+ struct item_info *i = item;
+ assert(i);
+
+ if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) {
+ assert(i->chunk.memblock);
+ pa_memblock_unref(i->chunk.memblock);
+ } else {
+ assert(i->type == PA_PSTREAM_ITEM_PACKET);
+ assert(i->packet);
+ pa_packet_unref(i->packet);
+ }
+
+ pa_xfree(i);
+}
+
+static void pstream_free(pa_pstream *p) {
+ assert(p);
+
+ pa_pstream_close(p);
+
+ pa_queue_free(p->send_queue, item_free, NULL);
+
+ if (p->write.current)
+ item_free(p->write.current, NULL);
+
+ if (p->read.memblock)
+ pa_memblock_unref(p->read.memblock);
+
+ if (p->read.packet)
+ pa_packet_unref(p->read.packet);
+
+ pa_xfree(p);
+}
+
+void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet) {
+ struct item_info *i;
+ assert(p && packet && p->ref >= 1);
+
+ if (p->dead)
+ return;
+
+/* pa_log(__FILE__": push-packet %p\n", packet); */
+
+ i = pa_xmalloc(sizeof(struct item_info));
+ i->type = PA_PSTREAM_ITEM_PACKET;
+ i->packet = pa_packet_ref(packet);
+
+ pa_queue_push(p->send_queue, i);
+ p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk) {
+ struct item_info *i;
+ assert(p && channel != (uint32_t) -1 && chunk && p->ref >= 1);
+
+ if (p->dead)
+ return;
+
+/* pa_log(__FILE__": push-memblock %p\n", chunk); */
+
+ i = pa_xmalloc(sizeof(struct item_info));
+ i->type = PA_PSTREAM_ITEM_MEMBLOCK;
+ i->chunk = *chunk;
+ i->channel = channel;
+ i->delta = delta;
+
+ pa_memblock_ref(i->chunk.memblock);
+
+ pa_queue_push(p->send_queue, i);
+ p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata) {
+ assert(p && callback);
+
+ p->recieve_packet_callback = callback;
+ p->recieve_packet_callback_userdata = userdata;
+}
+
+void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata), void *userdata) {
+ assert(p && callback);
+
+ p->recieve_memblock_callback = callback;
+ p->recieve_memblock_callback_userdata = userdata;
+}
+
+static void prepare_next_write_item(pa_pstream *p) {
+ assert(p);
+
+ if (!(p->write.current = pa_queue_pop(p->send_queue)))
+ return;
+
+ p->write.index = 0;
+
+ if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) {
+ /*pa_log(__FILE__": pop-packet %p\n", p->write.current->packet);*/
+
+ assert(p->write.current->packet);
+ p->write.data = p->write.current->packet->data;
+ p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length);
+ p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1);
+ p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = 0;
+ } else {
+ assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock);
+ p->write.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index;
+ p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length);
+ p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel);
+ p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = htonl(p->write.current->delta);
+ }
+}
+
+static void do_write(pa_pstream *p) {
+ void *d;
+ size_t l;
+ ssize_t r;
+ assert(p);
+
+ if (!p->write.current)
+ prepare_next_write_item(p);
+
+ if (!p->write.current)
+ return;
+
+ assert(p->write.data);
+
+ if (p->write.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
+ d = (uint8_t*) p->write.descriptor + p->write.index;
+ l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;
+ } else {
+ d = (uint8_t*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
+ l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
+ }
+
+ if ((r = pa_iochannel_write(p->io, d, l)) < 0)
+ goto die;
+
+ p->write.index += r;
+
+ if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE+ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {
+ assert(p->write.current);
+ item_free(p->write.current, (void *) 1);
+ p->write.current = NULL;
+
+ if (p->drain_callback && !pa_pstream_is_pending(p))
+ p->drain_callback(p, p->drain_userdata);
+ }
+
+ return;
+
+die:
+ p->dead = 1;
+ if (p->die_callback)
+ p->die_callback(p, p->die_callback_userdata);
+}
+
+static void do_read(pa_pstream *p) {
+ void *d;
+ size_t l;
+ ssize_t r;
+ assert(p);
+
+ if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
+ d = (uint8_t*) p->read.descriptor + p->read.index;
+ l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index;
+ } else {
+ assert(p->read.data);
+ d = (uint8_t*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE;
+ l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE);
+ }
+
+ if ((r = pa_iochannel_read(p->io, d, l)) <= 0)
+ goto die;
+
+ p->read.index += r;
+
+ if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) {
+ /* Reading of frame descriptor complete */
+
+ /* Frame size too large */
+ if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) {
+ pa_log(__FILE__": Frame size too large\n");
+ goto die;
+ }
+
+ assert(!p->read.packet && !p->read.memblock);
+
+ if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]) == (uint32_t) -1) {
+ /* Frame is a packet frame */
+ p->read.packet = pa_packet_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]));
+ assert(p->read.packet);
+ p->read.data = p->read.packet->data;
+ } else {
+ /* Frame is a memblock frame */
+ p->read.memblock = pa_memblock_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]), p->memblock_stat);
+ assert(p->read.memblock);
+ p->read.data = p->read.memblock->data;
+ }
+
+ } else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) {
+ /* Frame payload available */
+
+ if (p->read.memblock && p->recieve_memblock_callback) { /* Is this memblock data? Than pass it to the user */
+ l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r;
+
+ if (l > 0) {
+ pa_memchunk chunk;
+
+ chunk.memblock = p->read.memblock;
+ chunk.index = p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE - l;
+ chunk.length = l;
+
+ if (p->recieve_memblock_callback)
+ p->recieve_memblock_callback(
+ p,
+ ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]),
+ ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA]),
+ &chunk,
+ p->recieve_memblock_callback_userdata);
+ }
+ }
+
+ /* Frame complete */
+ if (p->read.index >= ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) {
+ if (p->read.memblock) {
+ assert(!p->read.packet);
+
+ pa_memblock_unref(p->read.memblock);
+ p->read.memblock = NULL;
+ } else {
+ assert(p->read.packet);
+
+ if (p->recieve_packet_callback)
+ p->recieve_packet_callback(p, p->read.packet, p->recieve_packet_callback_userdata);
+
+ pa_packet_unref(p->read.packet);
+ p->read.packet = NULL;
+ }
+
+ p->read.index = 0;
+ }
+ }
+
+ return;
+
+die:
+ p->dead = 1;
+ if (p->die_callback)
+ p->die_callback(p, p->die_callback_userdata);
+
+}
+
+void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata) {
+ assert(p && callback);
+ p->die_callback = callback;
+ p->die_callback_userdata = userdata;
+}
+
+int pa_pstream_is_pending(pa_pstream *p) {
+ assert(p);
+
+ if (p->dead)
+ return 0;
+
+ return p->write.current || !pa_queue_is_empty(p->send_queue);
+}
+
+void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata) {
+ assert(p);
+
+ p->drain_callback = cb;
+ p->drain_userdata = userdata;
+}
+
+void pa_pstream_unref(pa_pstream*p) {
+ assert(p && p->ref >= 1);
+
+ if (!(--(p->ref)))
+ pstream_free(p);
+}
+
+pa_pstream* pa_pstream_ref(pa_pstream*p) {
+ assert(p && p->ref >= 1);
+ p->ref++;
+ return p;
+}
+
+void pa_pstream_close(pa_pstream *p) {
+ assert(p);
+
+ p->dead = 1;
+
+ if (p->io) {
+ pa_iochannel_free(p->io);
+ p->io = NULL;
+ }
+
+ if (p->defer_event) {
+ p->mainloop->defer_free(p->defer_event);
+ p->defer_event = NULL;
+ }
+
+ p->die_callback = NULL;
+ p->drain_callback = NULL;
+ p->recieve_packet_callback = NULL;
+ p->recieve_memblock_callback = NULL;
+}
diff --git a/src/polypcore/pstream.h b/src/polypcore/pstream.h
new file mode 100644
index 00000000..ec63a98e
--- /dev/null
+++ b/src/polypcore/pstream.h
@@ -0,0 +1,52 @@
+#ifndef foopstreamhfoo
+#define foopstreamhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+#include "packet.h"
+#include "memblock.h"
+#include "iochannel.h"
+#include <polyp/mainloop-api.h>
+#include "memchunk.h"
+
+typedef struct pa_pstream pa_pstream;
+
+pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s);
+void pa_pstream_unref(pa_pstream*p);
+pa_pstream* pa_pstream_ref(pa_pstream*p);
+
+void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet);
+void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk);
+
+void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata);
+void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata), void *userdata);
+void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata);
+
+void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata);
+
+int pa_pstream_is_pending(pa_pstream *p);
+
+void pa_pstream_close(pa_pstream *p);
+
+#endif
diff --git a/src/polypcore/queue.c b/src/polypcore/queue.c
new file mode 100644
index 00000000..80ec0068
--- /dev/null
+++ b/src/polypcore/queue.c
@@ -0,0 +1,108 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <assert.h>
+#include <stdlib.h>
+
+#include "queue.h"
+#include "xmalloc.h"
+
+struct queue_entry {
+ struct queue_entry *next;
+ void *data;
+};
+
+struct pa_queue {
+ struct queue_entry *front, *back;
+ unsigned length;
+};
+
+pa_queue* pa_queue_new(void) {
+ pa_queue *q = pa_xnew(pa_queue, 1);
+ q->front = q->back = NULL;
+ q->length = 0;
+ return q;
+}
+
+void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) {
+ struct queue_entry *e;
+ assert(q);
+
+ e = q->front;
+ while (e) {
+ struct queue_entry *n = e->next;
+
+ if (destroy)
+ destroy(e->data, userdata);
+
+ pa_xfree(e);
+ e = n;
+ }
+
+ pa_xfree(q);
+}
+
+void pa_queue_push(pa_queue *q, void *p) {
+ struct queue_entry *e;
+
+ e = pa_xnew(struct queue_entry, 1);
+ e->data = p;
+ e->next = NULL;
+
+ if (q->back)
+ q->back->next = e;
+ else {
+ assert(!q->front);
+ q->front = e;
+ }
+
+ q->back = e;
+ q->length++;
+}
+
+void* pa_queue_pop(pa_queue *q) {
+ void *p;
+ struct queue_entry *e;
+ assert(q);
+
+ if (!(e = q->front))
+ return NULL;
+
+ q->front = e->next;
+ if (q->back == e)
+ q->back = NULL;
+
+ p = e->data;
+ pa_xfree(e);
+
+ q->length--;
+
+ return p;
+}
+
+int pa_queue_is_empty(pa_queue *q) {
+ assert(q);
+ return q->length == 0;
+}
diff --git a/src/polypcore/queue.h b/src/polypcore/queue.h
new file mode 100644
index 00000000..3edcfb63
--- /dev/null
+++ b/src/polypcore/queue.h
@@ -0,0 +1,40 @@
+#ifndef fooqueuehfoo
+#define fooqueuehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct pa_queue pa_queue;
+
+/* A simple implementation of the abstract data type queue. Stores
+ * pointers as members. The memory has to be managed by the caller. */
+
+pa_queue* pa_queue_new(void);
+
+/* Free the queue and run the specified callback function for every remaining entry. The callback function may be NULL. */
+void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata);
+
+void pa_queue_push(pa_queue *q, void *p);
+void* pa_queue_pop(pa_queue *q);
+
+int pa_queue_is_empty(pa_queue *q);
+
+#endif
diff --git a/src/polypcore/random.c b/src/polypcore/random.c
new file mode 100644
index 00000000..12f27bfd
--- /dev/null
+++ b/src/polypcore/random.c
@@ -0,0 +1,71 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <time.h>
+
+#include "random.h"
+#include "util.h"
+#include "log.h"
+
+#ifndef OS_IS_WIN32
+#define RANDOM_DEVICE "/dev/urandom"
+#endif
+
+void pa_random(void *ret_data, size_t length) {
+ int fd;
+ ssize_t r = 0;
+ assert(ret_data && length);
+
+#ifdef RANDOM_DEVICE
+ if ((fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) {
+
+ if ((r = pa_loop_read(fd, ret_data, length)) < 0 || (size_t) r != length)
+ pa_log_error(__FILE__": failed to read entropy from '%s'\n", RANDOM_DEVICE);
+
+ close(fd);
+ }
+#endif
+
+ if ((size_t) r != length) {
+ uint8_t *p;
+ size_t l;
+
+#ifdef RANDOM_DEVICE
+ pa_log_warn(__FILE__": WARNING: Failed to open entropy device '"RANDOM_DEVICE"': %s"
+ ", falling back to unsecure pseudo RNG.\n", strerror(errno));
+#endif
+
+ srand(time(NULL));
+
+ for (p = ret_data, l = length; l > 0; p++, l--)
+ *p = (uint8_t) rand();
+ }
+}
diff --git a/src/polypcore/random.h b/src/polypcore/random.h
new file mode 100644
index 00000000..bfb3df08
--- /dev/null
+++ b/src/polypcore/random.h
@@ -0,0 +1,27 @@
+#ifndef foorandomhfoo
+#define foorandomhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+void pa_random(void *ret_data, size_t length);
+
+#endif
diff --git a/src/polypcore/resampler.c b/src/polypcore/resampler.c
new file mode 100644
index 00000000..0417e44e
--- /dev/null
+++ b/src/polypcore/resampler.c
@@ -0,0 +1,614 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <string.h>
+
+#include <samplerate.h>
+#include <liboil/liboilfuncs.h>
+#include <liboil/liboil.h>
+
+#include "resampler.h"
+#include "sconv.h"
+#include "xmalloc.h"
+#include "log.h"
+
+struct pa_resampler {
+ pa_resample_method_t resample_method;
+ pa_sample_spec i_ss, o_ss;
+ pa_channel_map i_cm, o_cm;
+ size_t i_fz, o_fz;
+ pa_memblock_stat *memblock_stat;
+
+ void (*impl_free)(pa_resampler *r);
+ void (*impl_update_input_rate)(pa_resampler *r, uint32_t rate);
+ void (*impl_run)(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);
+ void *impl_data;
+};
+
+struct impl_libsamplerate {
+ float* buf1, *buf2, *buf3, *buf4;
+ unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples;
+
+ pa_convert_to_float32ne_func_t to_float32ne_func;
+ pa_convert_from_float32ne_func_t from_float32ne_func;
+ SRC_STATE *src_state;
+
+ int map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+ int map_required;
+};
+
+struct impl_trivial {
+ unsigned o_counter;
+ unsigned i_counter;
+};
+
+static int libsamplerate_init(pa_resampler*r);
+static int trivial_init(pa_resampler*r);
+
+pa_resampler* pa_resampler_new(
+ const pa_sample_spec *a,
+ const pa_channel_map *am,
+ const pa_sample_spec *b,
+ const pa_channel_map *bm,
+ pa_memblock_stat *s,
+ pa_resample_method_t resample_method) {
+
+ pa_resampler *r = NULL;
+
+ assert(a);
+ assert(b);
+ assert(pa_sample_spec_valid(a));
+ assert(pa_sample_spec_valid(b));
+ assert(resample_method != PA_RESAMPLER_INVALID);
+
+ r = pa_xnew(pa_resampler, 1);
+ r->impl_data = NULL;
+ r->memblock_stat = s;
+ r->resample_method = resample_method;
+
+ r->impl_free = NULL;
+ r->impl_update_input_rate = NULL;
+ r->impl_run = NULL;
+
+ /* Fill sample specs */
+ r->i_ss = *a;
+ r->o_ss = *b;
+
+ if (am)
+ r->i_cm = *am;
+ else
+ pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels);
+
+ if (bm)
+ r->o_cm = *bm;
+ else
+ pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels);
+
+ r->i_fz = pa_frame_size(a);
+ r->o_fz = pa_frame_size(b);
+
+ /* Choose implementation */
+ if (a->channels != b->channels ||
+ a->format != b->format ||
+ !pa_channel_map_equal(&r->i_cm, &r->o_cm) ||
+ resample_method != PA_RESAMPLER_TRIVIAL) {
+
+ /* Use the libsamplerate based resampler for the complicated cases */
+ if (resample_method == PA_RESAMPLER_TRIVIAL)
+ r->resample_method = PA_RESAMPLER_SRC_ZERO_ORDER_HOLD;
+
+ if (libsamplerate_init(r) < 0)
+ goto fail;
+
+ } else {
+ /* Use our own simple non-fp resampler for the trivial cases and when the user selects it */
+ if (trivial_init(r) < 0)
+ goto fail;
+ }
+
+ return r;
+
+fail:
+ if (r)
+ pa_xfree(r);
+
+ return NULL;
+}
+
+void pa_resampler_free(pa_resampler *r) {
+ assert(r);
+
+ if (r->impl_free)
+ r->impl_free(r);
+
+ pa_xfree(r);
+}
+
+void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) {
+ assert(r);
+ assert(rate > 0);
+
+ if (r->i_ss.rate == rate)
+ return;
+
+ r->i_ss.rate = rate;
+
+ if (r->impl_update_input_rate)
+ r->impl_update_input_rate(r, rate);
+}
+
+void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
+ assert(r && in && out && r->impl_run);
+
+ r->impl_run(r, in, out);
+}
+
+size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
+ assert(r && (out_length % r->o_fz) == 0);
+ return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
+}
+
+pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
+ assert(r);
+ return r->resample_method;
+}
+
+static const char * const resample_methods[] = {
+ "src-sinc-best-quality",
+ "src-sinc-medium-quality",
+ "src-sinc-fastest",
+ "src-zero-order-hold",
+ "src-linear",
+ "trivial"
+};
+
+const char *pa_resample_method_to_string(pa_resample_method_t m) {
+
+ if (m < 0 || m >= PA_RESAMPLER_MAX)
+ return NULL;
+
+ return resample_methods[m];
+}
+
+pa_resample_method_t pa_parse_resample_method(const char *string) {
+ pa_resample_method_t m;
+
+ assert(string);
+
+ for (m = 0; m < PA_RESAMPLER_MAX; m++)
+ if (!strcmp(string, resample_methods[m]))
+ return m;
+
+ return PA_RESAMPLER_INVALID;
+}
+
+
+/*** libsamplerate based implementation ***/
+
+static void libsamplerate_free(pa_resampler *r) {
+ struct impl_libsamplerate *u;
+
+ assert(r);
+ assert(r->impl_data);
+
+ u = r->impl_data;
+
+ if (u->src_state)
+ src_delete(u->src_state);
+
+ pa_xfree(u->buf1);
+ pa_xfree(u->buf2);
+ pa_xfree(u->buf3);
+ pa_xfree(u->buf4);
+ pa_xfree(u);
+}
+
+static void calc_map_table(pa_resampler *r) {
+ struct impl_libsamplerate *u;
+ unsigned oc;
+ assert(r);
+ assert(r->impl_data);
+
+ u = r->impl_data;
+
+ if (!(u->map_required = (!pa_channel_map_equal(&r->i_cm, &r->o_cm) || r->i_ss.channels != r->o_ss.channels)))
+ return;
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+ unsigned ic, i = 0;
+
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+ pa_channel_position_t a, b;
+
+ a = r->i_cm.map[ic];
+ b = r->o_cm.map[oc];
+
+ if (a == b ||
+ (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_LEFT) ||
+ (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_RIGHT) ||
+ (a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) ||
+ (a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO))
+
+ u->map_table[oc][i++] = ic;
+ }
+
+ /* Add an end marker */
+ if (i < PA_CHANNELS_MAX)
+ u->map_table[oc][i] = -1;
+ }
+}
+
+static float * convert_to_float(pa_resampler *r, float *input, unsigned n_frames) {
+ struct impl_libsamplerate *u;
+ unsigned n_samples;
+
+ assert(r);
+ assert(input);
+ assert(r->impl_data);
+ u = r->impl_data;
+
+ /* Convert the incoming sample into floats and place them in buf1 */
+
+ if (!u->to_float32ne_func)
+ return input;
+
+ n_samples = n_frames * r->i_ss.channels;
+
+ if (u->buf1_samples < n_samples)
+ u->buf1 = pa_xrealloc(u->buf1, sizeof(float) * (u->buf1_samples = n_samples));
+
+ u->to_float32ne_func(n_samples, input, u->buf1);
+
+ return u->buf1;
+}
+
+static float *remap_channels(pa_resampler *r, float *input, unsigned n_frames) {
+ struct impl_libsamplerate *u;
+ unsigned n_samples;
+ int i_skip, o_skip;
+ unsigned oc;
+
+ assert(r);
+ assert(input);
+ assert(r->impl_data);
+ u = r->impl_data;
+
+ /* Remap channels and place the result int buf2 */
+
+ if (!u->map_required)
+ return input;
+
+ n_samples = n_frames * r->o_ss.channels;
+
+ if (u->buf2_samples < n_samples)
+ u->buf2 = pa_xrealloc(u->buf2, sizeof(float) * (u->buf2_samples = n_samples));
+
+ memset(u->buf2, 0, n_samples * sizeof(float));
+
+ o_skip = sizeof(float) * r->o_ss.channels;
+ i_skip = sizeof(float) * r->i_ss.channels;
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+ unsigned i;
+ static const float one = 1.0;
+
+ for (i = 0; i < PA_CHANNELS_MAX && u->map_table[oc][i] >= 0; i++)
+ oil_vectoradd_f32(
+ u->buf2 + oc, o_skip,
+ u->buf2 + oc, o_skip,
+ input + u->map_table[oc][i], i_skip,
+ n_frames,
+ &one, &one);
+ }
+
+ return u->buf2;
+}
+
+static float *resample(pa_resampler *r, float *input, unsigned *n_frames) {
+ struct impl_libsamplerate *u;
+ SRC_DATA data;
+ unsigned out_n_frames, out_n_samples;
+ int ret;
+
+ assert(r);
+ assert(input);
+ assert(n_frames);
+ assert(r->impl_data);
+ u = r->impl_data;
+
+ /* Resample the data and place the result in buf3 */
+
+ if (!u->src_state)
+ return input;
+
+ out_n_frames = (*n_frames*r->o_ss.rate/r->i_ss.rate)+1024;
+ out_n_samples = out_n_frames * r->o_ss.channels;
+
+ if (u->buf3_samples < out_n_samples)
+ u->buf3 = pa_xrealloc(u->buf3, sizeof(float) * (u->buf3_samples = out_n_samples));
+
+ data.data_in = input;
+ data.input_frames = *n_frames;
+
+ data.data_out = u->buf3;
+ data.output_frames = out_n_frames;
+
+ data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
+ data.end_of_input = 0;
+
+ ret = src_process(u->src_state, &data);
+ assert(ret == 0);
+ assert((unsigned) data.input_frames_used == *n_frames);
+
+ *n_frames = data.output_frames_gen;
+
+ return u->buf3;
+}
+
+static float *convert_from_float(pa_resampler *r, float *input, unsigned n_frames) {
+ struct impl_libsamplerate *u;
+ unsigned n_samples;
+
+ assert(r);
+ assert(input);
+ assert(r->impl_data);
+ u = r->impl_data;
+
+ /* Convert the data into the correct sample type and place the result in buf4 */
+
+ if (!u->from_float32ne_func)
+ return input;
+
+ n_samples = n_frames * r->o_ss.channels;
+
+ if (u->buf4_samples < n_samples)
+ u->buf4 = pa_xrealloc(u->buf4, sizeof(float) * (u->buf4_samples = n_samples));
+
+ u->from_float32ne_func(n_samples, input, u->buf4);
+
+ return u->buf4;
+}
+
+static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
+ struct impl_libsamplerate *u;
+ float *buf, *input;
+ unsigned n_frames;
+
+ assert(r);
+ assert(in);
+ assert(out);
+ assert(in->length);
+ assert(in->memblock);
+ assert(in->length % r->i_fz == 0);
+ assert(r->impl_data);
+
+ u = r->impl_data;
+
+ buf = input = (float*) ((uint8_t*) in->memblock->data + in->index);
+ n_frames = in->length / r->i_fz;
+ assert(n_frames > 0);
+
+ buf = convert_to_float(r, buf, n_frames);
+ buf = remap_channels(r, buf, n_frames);
+ buf = resample(r, buf, &n_frames);
+
+ if (n_frames) {
+ buf = convert_from_float(r, buf, n_frames);
+
+ if (buf == input) {
+ /* Mm, no adjustment has been necessary, so let's return the original block */
+ out->memblock = pa_memblock_ref(in->memblock);
+ out->index = in->index;
+ out->length = in->length;
+ } else {
+ float **p = NULL;
+
+ out->length = n_frames * r->o_fz;
+ out->index = 0;
+
+ if (buf == u->buf1) {
+ p = &u->buf1;
+ u->buf1_samples = 0;
+ } else if (buf == u->buf2) {
+ p = &u->buf2;
+ u->buf2_samples = 0;
+ } else if (buf == u->buf3) {
+ p = &u->buf3;
+ u->buf3_samples = 0;
+ } else if (buf == u->buf4) {
+ p = &u->buf4;
+ u->buf4_samples = 0;
+ }
+
+ assert(p);
+
+ /* Take the existing buffer and make it a memblock */
+ out->memblock = pa_memblock_new_dynamic(*p, out->length, r->memblock_stat);
+ *p = NULL;
+ }
+ } else {
+ out->memblock = NULL;
+ out->index = out->length = 0;
+ }
+}
+
+static void libsamplerate_update_input_rate(pa_resampler *r, uint32_t rate) {
+ struct impl_libsamplerate *u;
+
+ assert(r);
+ assert(rate > 0);
+ assert(r->impl_data);
+ u = r->impl_data;
+
+ if (!u->src_state) {
+ int err;
+ u->src_state = src_new(r->resample_method, r->o_ss.channels, &err);
+ assert(u->src_state);
+ } else {
+ int ret = src_set_ratio(u->src_state, (double) r->o_ss.rate / rate);
+ assert(ret == 0);
+ }
+}
+
+static int libsamplerate_init(pa_resampler *r) {
+ struct impl_libsamplerate *u = NULL;
+ int err;
+
+ r->impl_data = u = pa_xnew(struct impl_libsamplerate, 1);
+
+ u->buf1 = u->buf2 = u->buf3 = u->buf4 = NULL;
+ u->buf1_samples = u->buf2_samples = u->buf3_samples = u->buf4_samples = 0;
+
+ if (r->i_ss.format == PA_SAMPLE_FLOAT32NE)
+ u->to_float32ne_func = NULL;
+ else if (!(u->to_float32ne_func = pa_get_convert_to_float32ne_function(r->i_ss.format)))
+ goto fail;
+
+ if (r->o_ss.format == PA_SAMPLE_FLOAT32NE)
+ u->from_float32ne_func = NULL;
+ else if (!(u->from_float32ne_func = pa_get_convert_from_float32ne_function(r->o_ss.format)))
+ goto fail;
+
+ if (r->o_ss.rate == r->i_ss.rate)
+ u->src_state = NULL;
+ else if (!(u->src_state = src_new(r->resample_method, r->o_ss.channels, &err)))
+ goto fail;
+
+ r->impl_free = libsamplerate_free;
+ r->impl_update_input_rate = libsamplerate_update_input_rate;
+ r->impl_run = libsamplerate_run;
+
+ calc_map_table(r);
+
+ return 0;
+
+fail:
+ pa_xfree(u);
+ return -1;
+}
+
+/* Trivial implementation */
+
+static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
+ size_t fz;
+ unsigned n_frames;
+ struct impl_trivial *u;
+
+ assert(r);
+ assert(in);
+ assert(out);
+ assert(r->impl_data);
+
+ u = r->impl_data;
+
+ fz = r->i_fz;
+ assert(fz == r->o_fz);
+
+ n_frames = in->length/fz;
+
+ if (r->i_ss.rate == r->o_ss.rate) {
+
+ /* In case there's no diefference in sample types, do nothing */
+ *out = *in;
+ pa_memblock_ref(out->memblock);
+
+ u->o_counter += n_frames;
+ } else {
+ /* Do real resampling */
+ size_t l;
+ unsigned o_index;
+
+ /* The length of the new memory block rounded up */
+ l = ((((n_frames+1) * r->o_ss.rate) / r->i_ss.rate) + 1) * fz;
+
+ out->index = 0;
+ out->memblock = pa_memblock_new(l, r->memblock_stat);
+
+ for (o_index = 0;; o_index++, u->o_counter++) {
+ unsigned j;
+
+ j = (u->o_counter * r->i_ss.rate / r->o_ss.rate);
+ j = j > u->i_counter ? j - u->i_counter : 0;
+
+ if (j >= n_frames)
+ break;
+
+ assert(o_index*fz < out->memblock->length);
+
+ memcpy((uint8_t*) out->memblock->data + fz*o_index,
+ (uint8_t*) in->memblock->data + in->index + fz*j, fz);
+
+ }
+
+ out->length = o_index*fz;
+ }
+
+ u->i_counter += n_frames;
+
+ /* Normalize counters */
+ while (u->i_counter >= r->i_ss.rate) {
+ u->i_counter -= r->i_ss.rate;
+ assert(u->o_counter >= r->o_ss.rate);
+ u->o_counter -= r->o_ss.rate;
+ }
+}
+
+static void trivial_free(pa_resampler *r) {
+ assert(r);
+
+ pa_xfree(r->impl_data);
+}
+
+static void trivial_update_input_rate(pa_resampler *r, uint32_t rate) {
+ struct impl_trivial *u;
+
+ assert(r);
+ assert(rate > 0);
+ assert(r->impl_data);
+
+ u = r->impl_data;
+ u->i_counter = 0;
+ u->o_counter = 0;
+}
+
+static int trivial_init(pa_resampler*r) {
+ struct impl_trivial *u;
+
+ assert(r);
+ assert(r->i_ss.format == r->o_ss.format);
+ assert(r->i_ss.channels == r->o_ss.channels);
+
+ r->impl_data = u = pa_xnew(struct impl_trivial, 1);
+ u->o_counter = u->i_counter = 0;
+
+ r->impl_run = trivial_run;
+ r->impl_free = trivial_free;
+ r->impl_update_input_rate = trivial_update_input_rate;
+
+ return 0;
+}
+
+
diff --git a/src/polypcore/resampler.h b/src/polypcore/resampler.h
new file mode 100644
index 00000000..cad48be5
--- /dev/null
+++ b/src/polypcore/resampler.h
@@ -0,0 +1,73 @@
+#ifndef fooresamplerhfoo
+#define fooresamplerhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <samplerate.h>
+
+#include <polyp/sample.h>
+#include "memblock.h"
+#include "memchunk.h"
+#include <polyp/channelmap.h>
+
+typedef struct pa_resampler pa_resampler;
+
+typedef enum pa_resample_method {
+ PA_RESAMPLER_INVALID = -1,
+ PA_RESAMPLER_SRC_SINC_BEST_QUALITY = SRC_SINC_BEST_QUALITY,
+ PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = SRC_SINC_MEDIUM_QUALITY,
+ PA_RESAMPLER_SRC_SINC_FASTEST = SRC_SINC_FASTEST,
+ PA_RESAMPLER_SRC_ZERO_ORDER_HOLD = SRC_ZERO_ORDER_HOLD,
+ PA_RESAMPLER_SRC_LINEAR = SRC_LINEAR,
+ PA_RESAMPLER_TRIVIAL,
+ PA_RESAMPLER_MAX
+} pa_resample_method_t;
+
+pa_resampler* pa_resampler_new(
+ const pa_sample_spec *a,
+ const pa_channel_map *am,
+ const pa_sample_spec *b,
+ const pa_channel_map *bm,
+ pa_memblock_stat *s,
+ pa_resample_method_t resample_method);
+
+void pa_resampler_free(pa_resampler *r);
+
+/* Returns the size of an input memory block which is required to return the specified amount of output data */
+size_t pa_resampler_request(pa_resampler *r, size_t out_length);
+
+/* Pass the specified memory chunk to the resampler and return the newly resampled data */
+void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);
+
+/* Change the input rate of the resampler object */
+void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate);
+
+/* Return the resampling method of the resampler object */
+pa_resample_method_t pa_resampler_get_method(pa_resampler *r);
+
+/* Try to parse the resampler method */
+pa_resample_method_t pa_parse_resample_method(const char *string);
+
+/* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */
+const char *pa_resample_method_to_string(pa_resample_method_t m);
+
+#endif
diff --git a/src/polypcore/sample-util.c b/src/polypcore/sample-util.c
new file mode 100644
index 00000000..52974c46
--- /dev/null
+++ b/src/polypcore/sample-util.c
@@ -0,0 +1,328 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include <liboil/liboilfuncs.h>
+
+#include "log.h"
+#include "sample-util.h"
+
+pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
+ assert(b && b->data && spec);
+ pa_silence_memory(b->data, b->length, spec);
+ return b;
+}
+
+void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
+ assert(c && c->memblock && c->memblock->data && spec && c->length);
+
+ pa_silence_memory((uint8_t*) c->memblock->data+c->index, c->length, spec);
+}
+
+void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
+ uint8_t c = 0;
+ assert(p && length && spec);
+
+ switch (spec->format) {
+ case PA_SAMPLE_U8:
+ c = 0x80;
+ break;
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_FLOAT32:
+ c = 0;
+ break;
+ case PA_SAMPLE_ALAW:
+ case PA_SAMPLE_ULAW:
+ c = 80;
+ break;
+ default:
+ assert(0);
+ }
+
+ memset(p, c, length);
+}
+
+size_t pa_mix(
+ const pa_mix_info streams[],
+ unsigned nstreams,
+ void *data,
+ size_t length,
+ const pa_sample_spec *spec,
+ const pa_cvolume *volume) {
+
+ assert(streams && data && length && spec);
+
+ switch (spec->format) {
+ case PA_SAMPLE_S16NE:{
+ size_t d;
+ unsigned channel = 0;
+
+ for (d = 0;; d += sizeof(int16_t)) {
+ int32_t sum = 0;
+
+ if (d >= length)
+ return d;
+
+ if (volume->values[channel] != PA_VOLUME_MUTED) {
+ unsigned i;
+
+ for (i = 0; i < nstreams; i++) {
+ int32_t v;
+ pa_volume_t cvolume = streams[i].volume.values[channel];
+
+ if (d >= streams[i].chunk.length)
+ return d;
+
+ if (cvolume == PA_VOLUME_MUTED)
+ v = 0;
+ else {
+ v = *((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d));
+
+ if (cvolume != PA_VOLUME_NORM) {
+ v *= cvolume;
+ v /= PA_VOLUME_NORM;
+ }
+ }
+
+ sum += v;
+ }
+
+ if (volume->values[channel] != PA_VOLUME_NORM) {
+ sum *= volume->values[channel];
+ sum /= PA_VOLUME_NORM;
+ }
+
+ if (sum < -0x8000) sum = -0x8000;
+ if (sum > 0x7FFF) sum = 0x7FFF;
+
+ }
+
+ *((int16_t*) data) = sum;
+ data = (uint8_t*) data + sizeof(int16_t);
+
+ if (++channel >= spec->channels)
+ channel = 0;
+ }
+ }
+
+ case PA_SAMPLE_U8: {
+ size_t d;
+ unsigned channel = 0;
+
+ for (d = 0;; d ++) {
+ int32_t sum = 0;
+
+ if (d >= length)
+ return d;
+
+ if (volume->values[channel] != PA_VOLUME_MUTED) {
+ unsigned i;
+
+ for (i = 0; i < nstreams; i++) {
+ int32_t v;
+ pa_volume_t cvolume = streams[i].volume.values[channel];
+
+ if (d >= streams[i].chunk.length)
+ return d;
+
+ if (cvolume == PA_VOLUME_MUTED)
+ v = 0;
+ else {
+ v = (int32_t) *((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d) - 0x80;
+
+ if (cvolume != PA_VOLUME_NORM) {
+ v *= cvolume;
+ v /= PA_VOLUME_NORM;
+ }
+ }
+
+ sum += v;
+ }
+
+ if (volume->values[channel] != PA_VOLUME_NORM) {
+ sum *= volume->values[channel];
+ sum /= PA_VOLUME_NORM;
+ }
+
+ if (sum < -0x80) sum = -0x80;
+ if (sum > 0x7F) sum = 0x7F;
+
+ }
+
+ *((uint8_t*) data) = (uint8_t) (sum + 0x80);
+ data = (uint8_t*) data + 1;
+
+ if (++channel >= spec->channels)
+ channel = 0;
+ }
+ }
+
+ case PA_SAMPLE_FLOAT32NE: {
+ size_t d;
+ unsigned channel = 0;
+
+ for (d = 0;; d += sizeof(float)) {
+ float sum = 0;
+
+ if (d >= length)
+ return d;
+
+ if (volume->values[channel] != PA_VOLUME_MUTED) {
+ unsigned i;
+
+ for (i = 0; i < nstreams; i++) {
+ float v;
+ pa_volume_t cvolume = streams[i].volume.values[channel];
+
+ if (d >= streams[i].chunk.length)
+ return d;
+
+ if (cvolume == PA_VOLUME_MUTED)
+ v = 0;
+ else {
+ v = *((float*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d));
+
+ if (cvolume != PA_VOLUME_NORM) {
+ v *= cvolume;
+ v /= PA_VOLUME_NORM;
+ }
+ }
+
+ sum += v;
+ }
+
+ if (volume->values[channel] != PA_VOLUME_NORM) {
+ sum *= volume->values[channel];
+ sum /= PA_VOLUME_NORM;
+ }
+ }
+
+ *((float*) data) = sum;
+ data = (uint8_t*) data + sizeof(float);
+
+ if (++channel >= spec->channels)
+ channel = 0;
+ }
+ }
+
+ default:
+ abort();
+ }
+}
+
+
+void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvolume *volume) {
+ assert(c && spec && (c->length % pa_frame_size(spec) == 0));
+ assert(volume);
+
+ if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM))
+ return;
+
+ if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_MUTED)) {
+ pa_silence_memchunk(c, spec);
+ return;
+ }
+
+ switch (spec->format) {
+ case PA_SAMPLE_S16NE: {
+ int16_t *d;
+ size_t n;
+ unsigned channel = 0;
+
+ for (d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ int32_t t = (int32_t)(*d);
+
+ t *= volume->values[channel];
+ t /= PA_VOLUME_NORM;
+
+ if (t < -0x8000) t = -0x8000;
+ if (t > 0x7FFF) t = 0x7FFF;
+
+ *d = (int16_t) t;
+
+ if (++channel >= spec->channels)
+ channel = 0;
+ }
+ break;
+ }
+
+ case PA_SAMPLE_U8: {
+ uint8_t *d;
+ size_t n;
+ unsigned channel = 0;
+
+ for (d = (uint8_t*) c->memblock->data + c->index, n = c->length; n > 0; d++, n--) {
+ int32_t t = (int32_t) *d - 0x80;
+
+ t *= volume->values[channel];
+ t /= PA_VOLUME_NORM;
+
+ if (t < -0x80) t = -0x80;
+ if (t > 0x7F) t = 0x7F;
+
+ *d = (uint8_t) (t + 0x80);
+
+ if (++channel >= spec->channels)
+ channel = 0;
+ }
+ break;
+ }
+
+ case PA_SAMPLE_FLOAT32NE: {
+ float *d;
+ int skip;
+ unsigned n;
+ unsigned channel;
+
+ d = (float*) ((uint8_t*) c->memblock->data + c->index);
+ skip = spec->channels * sizeof(float);
+ n = c->length/sizeof(float)/spec->channels;
+
+ for (channel = 0; channel < spec->channels ; channel ++) {
+ float v, *t;
+
+ if (volume->values[channel] == PA_VOLUME_NORM)
+ continue;
+
+ v = (float) volume->values[channel] / PA_VOLUME_NORM;
+
+ t = d + channel;
+ oil_scalarmult_f32(t, skip, t, skip, &v, n);
+ }
+ break;
+ }
+
+ default:
+ pa_log_error(__FILE__": ERROR: Unable to change volume of format %s.\n",
+ pa_sample_format_to_string(spec->format));
+ abort();
+ }
+}
+
diff --git a/src/polypcore/sample-util.h b/src/polypcore/sample-util.h
new file mode 100644
index 00000000..d0efbcc4
--- /dev/null
+++ b/src/polypcore/sample-util.h
@@ -0,0 +1,53 @@
+#ifndef foosampleutilhfoo
+#define foosampleutilhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/sample.h>
+#include "memblock.h"
+#include "memchunk.h"
+#include <polyp/volume.h>
+
+pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec);
+void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
+void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
+
+typedef struct pa_mix_info {
+ pa_memchunk chunk;
+ pa_cvolume volume;
+ void *userdata;
+} pa_mix_info;
+
+size_t pa_mix(
+ const pa_mix_info channels[],
+ unsigned nchannels,
+ void *data,
+ size_t length,
+ const pa_sample_spec *spec,
+ const pa_cvolume *volume);
+
+void pa_volume_memchunk(
+ pa_memchunk*c,
+ const pa_sample_spec *spec,
+ const pa_cvolume *volume);
+
+#endif
diff --git a/src/polypcore/scache.c b/src/polypcore/scache.c
new file mode 100644
index 00000000..02c7d34f
--- /dev/null
+++ b/src/polypcore/scache.c
@@ -0,0 +1,392 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include "scache.h"
+#include "sink-input.h"
+#include <polyp/mainloop.h>
+#include "sample-util.h"
+#include "play-memchunk.h"
+#include "xmalloc.h"
+#include "subscribe.h"
+#include "namereg.h"
+#include "sound-file.h"
+#include "util.h"
+#include "log.h"
+#include <polyp/channelmap.h>
+#include <polyp/volume.h>
+
+#define UNLOAD_POLL_TIME 2
+
+static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+ pa_core *c = userdata;
+ struct timeval ntv;
+ assert(c && c->mainloop == m && c->scache_auto_unload_event == e);
+
+ pa_scache_unload_unused(c);
+
+ pa_gettimeofday(&ntv);
+ ntv.tv_sec += UNLOAD_POLL_TIME;
+ m->time_restart(e, &ntv);
+}
+
+static void free_entry(pa_scache_entry *e) {
+ assert(e);
+ pa_namereg_unregister(e->core, e->name);
+ pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);
+ pa_xfree(e->name);
+ pa_xfree(e->filename);
+ if (e->memchunk.memblock)
+ pa_memblock_unref(e->memchunk.memblock);
+ pa_xfree(e);
+}
+
+static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
+ pa_scache_entry *e;
+ assert(c && name);
+
+ if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) {
+ if (e->memchunk.memblock)
+ pa_memblock_unref(e->memchunk.memblock);
+
+ pa_xfree(e->filename);
+
+ assert(e->core == c);
+
+ pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
+ } else {
+ e = pa_xmalloc(sizeof(pa_scache_entry));
+
+ if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) {
+ pa_xfree(e);
+ return NULL;
+ }
+
+ e->name = pa_xstrdup(name);
+ e->core = c;
+
+ if (!c->scache) {
+ c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ assert(c->scache);
+ }
+
+ pa_idxset_put(c->scache, e, &e->index);
+
+ pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index);
+ }
+
+ pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX);
+ e->last_used_time = 0;
+ e->memchunk.memblock = NULL;
+ e->memchunk.index = e->memchunk.length = 0;
+ e->filename = NULL;
+ e->lazy = 0;
+ e->last_used_time = 0;
+
+ memset(&e->sample_spec, 0, sizeof(pa_sample_spec));
+
+ return e;
+}
+
+int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) {
+ pa_scache_entry *e;
+ assert(c && name);
+
+ if (!(e = scache_add_item(c, name)))
+ return -1;
+
+ if (ss) {
+ e->sample_spec = *ss;
+ pa_channel_map_init_auto(&e->channel_map, ss->channels);
+ }
+
+ if (map)
+ e->channel_map = *map;
+
+ if (chunk) {
+ e->memchunk = *chunk;
+ pa_memblock_ref(e->memchunk.memblock);
+ }
+
+ if (idx)
+ *idx = e->index;
+
+ return 0;
+}
+
+int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
+ pa_sample_spec ss;
+ pa_memchunk chunk;
+ int r;
+
+#ifdef OS_IS_WIN32
+ char buf[MAX_PATH];
+
+ if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
+ filename = buf;
+#endif
+
+ if (pa_sound_file_load(filename, &ss, &chunk, c->memblock_stat) < 0)
+ return -1;
+
+ r = pa_scache_add_item(c, name, &ss, NULL, &chunk, idx);
+ pa_memblock_unref(chunk.memblock);
+
+ return r;
+}
+
+int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
+ pa_scache_entry *e;
+
+#ifdef OS_IS_WIN32
+ char buf[MAX_PATH];
+
+ if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
+ filename = buf;
+#endif
+
+ assert(c && name);
+
+ if (!(e = scache_add_item(c, name)))
+ return -1;
+
+ e->lazy = 1;
+ e->filename = pa_xstrdup(filename);
+
+ if (!c->scache_auto_unload_event) {
+ struct timeval ntv;
+ pa_gettimeofday(&ntv);
+ ntv.tv_sec += UNLOAD_POLL_TIME;
+ c->scache_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
+ }
+
+ if (idx)
+ *idx = e->index;
+
+ return 0;
+}
+
+int pa_scache_remove_item(pa_core *c, const char *name) {
+ pa_scache_entry *e;
+ assert(c && name);
+
+ if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
+ return -1;
+
+ if (pa_idxset_remove_by_data(c->scache, e, NULL) != e)
+ assert(0);
+
+ free_entry(e);
+ return 0;
+}
+
+static void free_cb(void *p, PA_GCC_UNUSED void *userdata) {
+ pa_scache_entry *e = p;
+ assert(e);
+ free_entry(e);
+}
+
+void pa_scache_free(pa_core *c) {
+ assert(c);
+
+ if (c->scache) {
+ pa_idxset_free(c->scache, free_cb, NULL);
+ c->scache = NULL;
+ }
+
+ if (c->scache_auto_unload_event)
+ c->mainloop->time_free(c->scache_auto_unload_event);
+}
+
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, const pa_cvolume *volume) {
+ pa_scache_entry *e;
+ char *t;
+ pa_cvolume r;
+ assert(c && name && sink);
+
+ if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1)))
+ return -1;
+
+ if (e->lazy && !e->memchunk.memblock) {
+ if (pa_sound_file_load(e->filename, &e->sample_spec, &e->memchunk, c->memblock_stat) < 0)
+ return -1;
+
+ pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
+ }
+
+ if (!e->memchunk.memblock)
+ return -1;
+
+ t = pa_sprintf_malloc("sample:%s", name);
+
+ if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, pa_sw_cvolume_multiply(&r, volume, &e->volume)) < 0) {
+ free(t);
+ return -1;
+ }
+
+ free(t);
+
+ if (e->lazy)
+ time(&e->last_used_time);
+
+ return 0;
+}
+
+const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
+ pa_scache_entry *e;
+ assert(c && id != PA_IDXSET_INVALID);
+
+ if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id)))
+ return NULL;
+
+ return e->name;
+}
+
+uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
+ pa_scache_entry *e;
+ assert(c && name);
+
+ if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
+ return PA_IDXSET_INVALID;
+
+ return e->index;
+}
+
+uint32_t pa_scache_total_size(pa_core *c) {
+ pa_scache_entry *e;
+ uint32_t idx, sum = 0;
+ assert(c);
+
+ if (!c->scache || !pa_idxset_size(c->scache))
+ return 0;
+
+ for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx))
+ if (e->memchunk.memblock)
+ sum += e->memchunk.length;
+
+ return sum;
+}
+
+void pa_scache_unload_unused(pa_core *c) {
+ pa_scache_entry *e;
+ time_t now;
+ uint32_t idx;
+ assert(c);
+
+ if (!c->scache || !pa_idxset_size(c->scache))
+ return;
+
+ time(&now);
+
+ for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
+
+ if (!e->lazy || !e->memchunk.memblock)
+ continue;
+
+ if (e->last_used_time + c->scache_idle_time > now)
+ continue;
+
+ pa_memblock_unref(e->memchunk.memblock);
+ e->memchunk.memblock = NULL;
+ e->memchunk.index = e->memchunk.length = 0;
+
+ pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
+ }
+}
+
+static void add_file(pa_core *c, const char *pathname) {
+ struct stat st;
+ const char *e;
+
+ e = pa_path_get_filename(pathname);
+
+ if (stat(pathname, &st) < 0) {
+ pa_log(__FILE__": stat('%s') failed: %s\n", pathname, strerror(errno));
+ return;
+ }
+
+#if defined(S_ISREG) && defined(S_ISLNK)
+ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
+#endif
+ pa_scache_add_file_lazy(c, e, pathname, NULL);
+}
+
+int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
+ DIR *dir;
+ assert(c && pathname);
+
+ /* First try to open this as directory */
+ if (!(dir = opendir(pathname))) {
+#ifdef HAVE_GLOB_H
+ glob_t p;
+ unsigned int i;
+ /* If that fails, try to open it as shell glob */
+
+ if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) {
+ pa_log(__FILE__": Failed to open directory: %s\n", strerror(errno));
+ return -1;
+ }
+
+ for (i = 0; i < p.gl_pathc; i++)
+ add_file(c, p.gl_pathv[i]);
+
+ globfree(&p);
+#else
+ return -1;
+#endif
+ } else {
+ struct dirent *e;
+
+ while ((e = readdir(dir))) {
+ char p[PATH_MAX];
+
+ if (e->d_name[0] == '.')
+ continue;
+
+ snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
+ add_file(c, p);
+ }
+ }
+
+ closedir(dir);
+ return 0;
+}
diff --git a/src/polypcore/scache.h b/src/polypcore/scache.h
new file mode 100644
index 00000000..d667ae60
--- /dev/null
+++ b/src/polypcore/scache.h
@@ -0,0 +1,62 @@
+#ifndef fooscachehfoo
+#define fooscachehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "core.h"
+#include "memchunk.h"
+#include "sink.h"
+
+typedef struct pa_scache_entry {
+ pa_core *core;
+ uint32_t index;
+ char *name;
+
+ pa_cvolume volume;
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+ pa_memchunk memchunk;
+
+ char *filename;
+
+ int lazy;
+ time_t last_used_time;
+} pa_scache_entry;
+
+int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx);
+int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx);
+int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx);
+
+int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
+
+int pa_scache_remove_item(pa_core *c, const char *name);
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, const pa_cvolume *cvolume);
+void pa_scache_free(pa_core *c);
+
+const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
+uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name);
+
+uint32_t pa_scache_total_size(pa_core *c);
+
+void pa_scache_unload_unused(pa_core *c);
+
+#endif
diff --git a/src/polypcore/sconv-s16be.c b/src/polypcore/sconv-s16be.c
new file mode 100644
index 00000000..8b076f06
--- /dev/null
+++ b/src/polypcore/sconv-s16be.c
@@ -0,0 +1,41 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 "endianmacros.h"
+
+#define INT16_FROM INT16_FROM_BE
+#define INT16_TO INT16_TO_BE
+
+#define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne
+#define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne
+
+#ifdef WORDS_BIGENDIAN
+#define SWAP_WORDS 0
+#else
+#define SWAP_WORDS 1
+#endif
+
+#include "sconv-s16le.h"
+#include "sconv-s16le.c"
diff --git a/src/polypcore/sconv-s16be.h b/src/polypcore/sconv-s16be.h
new file mode 100644
index 00000000..b2b6a8c7
--- /dev/null
+++ b/src/polypcore/sconv-s16be.h
@@ -0,0 +1,28 @@
+#ifndef foosconv_s16befoo
+#define foosconv_s16befoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+void pa_sconv_s16be_to_float32ne(unsigned n, const void *a, float *b);
+void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, void *b);
+
+#endif
diff --git a/src/polypcore/sconv-s16le.c b/src/polypcore/sconv-s16le.c
new file mode 100644
index 00000000..e47c5e8e
--- /dev/null
+++ b/src/polypcore/sconv-s16le.c
@@ -0,0 +1,101 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <inttypes.h>
+
+#include <liboil/liboilfuncs.h>
+
+#include "endianmacros.h"
+#include "sconv.h"
+#include "sconv-s16le.h"
+#include "log.h"
+
+#ifndef INT16_FROM
+#define INT16_FROM INT16_FROM_LE
+#endif
+
+#ifndef INT16_TO
+#define INT16_TO INT16_TO_LE
+#endif
+
+#ifndef SWAP_WORDS
+#ifdef WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#else
+#define SWAP_WORDS 0
+#endif
+#endif
+
+void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b) {
+ const int16_t *ca = a;
+
+ assert(a);
+ assert(b);
+
+#if SWAP_WORDS == 1
+
+ for (; n > 0; n--) {
+ int16_t s = *(ca++);
+ *(b++) = ((float) INT16_FROM(s))/0x7FFF;
+ }
+
+#else
+{
+ static const double add = 0, factor = 1.0/0x7FFF;
+ oil_scaleconv_f32_s16(b, ca, n, &add, &factor);
+}
+#endif
+}
+
+void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b) {
+ int16_t *cb = b;
+
+ assert(a);
+ assert(b);
+
+#if SWAP_WORDS == 1
+
+ for (; n > 0; n--) {
+ int16_t s;
+ float v = *(a++);
+
+ if (v > 1)
+ v = 1;
+
+ if (v < -1)
+ v = -1;
+
+ s = (int16_t) (v * 0x7FFF);
+ *(cb++) = INT16_TO(s);
+ }
+
+#else
+{
+ static const double add = 0, factor = 0x7FFF;
+ oil_scaleconv_s16_f32(cb, a, n, &add, &factor);
+}
+#endif
+}
diff --git a/src/polypcore/sconv-s16le.h b/src/polypcore/sconv-s16le.h
new file mode 100644
index 00000000..ef5e31e8
--- /dev/null
+++ b/src/polypcore/sconv-s16le.h
@@ -0,0 +1,28 @@
+#ifndef foosconv_s16lefoo
+#define foosconv_s16lefoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b);
+void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b);
+
+#endif
diff --git a/src/polypcore/sconv.c b/src/polypcore/sconv.c
new file mode 100644
index 00000000..1fcb5a0c
--- /dev/null
+++ b/src/polypcore/sconv.c
@@ -0,0 +1,168 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <liboil/liboilfuncs.h>
+#include <liboil/liboil.h>
+
+#include "endianmacros.h"
+#include "sconv.h"
+#include "g711.h"
+
+#include "sconv-s16le.h"
+#include "sconv-s16be.h"
+
+static void u8_to_float32ne(unsigned n, const void *a, float *b) {
+ const uint8_t *ca = a;
+ static const double add = -128.0/127.0, factor = 1.0/127.0;
+
+ assert(a);
+ assert(b);
+
+ oil_scaleconv_f32_u8(b, ca, n, &add, &factor);
+}
+
+static void u8_from_float32ne(unsigned n, const float *a, void *b) {
+ uint8_t *cb = b;
+ static const double add = 128.0, factor = 127.0;
+
+ assert(a);
+ assert(b);
+
+ oil_scaleconv_u8_f32(cb, a, n, &add, &factor);
+}
+
+static void float32ne_to_float32ne(unsigned n, const void *a, float *b) {
+ assert(a);
+ assert(b);
+
+ oil_memcpy(b, a, sizeof(float) * n);
+}
+
+static void float32ne_from_float32ne(unsigned n, const float *a, void *b) {
+ assert(a);
+ assert(b);
+
+ oil_memcpy(b, a, sizeof(float) * n);
+}
+
+static void ulaw_to_float32ne(unsigned n, const void *a, float *b) {
+ const uint8_t *ca = a;
+
+ assert(a);
+ assert(b);
+
+ for (; n > 0; n--)
+ *(b++) = st_ulaw2linear16(*(ca++)) * 1.0F / 0x7FFF;
+}
+
+static void ulaw_from_float32ne(unsigned n, const float *a, void *b) {
+ uint8_t *cb = b;
+
+ assert(a);
+ assert(b);
+
+ for (; n > 0; n--) {
+ float v = *(a++);
+
+ if (v > 1)
+ v = 1;
+
+ if (v < -1)
+ v = -1;
+
+ *(cb++) = st_14linear2ulaw((int16_t) (v * 0x1FFF));
+ }
+}
+
+static void alaw_to_float32ne(unsigned n, const void *a, float *b) {
+ const uint8_t *ca = a;
+
+ assert(a);
+ assert(b);
+
+ for (; n > 0; n--)
+ *(b++) = st_alaw2linear16(*(ca++)) * 1.0F / 0x7FFF;
+}
+
+static void alaw_from_float32ne(unsigned n, const float *a, void *b) {
+ uint8_t *cb = b;
+
+ assert(a);
+ assert(b);
+
+ for (; n > 0; n--) {
+ float v = *(a++);
+
+ if (v > 1)
+ v = 1;
+
+ if (v < -1)
+ v = -1;
+
+ *(cb++) = st_13linear2alaw((int16_t) (v * 0xFFF));
+ }
+}
+
+pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
+ switch(f) {
+ case PA_SAMPLE_U8:
+ return u8_to_float32ne;
+ case PA_SAMPLE_S16LE:
+ return pa_sconv_s16le_to_float32ne;
+ case PA_SAMPLE_S16BE:
+ return pa_sconv_s16be_to_float32ne;
+ case PA_SAMPLE_FLOAT32NE:
+ return float32ne_to_float32ne;
+ case PA_SAMPLE_ALAW:
+ return alaw_to_float32ne;
+ case PA_SAMPLE_ULAW:
+ return ulaw_to_float32ne;
+ default:
+ return NULL;
+ }
+}
+
+pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
+ switch(f) {
+ case PA_SAMPLE_U8:
+ return u8_from_float32ne;
+ case PA_SAMPLE_S16LE:
+ return pa_sconv_s16le_from_float32ne;
+ case PA_SAMPLE_S16BE:
+ return pa_sconv_s16be_from_float32ne;
+ case PA_SAMPLE_FLOAT32NE:
+ return float32ne_from_float32ne;
+ case PA_SAMPLE_ALAW:
+ return alaw_from_float32ne;
+ case PA_SAMPLE_ULAW:
+ return ulaw_from_float32ne;
+ default:
+ return NULL;
+ }
+}
diff --git a/src/polypcore/sconv.h b/src/polypcore/sconv.h
new file mode 100644
index 00000000..a0c15c24
--- /dev/null
+++ b/src/polypcore/sconv.h
@@ -0,0 +1,33 @@
+#ifndef foosconvhfoo
+#define foosconvhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <polyp/sample.h>
+
+typedef void (*pa_convert_to_float32ne_func_t)(unsigned n, const void *a, float *b);
+typedef void (*pa_convert_from_float32ne_func_t)(unsigned n, const float *a, void *b);
+
+pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f);
+pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f);
+
+#endif
diff --git a/src/polypcore/sink-input.c b/src/polypcore/sink-input.c
new file mode 100644
index 00000000..f447b8cf
--- /dev/null
+++ b/src/polypcore/sink-input.c
@@ -0,0 +1,368 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sink-input.h"
+#include "sample-util.h"
+#include "xmalloc.h"
+#include "subscribe.h"
+#include "log.h"
+
+#define CONVERT_BUFFER_LENGTH 4096
+
+pa_sink_input* pa_sink_input_new(
+ pa_sink *s,
+ const char *driver,
+ const char *name,
+ const pa_sample_spec *spec,
+ const pa_channel_map *map,
+ int variable_rate,
+ int resample_method) {
+
+ pa_sink_input *i;
+ pa_resampler *resampler = NULL;
+ int r;
+ char st[256];
+ pa_channel_map tmap;
+
+ assert(s);
+ assert(spec);
+ assert(s->state == PA_SINK_RUNNING);
+
+ if (pa_idxset_size(s->inputs) >= PA_MAX_INPUTS_PER_SINK) {
+ pa_log_warn(__FILE__": Failed to create sink input: too many inputs per sink.\n");
+ return NULL;
+ }
+
+ if (resample_method == PA_RESAMPLER_INVALID)
+ resample_method = s->core->resample_method;
+
+ if (!map) {
+ pa_channel_map_init_auto(&tmap, spec->channels);
+ map = &tmap;
+ }
+
+ if (variable_rate || !pa_sample_spec_equal(spec, &s->sample_spec) || !pa_channel_map_equal(map, &s->channel_map))
+ if (!(resampler = pa_resampler_new(spec, map, &s->sample_spec, &s->channel_map, s->core->memblock_stat, resample_method)))
+ return NULL;
+
+ i = pa_xnew(pa_sink_input, 1);
+ i->ref = 1;
+ i->state = PA_SINK_INPUT_RUNNING;
+ i->name = pa_xstrdup(name);
+ i->driver = pa_xstrdup(driver);
+ i->owner = NULL;
+ i->sink = s;
+ i->client = NULL;
+
+ i->sample_spec = *spec;
+ i->channel_map = *map;
+
+ pa_cvolume_reset(&i->volume, spec->channels);
+
+ i->peek = NULL;
+ i->drop = NULL;
+ i->kill = NULL;
+ i->get_latency = NULL;
+ i->underrun = NULL;
+ i->userdata = NULL;
+
+ i->playing = 0;
+
+ pa_memchunk_reset(&i->resampled_chunk);
+ i->resampler = resampler;
+
+ assert(s->core);
+ r = pa_idxset_put(s->core->sink_inputs, i, &i->index);
+ assert(r == 0 && i->index != PA_IDXSET_INVALID);
+ r = pa_idxset_put(s->inputs, i, NULL);
+ assert(r == 0);
+
+ pa_sample_spec_snprint(st, sizeof(st), spec);
+ pa_log_info(__FILE__": created %u \"%s\" on %u with sample spec \"%s\"\n", i->index, i->name, s->index, st);
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
+
+ return i;
+}
+
+void pa_sink_input_disconnect(pa_sink_input *i) {
+ assert(i);
+ assert(i->state != PA_SINK_INPUT_DISCONNECTED);
+ assert(i->sink);
+ assert(i->sink->core);
+
+ pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL);
+ pa_idxset_remove_by_data(i->sink->inputs, i, NULL);
+
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
+ i->sink = NULL;
+
+ i->peek = NULL;
+ i->drop = NULL;
+ i->kill = NULL;
+ i->get_latency = NULL;
+ i->underrun = NULL;
+
+ i->playing = 0;
+ i->state = PA_SINK_INPUT_DISCONNECTED;
+}
+
+static void sink_input_free(pa_sink_input* i) {
+ assert(i);
+
+ if (i->state != PA_SINK_INPUT_DISCONNECTED)
+ pa_sink_input_disconnect(i);
+
+ pa_log_info(__FILE__": freed %u \"%s\"\n", i->index, i->name);
+
+ if (i->resampled_chunk.memblock)
+ pa_memblock_unref(i->resampled_chunk.memblock);
+
+ if (i->resampler)
+ pa_resampler_free(i->resampler);
+
+ pa_xfree(i->name);
+ pa_xfree(i->driver);
+ pa_xfree(i);
+}
+
+void pa_sink_input_unref(pa_sink_input *i) {
+ assert(i);
+ assert(i->ref >= 1);
+
+ if (!(--i->ref))
+ sink_input_free(i);
+}
+
+pa_sink_input* pa_sink_input_ref(pa_sink_input *i) {
+ assert(i);
+ assert(i->ref >= 1);
+
+ i->ref++;
+ return i;
+}
+
+void pa_sink_input_kill(pa_sink_input*i) {
+ assert(i);
+ assert(i->ref >= 1);
+
+ if (i->kill)
+ i->kill(i);
+}
+
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) {
+ pa_usec_t r = 0;
+
+ assert(i);
+ assert(i->ref >= 1);
+
+ if (i->get_latency)
+ r += i->get_latency(i);
+
+ if (i->resampled_chunk.memblock)
+ r += pa_bytes_to_usec(i->resampled_chunk.length, &i->sample_spec);
+
+ return r;
+}
+
+int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume) {
+ int ret = -1;
+ int do_volume_adj_here;
+
+ assert(i);
+ assert(i->ref >= 1);
+ assert(chunk);
+ assert(volume);
+
+ pa_sink_input_ref(i);
+
+ if (!i->peek || !i->drop || i->state == PA_SINK_INPUT_CORKED)
+ goto finish;
+
+ if (!i->resampler) {
+ do_volume_adj_here = 0;
+ ret = i->peek(i, chunk);
+ goto finish;
+ }
+
+ do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
+
+ while (!i->resampled_chunk.memblock) {
+ pa_memchunk tchunk;
+ size_t l;
+
+ if ((ret = i->peek(i, &tchunk)) < 0)
+ goto finish;
+
+ assert(tchunk.length);
+
+ l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH);
+
+ if (l > tchunk.length)
+ l = tchunk.length;
+
+ i->drop(i, &tchunk, l);
+ tchunk.length = l;
+
+ /* It might be necessary to adjust the volume here */
+ if (do_volume_adj_here) {
+ pa_memchunk_make_writable(&tchunk, i->sink->core->memblock_stat, 0);
+ pa_volume_memchunk(&tchunk, &i->sample_spec, &i->volume);
+ }
+
+ pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk);
+ pa_memblock_unref(tchunk.memblock);
+ }
+
+ assert(i->resampled_chunk.memblock);
+ assert(i->resampled_chunk.length);
+
+ *chunk = i->resampled_chunk;
+ pa_memblock_ref(i->resampled_chunk.memblock);
+
+ ret = 0;
+
+finish:
+
+ if (ret < 0 && i->playing && i->underrun)
+ i->underrun(i);
+
+ i->playing = ret >= 0;
+
+ if (ret >= 0) {
+ /* Let's see if we had to apply the volume adjustment
+ * ourselves, or if this can be done by the sink for us */
+
+ if (do_volume_adj_here)
+ /* We've both the same channel map, so let's have the sink do the adjustment for us*/
+
+ pa_cvolume_reset(volume, i->sample_spec.channels);
+ else
+ /* We had different channel maps, so we already did the adjustment */
+ *volume = i->volume;
+ }
+
+ pa_sink_input_unref(i);
+
+ return ret;
+}
+
+void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
+ assert(i);
+ assert(i->ref >= 1);
+ assert(length > 0);
+
+ if (!i->resampler) {
+ if (i->drop)
+ i->drop(i, chunk, length);
+ return;
+ }
+
+ assert(i->resampled_chunk.memblock);
+ assert(i->resampled_chunk.length >= length);
+
+ i->resampled_chunk.index += length;
+ i->resampled_chunk.length -= length;
+
+ if (i->resampled_chunk.length <= 0) {
+ pa_memblock_unref(i->resampled_chunk.memblock);
+ i->resampled_chunk.memblock = NULL;
+ i->resampled_chunk.index = i->resampled_chunk.length = 0;
+ }
+}
+
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
+ assert(i);
+ assert(i->ref >= 1);
+ assert(i->sink);
+ assert(i->sink->core);
+
+ if (pa_cvolume_equal(&i->volume, volume))
+ return;
+
+ i->volume = *volume;
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+}
+
+const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i) {
+ assert(i);
+ assert(i->ref >= 1);
+
+ return &i->volume;
+}
+
+void pa_sink_input_cork(pa_sink_input *i, int b) {
+ int n;
+
+ assert(i);
+ assert(i->ref >= 1);
+
+ if (i->state == PA_SINK_INPUT_DISCONNECTED)
+ return;
+
+ n = i->state == PA_SINK_INPUT_CORKED && !b;
+
+ i->state = b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
+
+ if (n)
+ pa_sink_notify(i->sink);
+}
+
+void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
+ assert(i);
+ assert(i->resampler);
+ assert(i->ref >= 1);
+
+ if (i->sample_spec.rate == rate)
+ return;
+
+ i->sample_spec.rate = rate;
+ pa_resampler_set_input_rate(i->resampler, rate);
+}
+
+void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
+ assert(i);
+ assert(i->ref >= 1);
+
+ pa_xfree(i->name);
+ i->name = pa_xstrdup(name);
+
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+}
+
+pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
+ assert(i);
+ assert(i->ref >= 1);
+
+ if (!i->resampler)
+ return PA_RESAMPLER_INVALID;
+
+ return pa_resampler_get_method(i->resampler);
+}
diff --git a/src/polypcore/sink-input.h b/src/polypcore/sink-input.h
new file mode 100644
index 00000000..a5ad1958
--- /dev/null
+++ b/src/polypcore/sink-input.h
@@ -0,0 +1,106 @@
+#ifndef foosinkinputhfoo
+#define foosinkinputhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+typedef struct pa_sink_input pa_sink_input;
+
+#include "sink.h"
+#include <polyp/sample.h>
+#include "memblockq.h"
+#include "resampler.h"
+#include "module.h"
+#include "client.h"
+
+typedef enum pa_sink_input_state {
+ PA_SINK_INPUT_RUNNING,
+ PA_SINK_INPUT_CORKED,
+ PA_SINK_INPUT_DISCONNECTED
+} pa_sink_input_state_t;
+
+struct pa_sink_input {
+ int ref;
+ uint32_t index;
+ pa_sink_input_state_t state;
+
+ char *name, *driver;
+ pa_module *owner;
+
+ pa_sink *sink;
+ pa_client *client;
+
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+
+ pa_cvolume volume;
+
+ int (*peek) (pa_sink_input *i, pa_memchunk *chunk);
+ void (*drop) (pa_sink_input *i, const pa_memchunk *chunk, size_t length);
+ void (*kill) (pa_sink_input *i);
+ pa_usec_t (*get_latency) (pa_sink_input *i);
+ void (*underrun) (pa_sink_input *i);
+
+ void *userdata;
+
+ int playing;
+
+ pa_memchunk resampled_chunk;
+ pa_resampler *resampler;
+};
+
+pa_sink_input* pa_sink_input_new(
+ pa_sink *s,
+ const char *driver,
+ const char *name,
+ const pa_sample_spec *spec,
+ const pa_channel_map *map,
+ int variable_rate,
+ int resample_method);
+
+void pa_sink_input_unref(pa_sink_input* i);
+pa_sink_input* pa_sink_input_ref(pa_sink_input* i);
+
+/* To be called by the implementing module only */
+void pa_sink_input_disconnect(pa_sink_input* i);
+
+/* External code may request disconnection with this funcion */
+void pa_sink_input_kill(pa_sink_input*i);
+
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i);
+
+int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume);
+void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length);
+
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume);
+const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i);
+
+void pa_sink_input_cork(pa_sink_input *i, int b);
+
+void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
+
+void pa_sink_input_set_name(pa_sink_input *i, const char *name);
+
+pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
+
+#endif
diff --git a/src/polypcore/sink.c b/src/polypcore/sink.c
new file mode 100644
index 00000000..411befe7
--- /dev/null
+++ b/src/polypcore/sink.c
@@ -0,0 +1,445 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "sink.h"
+#include "sink-input.h"
+#include "namereg.h"
+#include "util.h"
+#include "sample-util.h"
+#include "xmalloc.h"
+#include "subscribe.h"
+#include "log.h"
+#include <polyp/polyplib-introspect.h>
+
+#define MAX_MIX_CHANNELS 32
+
+pa_sink* pa_sink_new(
+ pa_core *core,
+ const char *driver,
+ const char *name,
+ int fail,
+ const pa_sample_spec *spec,
+ const pa_channel_map *map) {
+
+ pa_sink *s;
+ char *n = NULL;
+ char st[256];
+ int r;
+
+ assert(core);
+ assert(name);
+ assert(*name);
+ assert(spec);
+
+ s = pa_xnew(pa_sink, 1);
+
+ if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) {
+ pa_xfree(s);
+ return NULL;
+ }
+
+ s->ref = 1;
+ s->core = core;
+ s->state = PA_SINK_RUNNING;
+ s->name = pa_xstrdup(name);
+ s->description = NULL;
+ s->driver = pa_xstrdup(driver);
+ s->owner = NULL;
+
+ s->sample_spec = *spec;
+ if (map)
+ s->channel_map = *map;
+ else
+ pa_channel_map_init_auto(&s->channel_map, spec->channels);
+
+ s->inputs = pa_idxset_new(NULL, NULL);
+
+ pa_cvolume_reset(&s->sw_volume, spec->channels);
+ pa_cvolume_reset(&s->hw_volume, spec->channels);
+
+ s->get_latency = NULL;
+ s->notify = NULL;
+ s->set_hw_volume = NULL;
+ s->get_hw_volume = NULL;
+ s->userdata = NULL;
+
+ r = pa_idxset_put(core->sinks, s, &s->index);
+ assert(s->index != PA_IDXSET_INVALID && r >= 0);
+
+ pa_sample_spec_snprint(st, sizeof(st), spec);
+ pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st);
+
+ n = pa_sprintf_malloc("%s_monitor", name);
+ s->monitor_source = pa_source_new(core, driver, n, 0, spec, map);
+ assert(s->monitor_source);
+ pa_xfree(n);
+ s->monitor_source->monitor_of = s;
+ s->monitor_source->description = pa_sprintf_malloc("Monitor source of sink '%s'", s->name);
+
+ pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
+
+ return s;
+}
+
+void pa_sink_disconnect(pa_sink* s) {
+ pa_sink_input *i, *j = NULL;
+
+ assert(s);
+ assert(s->state == PA_SINK_RUNNING);
+
+ pa_namereg_unregister(s->core, s->name);
+
+ while ((i = pa_idxset_first(s->inputs, NULL))) {
+ assert(i != j);
+ pa_sink_input_kill(i);
+ j = i;
+ }
+
+ pa_source_disconnect(s->monitor_source);
+
+ pa_idxset_remove_by_data(s->core->sinks, s, NULL);
+
+ s->get_latency = NULL;
+ s->notify = NULL;
+ s->get_hw_volume = NULL;
+ s->set_hw_volume = NULL;
+
+ s->state = PA_SINK_DISCONNECTED;
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+}
+
+static void sink_free(pa_sink *s) {
+ assert(s);
+ assert(!s->ref);
+
+ if (s->state != PA_SINK_DISCONNECTED)
+ pa_sink_disconnect(s);
+
+ pa_log_info(__FILE__": freed %u \"%s\"\n", s->index, s->name);
+
+ pa_source_unref(s->monitor_source);
+ s->monitor_source = NULL;
+
+ pa_idxset_free(s->inputs, NULL, NULL);
+
+ pa_xfree(s->name);
+ pa_xfree(s->description);
+ pa_xfree(s->driver);
+ pa_xfree(s);
+}
+
+void pa_sink_unref(pa_sink*s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ if (!(--s->ref))
+ sink_free(s);
+}
+
+pa_sink* pa_sink_ref(pa_sink *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->ref++;
+ return s;
+}
+
+void pa_sink_notify(pa_sink*s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ if (s->notify)
+ s->notify(s);
+}
+
+static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) {
+ uint32_t idx = PA_IDXSET_INVALID;
+ pa_sink_input *i;
+ unsigned n = 0;
+
+ assert(s);
+ assert(s->ref >= 1);
+ assert(info);
+
+ for (i = pa_idxset_first(s->inputs, &idx); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &idx)) {
+ /* Increase ref counter, to make sure that this input doesn't
+ * vanish while we still need it */
+ pa_sink_input_ref(i);
+
+ if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0) {
+ pa_sink_input_unref(i);
+ continue;
+ }
+
+ info->userdata = i;
+
+ assert(info->chunk.memblock);
+ assert(info->chunk.memblock->data);
+ assert(info->chunk.length);
+
+ info++;
+ maxinfo--;
+ n++;
+ }
+
+ return n;
+}
+
+static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) {
+ assert(s);
+ assert(s->ref >= 1);
+ assert(info);
+
+ for (; maxinfo > 0; maxinfo--, info++) {
+ pa_sink_input *i = info->userdata;
+
+ assert(i);
+ assert(info->chunk.memblock);
+
+ /* Drop read data */
+ pa_sink_input_drop(i, &info->chunk, length);
+ pa_memblock_unref(info->chunk.memblock);
+
+ /* Decrease ref counter */
+ pa_sink_input_unref(i);
+ info->userdata = NULL;
+ }
+}
+
+int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
+ pa_mix_info info[MAX_MIX_CHANNELS];
+ unsigned n;
+ int r = -1;
+
+ assert(s);
+ assert(s->ref >= 1);
+ assert(length);
+ assert(result);
+
+ pa_sink_ref(s);
+
+ n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
+
+ if (n <= 0)
+ goto finish;
+
+ if (n == 1) {
+ pa_cvolume volume;
+
+ *result = info[0].chunk;
+ pa_memblock_ref(result->memblock);
+
+ if (result->length > length)
+ result->length = length;
+
+ pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume);
+
+ if (!pa_cvolume_is_norm(&volume)) {
+ pa_memchunk_make_writable(result, s->core->memblock_stat, 0);
+ pa_volume_memchunk(result, &s->sample_spec, &volume);
+ }
+ } else {
+ result->memblock = pa_memblock_new(length, s->core->memblock_stat);
+ assert(result->memblock);
+
+ result->length = pa_mix(info, n, result->memblock->data, length, &s->sample_spec, &s->sw_volume);
+ result->index = 0;
+ }
+
+ inputs_drop(s, info, n, result->length);
+ pa_source_post(s->monitor_source, result);
+
+ r = 0;
+
+finish:
+ pa_sink_unref(s);
+
+ return r;
+}
+
+int pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
+ pa_mix_info info[MAX_MIX_CHANNELS];
+ unsigned n;
+ int r = -1;
+
+ assert(s);
+ assert(s->ref >= 1);
+ assert(target);
+ assert(target->memblock);
+ assert(target->length);
+ assert(target->memblock->data);
+
+ pa_sink_ref(s);
+
+ n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
+
+ if (n <= 0)
+ goto finish;
+
+ if (n == 1) {
+ pa_cvolume volume;
+
+ if (target->length > info[0].chunk.length)
+ target->length = info[0].chunk.length;
+
+ memcpy((uint8_t*) target->memblock->data + target->index,
+ (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index,
+ target->length);
+
+ pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume);
+
+ if (!pa_cvolume_is_norm(&volume))
+ pa_volume_memchunk(target, &s->sample_spec, &volume);
+ } else
+ target->length = pa_mix(info, n,
+ (uint8_t*) target->memblock->data + target->index,
+ target->length,
+ &s->sample_spec,
+ &s->sw_volume);
+
+ inputs_drop(s, info, n, target->length);
+ pa_source_post(s->monitor_source, target);
+
+ r = 0;
+
+finish:
+ pa_sink_unref(s);
+
+ return r;
+}
+
+void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
+ pa_memchunk chunk;
+ size_t l, d;
+
+ assert(s);
+ assert(s->ref >= 1);
+ assert(target);
+ assert(target->memblock);
+ assert(target->length);
+ assert(target->memblock->data);
+
+ pa_sink_ref(s);
+
+ l = target->length;
+ d = 0;
+ while (l > 0) {
+ chunk = *target;
+ chunk.index += d;
+ chunk.length -= d;
+
+ if (pa_sink_render_into(s, &chunk) < 0)
+ break;
+
+ d += chunk.length;
+ l -= chunk.length;
+ }
+
+ if (l > 0) {
+ chunk = *target;
+ chunk.index += d;
+ chunk.length -= d;
+ pa_silence_memchunk(&chunk, &s->sample_spec);
+ }
+
+ pa_sink_unref(s);
+}
+
+void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
+ assert(s);
+ assert(s->ref >= 1);
+ assert(length);
+ assert(result);
+
+ /*** This needs optimization ***/
+
+ result->memblock = pa_memblock_new(result->length = length, s->core->memblock_stat);
+ result->index = 0;
+
+ pa_sink_render_into_full(s, result);
+}
+
+pa_usec_t pa_sink_get_latency(pa_sink *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ if (!s->get_latency)
+ return 0;
+
+ return s->get_latency(s);
+}
+
+void pa_sink_set_owner(pa_sink *s, pa_module *m) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->owner = m;
+
+ if (s->monitor_source)
+ pa_source_set_owner(s->monitor_source, m);
+}
+
+void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) {
+ pa_cvolume *v;
+
+ assert(s);
+ assert(s->ref >= 1);
+ assert(volume);
+
+ if (m == PA_MIXER_HARDWARE && s->set_hw_volume)
+ v = &s->hw_volume;
+ else
+ v = &s->sw_volume;
+
+ if (pa_cvolume_equal(v, volume))
+ return;
+
+ *v = *volume;
+
+ if (v == &s->hw_volume)
+ if (s->set_hw_volume(s) < 0)
+ s->sw_volume = *volume;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ if (m == PA_MIXER_HARDWARE && s->set_hw_volume) {
+
+ if (s->get_hw_volume)
+ s->get_hw_volume(s);
+
+ return &s->hw_volume;
+ } else
+ return &s->sw_volume;
+}
diff --git a/src/polypcore/sink.h b/src/polypcore/sink.h
new file mode 100644
index 00000000..268461ef
--- /dev/null
+++ b/src/polypcore/sink.h
@@ -0,0 +1,100 @@
+#ifndef foosinkhfoo
+#define foosinkhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+typedef struct pa_sink pa_sink;
+
+#include "core.h"
+#include <polyp/sample.h>
+#include "idxset.h"
+#include "source.h"
+#include <polyp/channelmap.h>
+#include "module.h"
+#include <polyp/volume.h>
+
+#define PA_MAX_INPUTS_PER_SINK 6
+
+typedef enum pa_sink_state {
+ PA_SINK_RUNNING,
+ PA_SINK_DISCONNECTED
+} pa_sink_state_t;
+
+typedef enum pa_mixer {
+ PA_MIXER_SOFTWARE,
+ PA_MIXER_HARDWARE
+} pa_mixer_t;
+
+struct pa_sink {
+ int ref;
+ uint32_t index;
+ pa_core *core;
+ pa_sink_state_t state;
+
+ char *name, *description, *driver;
+ pa_module *owner;
+
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+
+ pa_idxset *inputs;
+ pa_source *monitor_source;
+
+ pa_cvolume hw_volume, sw_volume;
+
+ void (*notify)(pa_sink*sink);
+ pa_usec_t (*get_latency)(pa_sink *s);
+ int (*set_hw_volume)(pa_sink *s);
+ int (*get_hw_volume)(pa_sink *s);
+
+ void *userdata;
+};
+
+pa_sink* pa_sink_new(
+ pa_core *core,
+ const char *driver,
+ const char *name,
+ int namereg_fail,
+ const pa_sample_spec *spec,
+ const pa_channel_map *map);
+
+void pa_sink_disconnect(pa_sink* s);
+void pa_sink_unref(pa_sink*s);
+pa_sink* pa_sink_ref(pa_sink *s);
+
+int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
+void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
+int pa_sink_render_into(pa_sink*s, pa_memchunk *target);
+void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target);
+
+pa_usec_t pa_sink_get_latency(pa_sink *s);
+
+void pa_sink_notify(pa_sink*s);
+
+void pa_sink_set_owner(pa_sink *sink, pa_module *m);
+
+void pa_sink_set_volume(pa_sink *sink, pa_mixer_t m, const pa_cvolume *volume);
+const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_mixer_t m);
+
+#endif
diff --git a/src/polypcore/sioman.c b/src/polypcore/sioman.c
new file mode 100644
index 00000000..8d7b136c
--- /dev/null
+++ b/src/polypcore/sioman.c
@@ -0,0 +1,42 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include "sioman.h"
+
+static int stdio_inuse = 0;
+
+int pa_stdio_acquire(void) {
+ if (stdio_inuse)
+ return -1;
+
+ stdio_inuse = 1;
+ return 0;
+}
+
+void pa_stdio_release(void) {
+ assert(stdio_inuse);
+ stdio_inuse = 0;
+}
diff --git a/src/polypcore/sioman.h b/src/polypcore/sioman.h
new file mode 100644
index 00000000..840d93f2
--- /dev/null
+++ b/src/polypcore/sioman.h
@@ -0,0 +1,28 @@
+#ifndef foosiomanhfoo
+#define foosiomanhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+int pa_stdio_acquire(void);
+void pa_stdio_release(void);
+
+#endif
diff --git a/src/polypcore/socket-client.c b/src/polypcore/socket-client.c
new file mode 100644
index 00000000..29c9775e
--- /dev/null
+++ b/src/polypcore/socket-client.c
@@ -0,0 +1,513 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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
+
+/* #undef HAVE_LIBASYNCNS */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_LIBASYNCNS
+#include <asyncns.h>
+#endif
+
+#include "winsock.h"
+
+#include "socket-client.h"
+#include "socket-util.h"
+#include "util.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "parseaddr.h"
+
+#define CONNECT_TIMEOUT 5
+
+struct pa_socket_client {
+ int ref;
+ pa_mainloop_api *mainloop;
+ int fd;
+ pa_io_event *io_event;
+ pa_time_event *timeout_event;
+ pa_defer_event *defer_event;
+ void (*callback)(pa_socket_client*c, pa_iochannel *io, void *userdata);
+ void *userdata;
+ int local;
+#ifdef HAVE_LIBASYNCNS
+ asyncns_t *asyncns;
+ asyncns_query_t * asyncns_query;
+ pa_io_event *asyncns_io_event;
+#endif
+};
+
+static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
+ pa_socket_client *c;
+ assert(m);
+
+ c = pa_xmalloc(sizeof(pa_socket_client));
+ c->ref = 1;
+ c->mainloop = m;
+ c->fd = -1;
+ c->io_event = NULL;
+ c->defer_event = NULL;
+ c->timeout_event = NULL;
+ c->callback = NULL;
+ c->userdata = NULL;
+ c->local = 0;
+
+#ifdef HAVE_LIBASYNCNS
+ c->asyncns = NULL;
+ c->asyncns_io_event = NULL;
+ c->asyncns_query = NULL;
+#endif
+
+ return c;
+}
+
+static void free_events(pa_socket_client *c) {
+ assert(c);
+
+ if (c->io_event) {
+ c->mainloop->io_free(c->io_event);
+ c->io_event = NULL;
+ }
+
+ if (c->defer_event) {
+ c->mainloop->defer_free(c->defer_event);
+ c->defer_event = NULL;
+ }
+
+ if (c->timeout_event) {
+ c->mainloop->time_free(c->timeout_event);
+ c->timeout_event = NULL;
+ }
+}
+
+static void do_call(pa_socket_client *c) {
+ pa_iochannel *io = NULL;
+ int error;
+ socklen_t lerror;
+ assert(c && c->callback);
+
+ pa_socket_client_ref(c);
+
+ if (c->fd < 0)
+ goto finish;
+
+ lerror = sizeof(error);
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &lerror) < 0) {
+ pa_log(__FILE__": getsockopt(): %s\n", strerror(errno));
+ goto finish;
+ }
+
+ if (lerror != sizeof(error)) {
+ pa_log(__FILE__": getsockopt() returned invalid size.\n");
+ goto finish;
+ }
+
+ if (error != 0) {
+ pa_log_debug(__FILE__": connect(): %s\n", strerror(error));
+ errno = error;
+ goto finish;
+ }
+
+ io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
+ assert(io);
+
+finish:
+ if (!io && c->fd >= 0)
+ close(c->fd);
+ c->fd = -1;
+
+ free_events(c);
+
+ assert(c->callback);
+ c->callback(c, io, c->userdata);
+
+ pa_socket_client_unref(c);
+}
+
+static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+ pa_socket_client *c = userdata;
+ assert(m && c && c->defer_event == e);
+ do_call(c);
+}
+
+static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+ pa_socket_client *c = userdata;
+ assert(m && c && c->io_event == e && fd >= 0);
+ do_call(c);
+}
+
+static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
+ int r;
+ assert(c && sa && len);
+
+ pa_make_nonblock_fd(c->fd);
+
+ if ((r = connect(c->fd, sa, len)) < 0) {
+ if (errno != EINPROGRESS) {
+ /*pa_log(__FILE__": connect(): %s\n", strerror(errno));*/
+ return -1;
+ }
+
+ c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c);
+ assert(c->io_event);
+ } else {
+ c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c);
+ assert(c->defer_event);
+ }
+
+ return 0;
+}
+
+pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port) {
+ struct sockaddr_in sa;
+ assert(m && port > 0);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(port);
+ sa.sin_addr.s_addr = htonl(address);
+
+ return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
+}
+
+#ifdef HAVE_SYS_UN_H
+
+pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
+ struct sockaddr_un sa;
+ assert(m && filename);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
+ sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+
+ return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
+}
+
+#else /* HAVE_SYS_UN_H */
+
+pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
+ return NULL;
+}
+
+#endif /* HAVE_SYS_UN_H */
+
+static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) {
+ assert(c);
+ assert(sa);
+ assert(salen);
+
+ switch (sa->sa_family) {
+ case AF_UNIX:
+ c->local = 1;
+ break;
+
+ case AF_INET:
+ c->local = ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
+ break;
+
+ case AF_INET6:
+ c->local = memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
+ break;
+
+ default:
+ c->local = 0;
+ }
+
+ if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
+ pa_log(__FILE__": socket(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ pa_fd_set_cloexec(c->fd, 1);
+ if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
+ pa_socket_tcp_low_delay(c->fd);
+ else
+ pa_socket_low_delay(c->fd);
+
+ if (do_connect(c, sa, salen) < 0)
+ return -1;
+
+ return 0;
+}
+
+pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {
+ pa_socket_client *c;
+ assert(m && sa);
+ c = pa_socket_client_new(m);
+ assert(c);
+
+ if (sockaddr_prepare(c, sa, salen) < 0)
+ goto fail;
+
+ return c;
+
+fail:
+ pa_socket_client_unref(c);
+ return NULL;
+
+}
+
+static void socket_client_free(pa_socket_client *c) {
+ assert(c && c->mainloop);
+
+
+ free_events(c);
+
+ if (c->fd >= 0)
+ close(c->fd);
+
+#ifdef HAVE_LIBASYNCNS
+ if (c->asyncns_query)
+ asyncns_cancel(c->asyncns, c->asyncns_query);
+ if (c->asyncns)
+ asyncns_free(c->asyncns);
+ if (c->asyncns_io_event)
+ c->mainloop->io_free(c->asyncns_io_event);
+#endif
+
+ pa_xfree(c);
+}
+
+void pa_socket_client_unref(pa_socket_client *c) {
+ assert(c && c->ref >= 1);
+
+ if (!(--(c->ref)))
+ socket_client_free(c);
+}
+
+pa_socket_client* pa_socket_client_ref(pa_socket_client *c) {
+ assert(c && c->ref >= 1);
+ c->ref++;
+ return c;
+}
+
+void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata) {
+ assert(c);
+ c->callback = on_connection;
+ c->userdata = userdata;
+}
+
+pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
+ struct sockaddr_in6 sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(port);
+ memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr));
+
+ return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
+}
+
+#ifdef HAVE_LIBASYNCNS
+
+static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+ pa_socket_client *c = userdata;
+ struct addrinfo *res = NULL;
+ int ret;
+ assert(m && c && c->asyncns_io_event == e && fd >= 0);
+
+ if (asyncns_wait(c->asyncns, 0) < 0)
+ goto fail;
+
+ if (!asyncns_isdone(c->asyncns, c->asyncns_query))
+ return;
+
+ ret = asyncns_getaddrinfo_done(c->asyncns, c->asyncns_query, &res);
+ c->asyncns_query = NULL;
+
+ if (ret != 0 || !res)
+ goto fail;
+
+ if (res->ai_addr)
+ sockaddr_prepare(c, res->ai_addr, res->ai_addrlen);
+
+ asyncns_freeaddrinfo(res);
+
+ goto finish;
+
+fail:
+ errno == EHOSTUNREACH;
+ do_call(c);
+
+finish:
+
+ m->io_free(c->asyncns_io_event);
+ c->asyncns_io_event = NULL;
+}
+
+#endif
+
+static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ pa_socket_client *c = userdata;
+ assert(m);
+ assert(e);
+ assert(tv);
+ assert(c);
+
+ if (c->fd >= 0) {
+ close(c->fd);
+ c->fd = -1;
+ }
+
+ errno = ETIMEDOUT;
+ do_call(c);
+}
+
+static void start_timeout(pa_socket_client *c) {
+ struct timeval tv;
+ assert(c);
+ assert(!c->timeout_event);
+
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, CONNECT_TIMEOUT * 1000000);
+ c->timeout_event = c->mainloop->time_new(c->mainloop, &tv, timeout_cb, c);
+}
+
+pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*name, uint16_t default_port) {
+ pa_socket_client *c = NULL;
+ pa_parsed_address a;
+ assert(m && name);
+
+ if (pa_parse_address(name, &a) < 0)
+ return NULL;
+
+ if (!a.port)
+ a.port = default_port;
+
+ switch (a.type) {
+ case PA_PARSED_ADDRESS_UNIX:
+ if ((c = pa_socket_client_new_unix(m, a.path_or_host)))
+ start_timeout(c);
+ break;
+
+ case PA_PARSED_ADDRESS_TCP4: /* Fallthrough */
+ case PA_PARSED_ADDRESS_TCP6: /* Fallthrough */
+ case PA_PARSED_ADDRESS_TCP_AUTO:{
+
+ struct addrinfo hints;
+ char port[12];
+
+ snprintf(port, sizeof(port), "%u", (unsigned) a.port);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC);
+ hints.ai_socktype = SOCK_STREAM;
+
+#ifdef HAVE_LIBASYNCNS
+ {
+ asyncns_t *asyncns;
+
+ if (!(asyncns = asyncns_new(1)))
+ goto finish;
+
+ c = pa_socket_client_new(m);
+ c->asyncns = asyncns;
+ c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);
+ c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints);
+ assert(c->asyncns_query);
+ start_timeout(c);
+ }
+#else /* HAVE_LIBASYNCNS */
+ {
+#ifdef HAVE_GETADDRINFO
+ int ret;
+ struct addrinfo *res = NULL;
+
+ ret = getaddrinfo(a.path_or_host, port, &hints, &res);
+
+ if (ret < 0 || !res)
+ goto finish;
+
+ if (res->ai_addr) {
+ if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen)))
+ start_timeout(c);
+ }
+
+ freeaddrinfo(res);
+#else /* HAVE_GETADDRINFO */
+ struct hostent *host = NULL;
+ struct sockaddr_in s;
+
+ /* FIXME: PF_INET6 support */
+ if (hints.ai_family != PF_INET)
+ goto finish;
+
+ host = gethostbyname(a.path_or_host);
+ if (!host) {
+ unsigned int addr = inet_addr(a.path_or_host);
+ if (addr != INADDR_NONE)
+ host = gethostbyaddr((char*)&addr, 4, AF_INET);
+ }
+
+ if (!host)
+ goto finish;
+
+ s.sin_family = AF_INET;
+ memcpy(&s.sin_addr, host->h_addr, sizeof(struct in_addr));
+ s.sin_port = port;
+
+ if ((c = pa_socket_client_new_sockaddr(m, &s, sizeof(s))))
+ start_timeout(c);
+#endif /* HAVE_GETADDRINFO */
+ }
+#endif /* HAVE_LIBASYNCNS */
+ }
+ }
+
+finish:
+ pa_xfree(a.path_or_host);
+ return c;
+
+}
+
+/* Return non-zero when the target sockaddr is considered
+ local. "local" means UNIX socket or TCP socket on localhost. Other
+ local IP addresses are not considered local. */
+int pa_socket_client_is_local(pa_socket_client *c) {
+ assert(c);
+ return c->local;
+}
diff --git a/src/polypcore/socket-client.h b/src/polypcore/socket-client.h
new file mode 100644
index 00000000..624880d7
--- /dev/null
+++ b/src/polypcore/socket-client.h
@@ -0,0 +1,47 @@
+#ifndef foosocketclienthfoo
+#define foosocketclienthfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+#include <polyp/mainloop-api.h>
+#include "iochannel.h"
+
+struct sockaddr;
+
+typedef struct pa_socket_client pa_socket_client;
+
+pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port);
+pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port);
+pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename);
+pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen);
+pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char *a, uint16_t default_port);
+
+void pa_socket_client_unref(pa_socket_client *c);
+pa_socket_client* pa_socket_client_ref(pa_socket_client *c);
+
+void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata);
+
+int pa_socket_client_is_local(pa_socket_client *c);
+
+#endif
diff --git a/src/polypcore/socket-server.c b/src/polypcore/socket-server.c
new file mode 100644
index 00000000..262b32a7
--- /dev/null
+++ b/src/polypcore/socket-server.c
@@ -0,0 +1,432 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#ifndef SUN_LEN
+#define SUN_LEN(ptr) \
+ ((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
+#endif
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#ifndef HAVE_INET_NTOP
+#include "inet_ntop.h"
+#endif
+
+#include "winsock.h"
+
+#include "socket-server.h"
+#include "socket-util.h"
+#include "xmalloc.h"
+#include "util.h"
+#include "log.h"
+
+struct pa_socket_server {
+ int ref;
+ int fd;
+ char *filename;
+ char *tcpwrap_service;
+
+ void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata);
+ void *userdata;
+
+ pa_io_event *io_event;
+ pa_mainloop_api *mainloop;
+ enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type;
+};
+
+static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+ pa_socket_server *s = userdata;
+ pa_iochannel *io;
+ int nfd;
+ assert(s && s->mainloop == mainloop && s->io_event == e && e && fd >= 0 && fd == s->fd);
+
+ pa_socket_server_ref(s);
+
+ if ((nfd = accept(fd, NULL, NULL)) < 0) {
+ pa_log(__FILE__": accept(): %s\n", strerror(errno));
+ goto finish;
+ }
+
+ pa_fd_set_cloexec(nfd, 1);
+
+ if (!s->on_connection) {
+ close(nfd);
+ goto finish;
+ }
+
+#ifdef HAVE_LIBWRAP
+
+ if (s->type == SOCKET_SERVER_IPV4 && s->tcpwrap_service) {
+ struct request_info req;
+
+ request_init(&req, RQ_DAEMON, s->tcpwrap_service, RQ_FILE, nfd, NULL);
+ fromhost(&req);
+ if (!hosts_access(&req)) {
+ pa_log(__FILE__": TCP connection refused by tcpwrap.\n");
+ close(nfd);
+ goto finish;
+ }
+
+ pa_log(__FILE__": TCP connection accepted by tcpwrap.\n");
+ }
+#endif
+
+ /* There should be a check for socket type here */
+ if (s->type == SOCKET_SERVER_IPV4)
+ pa_socket_tcp_low_delay(fd);
+ else
+ pa_socket_low_delay(fd);
+
+ io = pa_iochannel_new(s->mainloop, nfd, nfd);
+ assert(io);
+ s->on_connection(s, io, s->userdata);
+
+finish:
+ pa_socket_server_unref(s);
+}
+
+pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
+ pa_socket_server *s;
+ assert(m && fd >= 0);
+
+ s = pa_xmalloc(sizeof(pa_socket_server));
+ s->ref = 1;
+ s->fd = fd;
+ s->filename = NULL;
+ s->on_connection = NULL;
+ s->userdata = NULL;
+ s->tcpwrap_service = NULL;
+
+ s->mainloop = m;
+ s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s);
+ assert(s->io_event);
+
+ s->type = SOCKET_SERVER_GENERIC;
+
+ return s;
+}
+
+pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
+ assert(s && s->ref >= 1);
+ s->ref++;
+ return s;
+}
+
+#ifdef HAVE_SYS_UN_H
+
+pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
+ int fd = -1;
+ struct sockaddr_un sa;
+ pa_socket_server *s;
+
+ assert(m && filename);
+
+ if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ pa_log(__FILE__": socket(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ pa_fd_set_cloexec(fd, 1);
+
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
+ sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+
+ pa_socket_low_delay(fd);
+
+ if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) {
+ pa_log(__FILE__": bind(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (listen(fd, 5) < 0) {
+ pa_log(__FILE__": listen(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ s = pa_socket_server_new(m, fd);
+ assert(s);
+
+ s->filename = pa_xstrdup(filename);
+ s->type = SOCKET_SERVER_UNIX;
+
+ return s;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return NULL;
+}
+
+#else /* HAVE_SYS_UN_H */
+
+pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
+ return NULL;
+}
+
+#endif /* HAVE_SYS_UN_H */
+
+pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service) {
+ pa_socket_server *ss;
+ int fd = -1;
+ struct sockaddr_in sa;
+ int on = 1;
+
+ assert(m && port);
+
+ if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ pa_log(__FILE__": socket(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ pa_fd_set_cloexec(fd, 1);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
+ pa_log(__FILE__": setsockopt(): %s\n", strerror(errno));
+
+ pa_socket_tcp_low_delay(fd);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(port);
+ sa.sin_addr.s_addr = htonl(address);
+
+ if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ pa_log(__FILE__": bind(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (listen(fd, 5) < 0) {
+ pa_log(__FILE__": listen(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if ((ss = pa_socket_server_new(m, fd))) {
+ ss->type = SOCKET_SERVER_IPV4;
+ ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
+ }
+
+ return ss;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return NULL;
+}
+
+pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port) {
+ pa_socket_server *ss;
+ int fd = -1;
+ struct sockaddr_in6 sa;
+ int on = 1;
+
+ assert(m && port);
+
+ if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
+ pa_log(__FILE__": socket(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ pa_fd_set_cloexec(fd, 1);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
+ pa_log(__FILE__": setsockopt(): %s\n", strerror(errno));
+
+ pa_socket_tcp_low_delay(fd);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(port);
+ memcpy(sa.sin6_addr.s6_addr, address, 16);
+
+ if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ pa_log(__FILE__": bind(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (listen(fd, 5) < 0) {
+ pa_log(__FILE__": listen(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if ((ss = pa_socket_server_new(m, fd)))
+ ss->type = SOCKET_SERVER_IPV6;
+
+ return ss;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return NULL;
+}
+
+static void socket_server_free(pa_socket_server*s) {
+ assert(s);
+ close(s->fd);
+
+ if (s->filename) {
+ unlink(s->filename);
+ pa_xfree(s->filename);
+ }
+
+ pa_xfree(s->tcpwrap_service);
+
+ s->mainloop->io_free(s->io_event);
+ pa_xfree(s);
+}
+
+void pa_socket_server_unref(pa_socket_server *s) {
+ assert(s && s->ref >= 1);
+
+ if (!(--(s->ref)))
+ socket_server_free(s);
+}
+
+void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata) {
+ assert(s && s->ref >= 1);
+
+ s->on_connection = on_connection;
+ s->userdata = userdata;
+}
+
+
+char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
+ assert(s && c && l > 0);
+
+ switch (s->type) {
+ case SOCKET_SERVER_IPV6: {
+ struct sockaddr_in6 sa;
+ socklen_t sa_len = sizeof(sa);
+
+ if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
+ pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) {
+ char fqdn[256];
+ if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
+ return NULL;
+
+ snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
+
+ } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
+ char hn[256];
+ if (!pa_get_host_name(hn, sizeof(hn)))
+ return NULL;
+
+ snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
+ } else {
+ char ip[INET6_ADDRSTRLEN];
+
+ if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) {
+ pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
+ }
+
+ return c;
+ }
+
+ case SOCKET_SERVER_IPV4: {
+ struct sockaddr_in sa;
+ socklen_t sa_len = sizeof(sa);
+
+ if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
+ pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ if (sa.sin_addr.s_addr == INADDR_ANY) {
+ char fqdn[256];
+ if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
+ return NULL;
+
+ snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
+ } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
+ char hn[256];
+ if (!pa_get_host_name(hn, sizeof(hn)))
+ return NULL;
+
+ snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
+ } else {
+ char ip[INET_ADDRSTRLEN];
+
+ if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) {
+ pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
+
+ }
+
+ return c;
+ }
+
+ case SOCKET_SERVER_UNIX: {
+ char hn[256];
+
+ if (!s->filename)
+ return NULL;
+
+ if (!pa_get_host_name(hn, sizeof(hn)))
+ return NULL;
+
+ snprintf(c, l, "{%s}unix:%s", hn, s->filename);
+ return c;
+ }
+
+ default:
+ return NULL;
+ }
+}
diff --git a/src/polypcore/socket-server.h b/src/polypcore/socket-server.h
new file mode 100644
index 00000000..4d8bfad5
--- /dev/null
+++ b/src/polypcore/socket-server.h
@@ -0,0 +1,45 @@
+#ifndef foosocketserverhfoo
+#define foosocketserverhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <polyp/mainloop-api.h>
+#include "iochannel.h"
+
+/* It is safe to destroy the calling socket_server object from the callback */
+
+typedef struct pa_socket_server pa_socket_server;
+
+pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd);
+pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename);
+pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port);
+
+void pa_socket_server_unref(pa_socket_server*s);
+pa_socket_server* pa_socket_server_ref(pa_socket_server *s);
+
+void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata);
+
+char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l);
+
+#endif
diff --git a/src/polypcore/socket-util.c b/src/polypcore/socket-util.c
new file mode 100644
index 00000000..032a3c91
--- /dev/null
+++ b/src/polypcore/socket-util.c
@@ -0,0 +1,246 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdarg.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include "winsock.h"
+
+#include "socket-util.h"
+#include "util.h"
+#include "xmalloc.h"
+#include "log.h"
+
+void pa_socket_peer_to_string(int fd, char *c, size_t l) {
+ struct stat st;
+
+ assert(c && l && fd >= 0);
+
+#ifndef OS_IS_WIN32
+ if (fstat(fd, &st) < 0) {
+ snprintf(c, l, "Invalid client fd");
+ return;
+ }
+#endif
+
+#ifndef OS_IS_WIN32
+ if (S_ISSOCK(st.st_mode)) {
+#endif
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+#ifdef HAVE_SYS_UN_H
+ struct sockaddr_un un;
+#endif
+ } sa;
+ socklen_t sa_len = sizeof(sa);
+
+ if (getpeername(fd, &sa.sa, &sa_len) >= 0) {
+
+ if (sa.sa.sa_family == AF_INET) {
+ uint32_t ip = ntohl(sa.in.sin_addr.s_addr);
+
+ snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u",
+ ip >> 24,
+ (ip >> 16) & 0xFF,
+ (ip >> 8) & 0xFF,
+ ip & 0xFF,
+ ntohs(sa.in.sin_port));
+ return;
+#ifdef HAVE_SYS_UN_H
+ } else if (sa.sa.sa_family == AF_UNIX) {
+ snprintf(c, l, "UNIX socket client");
+ return;
+#endif
+ }
+
+ }
+#ifndef OS_IS_WIN32
+ snprintf(c, l, "Unknown network client");
+ return;
+ } else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) {
+ snprintf(c, l, "STDIN/STDOUT client");
+ return;
+ }
+#endif /* OS_IS_WIN32 */
+
+ snprintf(c, l, "Unknown client");
+}
+
+int pa_socket_low_delay(int fd) {
+#ifdef SO_PRIORITY
+ int priority;
+ assert(fd >= 0);
+
+ priority = 7;
+ if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void*)&priority, sizeof(priority)) < 0)
+ return -1;
+#endif
+
+ return 0;
+}
+
+int pa_socket_tcp_low_delay(int fd) {
+ int ret, tos, on;
+
+ assert(fd >= 0);
+
+ ret = pa_socket_low_delay(fd);
+
+ on = 1;
+ tos = 0;
+
+#if defined(SOL_TCP) || defined(IPPROTO_TCP)
+#if defined(SOL_TCP)
+ if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
+#else
+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
+#endif
+ ret = -1;
+#endif
+
+#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || \
+ defined(IPPROTO_IP))
+ tos = IPTOS_LOWDELAY;
+#ifdef SOL_IP
+ if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+#else
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+#endif
+ ret = -1;
+#endif
+
+ return ret;
+
+}
+
+int pa_socket_set_rcvbuf(int fd, size_t l) {
+ assert(fd >= 0);
+
+/* if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) { */
+/* pa_log(__FILE__": SO_RCVBUF: %s\n", strerror(errno)); */
+/* return -1; */
+/* } */
+
+ return 0;
+}
+
+int pa_socket_set_sndbuf(int fd, size_t l) {
+ assert(fd >= 0);
+
+/* if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) { */
+/* pa_log(__FILE__": SO_SNDBUF: %s\n", strerror(errno)); */
+/* return -1; */
+/* } */
+
+ return 0;
+}
+
+#ifdef HAVE_SYS_UN_H
+
+int pa_unix_socket_is_stale(const char *fn) {
+ struct sockaddr_un sa;
+ int fd = -1, ret = -1;
+
+ if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ pa_log(__FILE__": socket(): %s\n", strerror(errno));
+ goto finish;
+ }
+
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, fn, sizeof(sa.sun_path)-1);
+ sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+
+ if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
+ if (errno == ECONNREFUSED)
+ ret = 1;
+ } else
+ ret = 0;
+
+finish:
+ if (fd >= 0)
+ close(fd);
+
+ return ret;
+}
+
+int pa_unix_socket_remove_stale(const char *fn) {
+ int r;
+
+ if ((r = pa_unix_socket_is_stale(fn)) < 0)
+ return errno != ENOENT ? -1 : 0;
+
+ if (!r)
+ return 0;
+
+ /* Yes, here is a race condition. But who cares? */
+ if (unlink(fn) < 0)
+ return -1;
+
+ return 0;
+}
+
+#else /* HAVE_SYS_UN_H */
+
+int pa_unix_socket_is_stale(const char *fn) {
+ return -1;
+}
+
+int pa_unix_socket_remove_stale(const char *fn) {
+ return -1;
+}
+
+#endif /* HAVE_SYS_UN_H */
diff --git a/src/polypcore/socket-util.h b/src/polypcore/socket-util.h
new file mode 100644
index 00000000..ae16fb16
--- /dev/null
+++ b/src/polypcore/socket-util.h
@@ -0,0 +1,38 @@
+#ifndef foosocketutilhfoo
+#define foosocketutilhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+void pa_socket_peer_to_string(int fd, char *c, size_t l);
+
+int pa_socket_low_delay(int fd);
+int pa_socket_tcp_low_delay(int fd);
+
+int pa_socket_set_sndbuf(int fd, size_t l);
+int pa_socket_set_rcvbuf(int fd, size_t l);
+
+int pa_unix_socket_is_stale(const char *fn);
+int pa_unix_socket_remove_stale(const char *fn);
+
+#endif
diff --git a/src/polypcore/sound-file-stream.c b/src/polypcore/sound-file-stream.c
new file mode 100644
index 00000000..881e3077
--- /dev/null
+++ b/src/polypcore/sound-file-stream.c
@@ -0,0 +1,179 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sndfile.h>
+
+#include "sound-file-stream.h"
+#include "sink-input.h"
+#include "xmalloc.h"
+#include "log.h"
+
+#define BUF_SIZE (1024*10)
+
+struct userdata {
+ SNDFILE *sndfile;
+ pa_sink_input *sink_input;
+ pa_memchunk memchunk;
+ sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+};
+
+static void free_userdata(struct userdata *u) {
+ assert(u);
+ if (u->sink_input) {
+ pa_sink_input_disconnect(u->sink_input);
+ pa_sink_input_unref(u->sink_input);
+ }
+
+ if (u->memchunk.memblock)
+ pa_memblock_unref(u->memchunk.memblock);
+ if (u->sndfile)
+ sf_close(u->sndfile);
+
+ pa_xfree(u);
+}
+
+static void sink_input_kill(pa_sink_input *i) {
+ assert(i && i->userdata);
+ free_userdata(i->userdata);
+}
+
+static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) {
+ sink_input_kill(i);
+}
+
+static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
+ struct userdata *u;
+ assert(i && chunk && i->userdata);
+ u = i->userdata;
+
+ if (!u->memchunk.memblock) {
+ uint32_t fs = pa_frame_size(&i->sample_spec);
+ sf_count_t samples = BUF_SIZE/fs;
+
+ u->memchunk.memblock = pa_memblock_new(BUF_SIZE, i->sink->core->memblock_stat);
+ u->memchunk.index = 0;
+ samples = u->readf_function(u->sndfile, u->memchunk.memblock->data, samples);
+ u->memchunk.length = samples*fs;
+
+ if (!u->memchunk.length) {
+ pa_memblock_unref(u->memchunk.memblock);
+ u->memchunk.memblock = NULL;
+ u->memchunk.index = u->memchunk.length = 0;
+ pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i);
+ return -1;
+ }
+ }
+
+ *chunk = u->memchunk;
+ pa_memblock_ref(chunk->memblock);
+ assert(chunk->length);
+ return 0;
+}
+
+static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) {
+ struct userdata *u;
+ assert(i && chunk && length && i->userdata);
+ u = i->userdata;
+
+ assert(!memcmp(chunk, &u->memchunk, sizeof(chunk)));
+ assert(length <= u->memchunk.length);
+
+ u->memchunk.index += length;
+ u->memchunk.length -= length;
+
+ if (u->memchunk.length <= 0) {
+ pa_memblock_unref(u->memchunk.memblock);
+ u->memchunk.memblock = NULL;
+ u->memchunk.index = u->memchunk.length = 0;
+ }
+}
+
+int pa_play_file(pa_sink *sink, const char *fname, const pa_cvolume *volume) {
+ struct userdata *u = NULL;
+ SF_INFO sfinfo;
+ pa_sample_spec ss;
+ assert(sink && fname);
+
+ u = pa_xmalloc(sizeof(struct userdata));
+ u->sink_input = NULL;
+ u->memchunk.memblock = NULL;
+ u->memchunk.index = u->memchunk.length = 0;
+ u->sndfile = NULL;
+
+ memset(&sfinfo, 0, sizeof(sfinfo));
+
+ if (!(u->sndfile = sf_open(fname, SFM_READ, &sfinfo))) {
+ pa_log(__FILE__": Failed to open file %s\n", fname);
+ goto fail;
+ }
+
+ switch (sfinfo.format & 0xFF) {
+ case SF_FORMAT_PCM_16:
+ case SF_FORMAT_PCM_U8:
+ case SF_FORMAT_ULAW:
+ case SF_FORMAT_ALAW:
+ ss.format = PA_SAMPLE_S16NE;
+ u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
+ break;
+ case SF_FORMAT_FLOAT:
+ default:
+ ss.format = PA_SAMPLE_FLOAT32NE;
+ u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
+ break;
+ }
+
+ ss.rate = sfinfo.samplerate;
+ ss.channels = sfinfo.channels;
+
+ if (!pa_sample_spec_valid(&ss)) {
+ pa_log(__FILE__": Unsupported sample format in file %s\n", fname);
+ goto fail;
+ }
+
+ if (!(u->sink_input = pa_sink_input_new(sink, __FILE__, fname, &ss, NULL, 0, -1)))
+ goto fail;
+
+ if (volume)
+ u->sink_input->volume = *volume;
+ u->sink_input->peek = sink_input_peek;
+ u->sink_input->drop = sink_input_drop;
+ u->sink_input->kill = sink_input_kill;
+ u->sink_input->userdata = u;
+
+ pa_sink_notify(sink);
+
+ return 0;
+
+fail:
+ if (u)
+ free_userdata(u);
+
+ return -1;
+}
diff --git a/src/polypcore/sound-file-stream.h b/src/polypcore/sound-file-stream.h
new file mode 100644
index 00000000..2e56ef49
--- /dev/null
+++ b/src/polypcore/sound-file-stream.h
@@ -0,0 +1,29 @@
+#ifndef foosoundfilestreamhfoo
+#define foosoundfilestreamhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "sink.h"
+
+int pa_play_file(pa_sink *sink, const char *fname, const pa_cvolume *volume);
+
+#endif
diff --git a/src/polypcore/sound-file.c b/src/polypcore/sound-file.c
new file mode 100644
index 00000000..a81c283a
--- /dev/null
+++ b/src/polypcore/sound-file.c
@@ -0,0 +1,139 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+
+#include <sndfile.h>
+
+#include "sound-file.h"
+#include <polyp/sample.h>
+#include "log.h"
+
+#define MAX_FILE_SIZE (1024*1024)
+
+int pa_sound_file_load(const char *fname, pa_sample_spec *ss, pa_memchunk *chunk, pa_memblock_stat *s) {
+ SNDFILE*sf = NULL;
+ SF_INFO sfinfo;
+ int ret = -1;
+ size_t l;
+ sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+ assert(fname && ss && chunk);
+
+ chunk->memblock = NULL;
+ chunk->index = chunk->length = 0;
+
+ memset(&sfinfo, 0, sizeof(sfinfo));
+
+ if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
+ pa_log(__FILE__": Failed to open file %s\n", fname);
+ goto finish;
+ }
+
+ switch (sfinfo.format & SF_FORMAT_SUBMASK) {
+ case SF_FORMAT_FLOAT:
+ case SF_FORMAT_DOUBLE:
+ /* Only float and double need a special case. */
+ ss->format = PA_SAMPLE_FLOAT32NE;
+ readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
+ break;
+ default:
+ /* Everything else is cleanly converted to signed 16 bit. */
+ ss->format = PA_SAMPLE_S16NE;
+ readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
+ break;
+ }
+
+ ss->rate = sfinfo.samplerate;
+ ss->channels = sfinfo.channels;
+
+ if (!pa_sample_spec_valid(ss)) {
+ pa_log(__FILE__": Unsupported sample format in file %s\n", fname);
+ goto finish;
+ }
+
+ if ((l = pa_frame_size(ss)*sfinfo.frames) > MAX_FILE_SIZE) {
+ pa_log(__FILE__": File too large\n");
+ goto finish;
+ }
+
+ chunk->memblock = pa_memblock_new(l, s);
+ assert(chunk->memblock);
+ chunk->index = 0;
+ chunk->length = l;
+
+ if (readf_function(sf, chunk->memblock->data, sfinfo.frames) != sfinfo.frames) {
+ pa_log(__FILE__": Premature file end\n");
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+
+ if (sf)
+ sf_close(sf);
+
+ if (ret != 0 && chunk->memblock)
+ pa_memblock_unref(chunk->memblock);
+
+ return ret;
+
+}
+
+int pa_sound_file_too_big_to_cache(const char *fname) {
+ SNDFILE*sf = NULL;
+ SF_INFO sfinfo;
+ pa_sample_spec ss;
+
+ if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
+ pa_log(__FILE__": Failed to open file %s\n", fname);
+ return 0;
+ }
+
+ sf_close(sf);
+
+ switch (sfinfo.format & SF_FORMAT_SUBMASK) {
+ case SF_FORMAT_FLOAT:
+ case SF_FORMAT_DOUBLE:
+ /* Only float and double need a special case. */
+ ss.format = PA_SAMPLE_FLOAT32NE;
+ break;
+ default:
+ /* Everything else is cleanly converted to signed 16 bit. */
+ ss.format = PA_SAMPLE_S16NE;
+ break;
+ }
+
+ ss.rate = sfinfo.samplerate;
+ ss.channels = sfinfo.channels;
+
+ if ((pa_frame_size(&ss) * sfinfo.frames) > MAX_FILE_SIZE) {
+ pa_log(__FILE__": File too large %s\n", fname);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/polypcore/sound-file.h b/src/polypcore/sound-file.h
new file mode 100644
index 00000000..f3003bb9
--- /dev/null
+++ b/src/polypcore/sound-file.h
@@ -0,0 +1,32 @@
+#ifndef soundfilehfoo
+#define soundfilehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "memchunk.h"
+#include <polyp/sample.h>
+
+int pa_sound_file_load(const char *fname, pa_sample_spec *ss, pa_memchunk *chunk, pa_memblock_stat *s);
+
+int pa_sound_file_too_big_to_cache(const char *fname);
+
+#endif
diff --git a/src/polypcore/source-output.c b/src/polypcore/source-output.c
new file mode 100644
index 00000000..e1d8ccf7
--- /dev/null
+++ b/src/polypcore/source-output.c
@@ -0,0 +1,228 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "source-output.h"
+#include "xmalloc.h"
+#include "subscribe.h"
+#include "log.h"
+
+pa_source_output* pa_source_output_new(
+ pa_source *s,
+ const char *driver,
+ const char *name,
+ const pa_sample_spec *spec,
+ const pa_channel_map *map,
+ int resample_method) {
+
+ pa_source_output *o;
+ pa_resampler *resampler = NULL;
+ int r;
+ char st[256];
+ pa_channel_map tmap;
+
+ assert(s);
+ assert(spec);
+ assert(s->state == PA_SOURCE_RUNNING);
+
+ if (pa_idxset_size(s->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
+ pa_log(__FILE__": Failed to create source output: too many outputs per source.\n");
+ return NULL;
+ }
+
+ if (resample_method == PA_RESAMPLER_INVALID)
+ resample_method = s->core->resample_method;
+
+ if (!map) {
+ pa_channel_map_init_auto(&tmap, spec->channels);
+ map = &tmap;
+ }
+
+ if (!pa_sample_spec_equal(&s->sample_spec, spec) || !pa_channel_map_equal(&s->channel_map, map))
+ if (!(resampler = pa_resampler_new(&s->sample_spec, &s->channel_map, spec, map, s->core->memblock_stat, resample_method)))
+ return NULL;
+
+ o = pa_xmalloc(sizeof(pa_source_output));
+ o->ref = 1;
+ o->state = PA_SOURCE_OUTPUT_RUNNING;
+ o->name = pa_xstrdup(name);
+ o->driver = pa_xstrdup(driver);
+ o->owner = NULL;
+ o->source = s;
+ o->client = NULL;
+
+ o->sample_spec = *spec;
+ o->channel_map = *map;
+
+ o->push = NULL;
+ o->kill = NULL;
+ o->get_latency = NULL;
+ o->userdata = NULL;
+
+ o->resampler = resampler;
+
+ assert(s->core);
+ r = pa_idxset_put(s->core->source_outputs, o, &o->index);
+ assert(r == 0 && o->index != PA_IDXSET_INVALID);
+ r = pa_idxset_put(s->outputs, o, NULL);
+ assert(r == 0);
+
+ pa_sample_spec_snprint(st, sizeof(st), spec);
+ pa_log_info(__FILE__": created %u \"%s\" on %u with sample spec \"%s\"\n", o->index, o->name, s->index, st);
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
+
+ return o;
+}
+
+void pa_source_output_disconnect(pa_source_output*o) {
+ assert(o);
+ assert(o->state != PA_SOURCE_OUTPUT_DISCONNECTED);
+ assert(o->source);
+ assert(o->source->core);
+
+ pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL);
+ pa_idxset_remove_by_data(o->source->outputs, o, NULL);
+
+ pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
+ o->source = NULL;
+
+ o->push = NULL;
+ o->kill = NULL;
+ o->get_latency = NULL;
+
+ o->state = PA_SOURCE_OUTPUT_DISCONNECTED;
+}
+
+static void source_output_free(pa_source_output* o) {
+ assert(o);
+
+ if (o->state != PA_SOURCE_OUTPUT_DISCONNECTED)
+ pa_source_output_disconnect(o);
+
+ pa_log_info(__FILE__": freed %u \"%s\"\n", o->index, o->name);
+
+ if (o->resampler)
+ pa_resampler_free(o->resampler);
+
+ pa_xfree(o->name);
+ pa_xfree(o->driver);
+ pa_xfree(o);
+}
+
+
+void pa_source_output_unref(pa_source_output* o) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!(--o->ref))
+ source_output_free(o);
+}
+
+pa_source_output* pa_source_output_ref(pa_source_output *o) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ o->ref++;
+ return o;
+}
+
+
+void pa_source_output_kill(pa_source_output*o) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (o->kill)
+ o->kill(o);
+}
+
+void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
+ pa_memchunk rchunk;
+
+ assert(o);
+ assert(chunk);
+ assert(chunk->length);
+ assert(o->push);
+
+ if (o->state == PA_SOURCE_OUTPUT_CORKED)
+ return;
+
+ if (!o->resampler) {
+ o->push(o, chunk);
+ return;
+ }
+
+ pa_resampler_run(o->resampler, chunk, &rchunk);
+ if (!rchunk.length)
+ return;
+
+ assert(rchunk.memblock);
+ o->push(o, &rchunk);
+ pa_memblock_unref(rchunk.memblock);
+}
+
+void pa_source_output_set_name(pa_source_output *o, const char *name) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ pa_xfree(o->name);
+ o->name = pa_xstrdup(name);
+
+ pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+}
+
+pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (o->get_latency)
+ return o->get_latency(o);
+
+ return 0;
+}
+
+void pa_source_output_cork(pa_source_output *o, int b) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (o->state == PA_SOURCE_OUTPUT_DISCONNECTED)
+ return;
+
+ o->state = b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
+}
+
+pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
+ assert(o);
+ assert(o->ref >= 1);
+
+ if (!o->resampler)
+ return PA_RESAMPLER_INVALID;
+
+ return pa_resampler_get_method(o->resampler);
+}
diff --git a/src/polypcore/source-output.h b/src/polypcore/source-output.h
new file mode 100644
index 00000000..4b1cbf15
--- /dev/null
+++ b/src/polypcore/source-output.h
@@ -0,0 +1,92 @@
+#ifndef foosourceoutputhfoo
+#define foosourceoutputhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+
+typedef struct pa_source_output pa_source_output;
+
+#include "source.h"
+#include <polyp/sample.h>
+#include "memblockq.h"
+#include "resampler.h"
+#include "module.h"
+#include "client.h"
+
+typedef enum {
+ PA_SOURCE_OUTPUT_RUNNING,
+ PA_SOURCE_OUTPUT_CORKED,
+ PA_SOURCE_OUTPUT_DISCONNECTED
+} pa_source_output_state_t;
+
+struct pa_source_output {
+ int ref;
+ uint32_t index;
+ pa_source_output_state_t state;
+
+ char *name, *driver;
+ pa_module *owner;
+
+ pa_source *source;
+ pa_client *client;
+
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+
+ void (*push)(pa_source_output *o, const pa_memchunk *chunk);
+ void (*kill)(pa_source_output* o);
+ pa_usec_t (*get_latency) (pa_source_output *i);
+
+ pa_resampler* resampler;
+
+ void *userdata;
+};
+
+pa_source_output* pa_source_output_new(
+ pa_source *s,
+ const char *driver,
+ const char *name,
+ const pa_sample_spec *spec,
+ const pa_channel_map *map,
+ int resample_method);
+
+void pa_source_output_unref(pa_source_output* o);
+pa_source_output* pa_source_output_ref(pa_source_output *o);
+
+/* To be called by the implementing module only */
+void pa_source_output_disconnect(pa_source_output*o);
+
+/* External code may request disconnection with this funcion */
+void pa_source_output_kill(pa_source_output*o);
+
+void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk);
+
+void pa_source_output_set_name(pa_source_output *i, const char *name);
+
+pa_usec_t pa_source_output_get_latency(pa_source_output *i);
+
+void pa_source_output_cork(pa_source_output *i, int b);
+
+pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
+
+#endif
diff --git a/src/polypcore/source.c b/src/polypcore/source.c
new file mode 100644
index 00000000..6e377b20
--- /dev/null
+++ b/src/polypcore/source.c
@@ -0,0 +1,195 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "source.h"
+#include "source-output.h"
+#include "namereg.h"
+#include "xmalloc.h"
+#include "subscribe.h"
+#include "log.h"
+
+pa_source* pa_source_new(
+ pa_core *core,
+ const char *driver,
+ const char *name,
+ int fail,
+ const pa_sample_spec *spec,
+ const pa_channel_map *map) {
+
+ pa_source *s;
+ char st[256];
+ int r;
+
+ assert(core);
+ assert(name);
+ assert(*name);
+ assert(spec);
+
+ s = pa_xnew(pa_source, 1);
+
+ if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) {
+ pa_xfree(s);
+ return NULL;
+ }
+
+ s->ref = 1;
+ s->core = core;
+ s->state = PA_SOURCE_RUNNING;
+ s->name = pa_xstrdup(name);
+ s->description = NULL;
+ s->driver = pa_xstrdup(driver);
+ s->owner = NULL;
+
+ s->sample_spec = *spec;
+ if (map)
+ s->channel_map = *map;
+ else
+ pa_channel_map_init_auto(&s->channel_map, spec->channels);
+
+ s->outputs = pa_idxset_new(NULL, NULL);
+ s->monitor_of = NULL;
+
+ s->get_latency = NULL;
+ s->notify = NULL;
+ s->userdata = NULL;
+
+ r = pa_idxset_put(core->sources, s, &s->index);
+ assert(s->index != PA_IDXSET_INVALID && r >= 0);
+
+ pa_sample_spec_snprint(st, sizeof(st), spec);
+ pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st);
+
+ pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
+
+ return s;
+}
+
+void pa_source_disconnect(pa_source *s) {
+ pa_source_output *o, *j = NULL;
+
+ assert(s);
+ assert(s->state == PA_SOURCE_RUNNING);
+
+ pa_namereg_unregister(s->core, s->name);
+
+ while ((o = pa_idxset_first(s->outputs, NULL))) {
+ assert(o != j);
+ pa_source_output_kill(o);
+ j = o;
+ }
+
+ pa_idxset_remove_by_data(s->core->sources, s, NULL);
+
+ s->get_latency = NULL;
+ s->notify = NULL;
+
+ s->state = PA_SOURCE_DISCONNECTED;
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+}
+
+static void source_free(pa_source *s) {
+ assert(s);
+ assert(!s->ref);
+
+ if (s->state != PA_SOURCE_DISCONNECTED)
+ pa_source_disconnect(s);
+
+ pa_log_info(__FILE__": freed %u \"%s\"\n", s->index, s->name);
+
+ pa_idxset_free(s->outputs, NULL, NULL);
+
+ pa_xfree(s->name);
+ pa_xfree(s->description);
+ pa_xfree(s->driver);
+ pa_xfree(s);
+}
+
+void pa_source_unref(pa_source *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ if (!(--s->ref))
+ source_free(s);
+}
+
+pa_source* pa_source_ref(pa_source *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->ref++;
+ return s;
+}
+
+void pa_source_notify(pa_source*s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ if (s->notify)
+ s->notify(s);
+}
+
+static int do_post(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void*userdata) {
+ pa_source_output *o = p;
+ const pa_memchunk *chunk = userdata;
+
+ assert(o);
+ assert(chunk);
+
+ pa_source_output_push(o, chunk);
+ return 0;
+}
+
+void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
+ assert(s);
+ assert(s->ref >= 1);
+ assert(chunk);
+
+ pa_source_ref(s);
+ pa_idxset_foreach(s->outputs, do_post, (void*) chunk);
+ pa_source_unref(s);
+}
+
+void pa_source_set_owner(pa_source *s, pa_module *m) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ s->owner = m;
+}
+
+pa_usec_t pa_source_get_latency(pa_source *s) {
+ assert(s);
+ assert(s->ref >= 1);
+
+ if (!s->get_latency)
+ return 0;
+
+ return s->get_latency(s);
+}
+
diff --git a/src/polypcore/source.h b/src/polypcore/source.h
new file mode 100644
index 00000000..be2fb985
--- /dev/null
+++ b/src/polypcore/source.h
@@ -0,0 +1,86 @@
+#ifndef foosourcehfoo
+#define foosourcehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct pa_source pa_source;
+
+#include <inttypes.h>
+#include "core.h"
+#include <polyp/sample.h>
+#include "idxset.h"
+#include "memblock.h"
+#include "memchunk.h"
+#include "sink.h"
+#include <polyp/channelmap.h>
+#include "module.h"
+
+#define PA_MAX_OUTPUTS_PER_SOURCE 16
+
+typedef enum pa_source_state {
+ PA_SOURCE_RUNNING,
+ PA_SOURCE_DISCONNECTED
+} pa_source_state_t;
+
+struct pa_source {
+ int ref;
+ uint32_t index;
+ pa_core *core;
+ pa_source_state_t state;
+
+ char *name, *description, *driver;
+ pa_module *owner;
+
+ pa_sample_spec sample_spec;
+ pa_channel_map channel_map;
+
+ pa_idxset *outputs;
+ pa_sink *monitor_of;
+
+ void (*notify)(pa_source*source);
+ pa_usec_t (*get_latency)(pa_source *s);
+
+ void *userdata;
+};
+
+pa_source* pa_source_new(
+ pa_core *core,
+ const char *driver,
+ const char *name,
+ int namereg_fail,
+ const pa_sample_spec *spec,
+ const pa_channel_map *map);
+
+void pa_source_disconnect(pa_source *s);
+void pa_source_unref(pa_source *s);
+pa_source* pa_source_ref(pa_source *c);
+
+/* Pass a new memory block to all output streams */
+void pa_source_post(pa_source*s, const pa_memchunk *b);
+
+void pa_source_notify(pa_source *s);
+
+void pa_source_set_owner(pa_source *s, pa_module *m);
+
+pa_usec_t pa_source_get_latency(pa_source *s);
+
+#endif
diff --git a/src/polypcore/strbuf.c b/src/polypcore/strbuf.c
new file mode 100644
index 00000000..dcad5e78
--- /dev/null
+++ b/src/polypcore/strbuf.c
@@ -0,0 +1,167 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <sys/types.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <polypcore/xmalloc.h>
+
+#include "strbuf.h"
+
+/* A chunk of the linked list that makes up the string */
+struct chunk {
+ struct chunk *next;
+ size_t length;
+};
+
+#define CHUNK_TO_TEXT(c) ((char*) (c) + sizeof(struct chunk))
+
+struct pa_strbuf {
+ size_t length;
+ struct chunk *head, *tail;
+};
+
+pa_strbuf *pa_strbuf_new(void) {
+ pa_strbuf *sb = pa_xmalloc(sizeof(pa_strbuf));
+ sb->length = 0;
+ sb->head = sb->tail = NULL;
+ return sb;
+}
+
+void pa_strbuf_free(pa_strbuf *sb) {
+ assert(sb);
+ while (sb->head) {
+ struct chunk *c = sb->head;
+ sb->head = sb->head->next;
+ pa_xfree(c);
+ }
+
+ pa_xfree(sb);
+}
+
+/* Make a C string from the string buffer. The caller has to free
+ * string with pa_xfree(). */
+char *pa_strbuf_tostring(pa_strbuf *sb) {
+ char *t, *e;
+ struct chunk *c;
+ assert(sb);
+
+ e = t = pa_xmalloc(sb->length+1);
+
+ for (c = sb->head; c; c = c->next) {
+ assert((size_t) (e-t) <= sb->length);
+ memcpy(e, CHUNK_TO_TEXT(c), c->length);
+ e += c->length;
+ }
+
+ /* Trailing NUL */
+ *e = 0;
+
+ assert(e == t+sb->length);
+
+ return t;
+}
+
+/* Combination of pa_strbuf_free() and pa_strbuf_tostring() */
+char *pa_strbuf_tostring_free(pa_strbuf *sb) {
+ char *t;
+ assert(sb);
+ t = pa_strbuf_tostring(sb);
+ pa_strbuf_free(sb);
+ return t;
+}
+
+/* Append a string to the string buffer */
+void pa_strbuf_puts(pa_strbuf *sb, const char *t) {
+ assert(sb && t);
+ pa_strbuf_putsn(sb, t, strlen(t));
+}
+
+/* Append a new chunk to the linked list */
+static void append(pa_strbuf *sb, struct chunk *c) {
+ assert(sb && c);
+
+ if (sb->tail) {
+ assert(sb->head);
+ sb->tail->next = c;
+ } else {
+ assert(!sb->head);
+ sb->head = c;
+ }
+
+ sb->tail = c;
+ sb->length += c->length;
+ c->next = NULL;
+}
+
+/* Append up to l bytes of a string to the string buffer */
+void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {
+ struct chunk *c;
+ assert(sb && t);
+
+ if (!l)
+ return;
+
+ c = pa_xmalloc(sizeof(struct chunk)+l);
+ c->length = l;
+ memcpy(CHUNK_TO_TEXT(c), t, l);
+
+ append(sb, c);
+}
+
+/* Append a printf() style formatted string to the string buffer. */
+/* The following is based on an example from the GNU libc documentation */
+int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
+ int size = 100;
+ struct chunk *c = NULL;
+
+ assert(sb);
+
+ for(;;) {
+ va_list ap;
+ int r;
+
+ c = pa_xrealloc(c, sizeof(struct chunk)+size);
+
+ va_start(ap, format);
+ r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap);
+ va_end(ap);
+
+ if (r > -1 && r < size) {
+ c->length = r;
+ append(sb, c);
+ return r;
+ }
+
+ if (r > -1) /* glibc 2.1 */
+ size = r+1;
+ else /* glibc 2.0 */
+ size *= 2;
+ }
+}
diff --git a/src/polypcore/strbuf.h b/src/polypcore/strbuf.h
new file mode 100644
index 00000000..a88f5190
--- /dev/null
+++ b/src/polypcore/strbuf.h
@@ -0,0 +1,38 @@
+#ifndef foostrbufhfoo
+#define foostrbufhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "gccmacro.h"
+
+typedef struct pa_strbuf pa_strbuf;
+
+pa_strbuf *pa_strbuf_new(void);
+void pa_strbuf_free(pa_strbuf *sb);
+char *pa_strbuf_tostring(pa_strbuf *sb);
+char *pa_strbuf_tostring_free(pa_strbuf *sb);
+
+int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
+void pa_strbuf_puts(pa_strbuf *sb, const char *t);
+void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
+
+#endif
diff --git a/src/polypcore/strlist.c b/src/polypcore/strlist.c
new file mode 100644
index 00000000..09eb0c8a
--- /dev/null
+++ b/src/polypcore/strlist.c
@@ -0,0 +1,137 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+
+#include "strlist.h"
+#include "xmalloc.h"
+#include "strbuf.h"
+#include "util.h"
+
+struct pa_strlist {
+ pa_strlist *next;
+ char *str;
+};
+
+pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) {
+ pa_strlist *n;
+ assert(s);
+ n = pa_xmalloc(sizeof(pa_strlist));
+ n->str = pa_xstrdup(s);
+ n->next = l;
+ return n;
+}
+
+char *pa_strlist_tostring(pa_strlist *l) {
+ int first = 1;
+ pa_strbuf *b;
+
+ b = pa_strbuf_new();
+ for (; l; l = l->next) {
+ if (!first)
+ pa_strbuf_puts(b, " ");
+ first = 0;
+ pa_strbuf_puts(b, l->str);
+ }
+
+ return pa_strbuf_tostring_free(b);
+}
+
+pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s) {
+ pa_strlist *ret = l, *prev = NULL;
+ assert(l && s);
+
+ while (l) {
+ if (!strcmp(l->str, s)) {
+ pa_strlist *n = l->next;
+
+ if (!prev) {
+ assert(ret == l);
+ ret = n;
+ } else
+ prev->next = n;
+
+ pa_xfree(l->str);
+ pa_xfree(l);
+
+ l = n;
+
+ } else {
+ prev = l;
+ l = l->next;
+ }
+ }
+
+ return ret;
+}
+
+void pa_strlist_free(pa_strlist *l) {
+ while (l) {
+ pa_strlist *c = l;
+ l = l->next;
+
+ pa_xfree(c->str);
+ pa_xfree(c);
+ }
+}
+
+pa_strlist* pa_strlist_pop(pa_strlist *l, char **s) {
+ pa_strlist *r;
+ assert(s);
+
+ if (!l) {
+ *s = NULL;
+ return NULL;
+ }
+
+ *s = l->str;
+ r = l->next;
+ pa_xfree(l);
+ return r;
+}
+
+pa_strlist* pa_strlist_parse(const char *s) {
+ pa_strlist *head = NULL, *p = NULL;
+ const char *state = NULL;
+ char *r;
+
+ while ((r = pa_split_spaces(s, &state))) {
+ pa_strlist *n;
+
+ n = pa_xmalloc(sizeof(pa_strlist));
+ n->str = r;
+ n->next = NULL;
+
+ if (p)
+ p->next = n;
+ else
+ head = n;
+
+ p = n;
+ }
+
+ return head;
+}
diff --git a/src/polypcore/strlist.h b/src/polypcore/strlist.h
new file mode 100644
index 00000000..2c54dc74
--- /dev/null
+++ b/src/polypcore/strlist.h
@@ -0,0 +1,47 @@
+#ifndef foostrlisthfoo
+#define foostrlisthfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct pa_strlist pa_strlist;
+
+/* Add the specified server string to the list, return the new linked list head */
+pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s);
+
+/* Remove the specified string from the list, return the new linked list head */
+pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s);
+
+/* Make a whitespace separated string of all server stringes. Returned memory has to be freed with pa_xfree() */
+char *pa_strlist_tostring(pa_strlist *l);
+
+/* Free the entire list */
+void pa_strlist_free(pa_strlist *l);
+
+/* Return the next entry in the list in *string and remove it from
+ * the list. Returns the new list head. The memory *string points to
+ * has to be freed with pa_xfree() */
+pa_strlist* pa_strlist_pop(pa_strlist *l, char **s);
+
+/* Parse a whitespace separated server list */
+pa_strlist* pa_strlist_parse(const char *s);
+
+#endif
diff --git a/src/polypcore/subscribe.c b/src/polypcore/subscribe.c
new file mode 100644
index 00000000..e8b3c841
--- /dev/null
+++ b/src/polypcore/subscribe.c
@@ -0,0 +1,230 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+
+#include "queue.h"
+#include "subscribe.h"
+#include "xmalloc.h"
+#include "log.h"
+
+/* The subscription subsystem may be used to be notified whenever an
+ * entity (sink, source, ...) is created or deleted. Modules may
+ * register a callback function that is called whenever an event
+ * matching a subscription mask happens. The execution of the callback
+ * function is postponed to the next main loop iteration, i.e. is not
+ * called from within the stack frame the entity was created in. */
+
+struct pa_subscription {
+ pa_core *core;
+ int dead;
+ void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata);
+ void *userdata;
+ pa_subscription_mask_t mask;
+
+ pa_subscription *prev, *next;
+};
+
+struct pa_subscription_event {
+ pa_subscription_event_type_t type;
+ uint32_t index;
+};
+
+static void sched_event(pa_core *c);
+
+/* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */
+pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) {
+ pa_subscription *s;
+ assert(c);
+
+ s = pa_xmalloc(sizeof(pa_subscription));
+ s->core = c;
+ s->dead = 0;
+ s->callback = callback;
+ s->userdata = userdata;
+ s->mask = m;
+
+ if ((s->next = c->subscriptions))
+ s->next->prev = s;
+ s->prev = NULL;
+ c->subscriptions = s;
+ return s;
+}
+
+/* Free a subscription object, effectively marking it for deletion */
+void pa_subscription_free(pa_subscription*s) {
+ assert(s && !s->dead);
+ s->dead = 1;
+ sched_event(s->core);
+}
+
+static void free_item(pa_subscription *s) {
+ assert(s && s->core);
+
+ if (s->prev)
+ s->prev->next = s->next;
+ else
+ s->core->subscriptions = s->next;
+
+ if (s->next)
+ s->next->prev = s->prev;
+
+ pa_xfree(s);
+}
+
+/* Free all subscription objects */
+void pa_subscription_free_all(pa_core *c) {
+ pa_subscription_event *e;
+ assert(c);
+
+ while (c->subscriptions)
+ free_item(c->subscriptions);
+
+ if (c->subscription_event_queue) {
+ while ((e = pa_queue_pop(c->subscription_event_queue)))
+ pa_xfree(e);
+
+ pa_queue_free(c->subscription_event_queue, NULL, NULL);
+ c->subscription_event_queue = NULL;
+ }
+
+ if (c->subscription_defer_event) {
+ c->mainloop->defer_free(c->subscription_defer_event);
+ c->subscription_defer_event = NULL;
+ }
+}
+
+/*static void dump_event(pa_subscription_event*e) {
+ switch (e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+ case PA_SUBSCRIPTION_EVENT_SINK:
+ pa_log(__FILE__": SINK_EVENT");
+ break;
+ case PA_SUBSCRIPTION_EVENT_SOURCE:
+ pa_log(__FILE__": SOURCE_EVENT");
+ break;
+ case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+ pa_log(__FILE__": SINK_INPUT_EVENT");
+ break;
+ case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
+ pa_log(__FILE__": SOURCE_OUTPUT_EVENT");
+ break;
+ case PA_SUBSCRIPTION_EVENT_MODULE:
+ pa_log(__FILE__": MODULE_EVENT");
+ break;
+ case PA_SUBSCRIPTION_EVENT_CLIENT:
+ pa_log(__FILE__": CLIENT_EVENT");
+ break;
+ default:
+ pa_log(__FILE__": OTHER");
+ break;
+ }
+
+ switch (e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
+ case PA_SUBSCRIPTION_EVENT_NEW:
+ pa_log(__FILE__": NEW");
+ break;
+ case PA_SUBSCRIPTION_EVENT_CHANGE:
+ pa_log(__FILE__": CHANGE");
+ break;
+ case PA_SUBSCRIPTION_EVENT_REMOVE:
+ pa_log(__FILE__": REMOVE");
+ break;
+ default:
+ pa_log(__FILE__": OTHER");
+ break;
+ }
+
+ pa_log(__FILE__": %u\n", e->index);
+}*/
+
+/* Deferred callback for dispatching subscirption events */
+static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
+ pa_core *c = userdata;
+ pa_subscription *s;
+ assert(c && c->subscription_defer_event == de && c->mainloop == m);
+
+ c->mainloop->defer_enable(c->subscription_defer_event, 0);
+
+
+ /* Dispatch queued events */
+
+ if (c->subscription_event_queue) {
+ pa_subscription_event *e;
+
+ while ((e = pa_queue_pop(c->subscription_event_queue))) {
+
+ for (s = c->subscriptions; s; s = s->next) {
+
+ if (!s->dead && pa_subscription_match_flags(s->mask, e->type))
+ s->callback(c, e->type, e->index, s->userdata);
+ }
+
+ pa_xfree(e);
+ }
+ }
+
+ /* Remove dead subscriptions */
+
+ s = c->subscriptions;
+ while (s) {
+ pa_subscription *n = s->next;
+ if (s->dead)
+ free_item(s);
+ s = n;
+ }
+}
+
+/* Schedule an mainloop event so that a pending subscription event is dispatched */
+static void sched_event(pa_core *c) {
+ assert(c);
+
+ if (!c->subscription_defer_event) {
+ c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c);
+ assert(c->subscription_defer_event);
+ }
+
+ c->mainloop->defer_enable(c->subscription_defer_event, 1);
+}
+
+/* Append a new subscription event to the subscription event queue and schedule a main loop event */
+void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t index) {
+ pa_subscription_event *e;
+ assert(c);
+
+ e = pa_xmalloc(sizeof(pa_subscription_event));
+ e->type = t;
+ e->index = index;
+
+ if (!c->subscription_event_queue) {
+ c->subscription_event_queue = pa_queue_new();
+ assert(c->subscription_event_queue);
+ }
+
+ pa_queue_push(c->subscription_event_queue, e);
+ sched_event(c);
+}
+
+
diff --git a/src/polypcore/subscribe.h b/src/polypcore/subscribe.h
new file mode 100644
index 00000000..625159e3
--- /dev/null
+++ b/src/polypcore/subscribe.h
@@ -0,0 +1,37 @@
+#ifndef foosubscribehfoo
+#define foosubscribehfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct pa_subscription pa_subscription;
+typedef struct pa_subscription_event pa_subscription_event;
+
+#include "core.h"
+#include "native-common.h"
+
+pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata);
+void pa_subscription_free(pa_subscription*s);
+void pa_subscription_free_all(pa_core *c);
+
+void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx);
+
+#endif
diff --git a/src/polypcore/tagstruct.c b/src/polypcore/tagstruct.c
new file mode 100644
index 00000000..676f67de
--- /dev/null
+++ b/src/polypcore/tagstruct.c
@@ -0,0 +1,609 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include "winsock.h"
+
+#include "tagstruct.h"
+#include "xmalloc.h"
+
+
+struct pa_tagstruct {
+ uint8_t *data;
+ size_t length, allocated;
+ size_t rindex;
+
+ int dynamic;
+};
+
+pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
+ pa_tagstruct*t;
+
+ assert(!data || (data && length));
+
+ t = pa_xmalloc(sizeof(pa_tagstruct));
+ t->data = (uint8_t*) data;
+ t->allocated = t->length = data ? length : 0;
+ t->rindex = 0;
+ t->dynamic = !data;
+ return t;
+}
+
+void pa_tagstruct_free(pa_tagstruct*t) {
+ assert(t);
+ if (t->dynamic)
+ pa_xfree(t->data);
+ pa_xfree(t);
+}
+
+uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
+ uint8_t *p;
+ assert(t && t->dynamic && l);
+ p = t->data;
+ *l = t->length;
+ pa_xfree(t);
+ return p;
+}
+
+static void extend(pa_tagstruct*t, size_t l) {
+ assert(t && t->dynamic);
+
+ if (t->length+l <= t->allocated)
+ return;
+
+ t->data = pa_xrealloc(t->data, t->allocated = t->length+l+100);
+}
+
+
+void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
+ size_t l;
+ assert(t);
+ if (s) {
+ l = strlen(s)+2;
+ extend(t, l);
+ t->data[t->length] = PA_TAG_STRING;
+ strcpy((char*) (t->data+t->length+1), s);
+ t->length += l;
+ } else {
+ extend(t, 1);
+ t->data[t->length] = PA_TAG_STRING_NULL;
+ t->length += 1;
+ }
+}
+
+void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
+ assert(t);
+ extend(t, 5);
+ t->data[t->length] = PA_TAG_U32;
+ i = htonl(i);
+ memcpy(t->data+t->length+1, &i, 4);
+ t->length += 5;
+}
+
+void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
+ assert(t);
+ extend(t, 2);
+ t->data[t->length] = PA_TAG_U8;
+ *(t->data+t->length+1) = c;
+ t->length += 2;
+}
+
+void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
+ uint32_t rate;
+ assert(t && ss);
+ extend(t, 7);
+ t->data[t->length] = PA_TAG_SAMPLE_SPEC;
+ t->data[t->length+1] = (uint8_t) ss->format;
+ t->data[t->length+2] = ss->channels;
+ rate = htonl(ss->rate);
+ memcpy(t->data+t->length+3, &rate, 4);
+ t->length += 7;
+}
+
+void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
+ uint32_t tmp;
+ assert(t && p);
+
+ extend(t, 5+length);
+ t->data[t->length] = PA_TAG_ARBITRARY;
+ tmp = htonl(length);
+ memcpy(t->data+t->length+1, &tmp, 4);
+ if (length)
+ memcpy(t->data+t->length+5, p, length);
+ t->length += 5+length;
+}
+
+void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) {
+ assert(t);
+ extend(t, 1);
+ t->data[t->length] = b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE;
+ t->length += 1;
+}
+
+void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
+ uint32_t tmp;
+ assert(t);
+ extend(t, 9);
+ t->data[t->length] = PA_TAG_TIMEVAL;
+ tmp = htonl(tv->tv_sec);
+ memcpy(t->data+t->length+1, &tmp, 4);
+ tmp = htonl(tv->tv_usec);
+ memcpy(t->data+t->length+5, &tmp, 4);
+ t->length += 9;
+}
+
+void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
+ uint32_t tmp;
+ assert(t);
+ extend(t, 9);
+ t->data[t->length] = PA_TAG_USEC;
+ tmp = htonl((uint32_t) (u >> 32));
+ memcpy(t->data+t->length+1, &tmp, 4);
+ tmp = htonl((uint32_t) u);
+ memcpy(t->data+t->length+5, &tmp, 4);
+ t->length += 9;
+}
+
+void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
+ uint32_t tmp;
+ assert(t);
+ extend(t, 9);
+ t->data[t->length] = PA_TAG_U64;
+ tmp = htonl((uint32_t) (u >> 32));
+ memcpy(t->data+t->length+1, &tmp, 4);
+ tmp = htonl((uint32_t) u);
+ memcpy(t->data+t->length+5, &tmp, 4);
+ t->length += 9;
+}
+
+void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {
+ unsigned i;
+
+ assert(t);
+ extend(t, 2 + map->channels);
+
+ t->data[t->length++] = PA_TAG_CHANNEL_MAP;
+ t->data[t->length++] = map->channels;
+
+ for (i = 0; i < map->channels; i ++)
+ t->data[t->length++] = (uint8_t) map->map[i];
+}
+
+void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
+ unsigned i;
+ pa_volume_t vol;
+
+ assert(t);
+ extend(t, 2 + cvolume->channels * sizeof(pa_volume_t));
+
+ t->data[t->length++] = PA_TAG_CVOLUME;
+ t->data[t->length++] = cvolume->channels;
+
+ for (i = 0; i < cvolume->channels; i ++) {
+ vol = htonl(cvolume->values[i]);
+ memcpy(t->data + t->length, &vol, sizeof(pa_volume_t));
+ t->length += sizeof(pa_volume_t);
+ }
+}
+
+int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
+ int error = 0;
+ size_t n;
+ char *c;
+ assert(t && s);
+
+ if (t->rindex+1 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] == PA_TAG_STRING_NULL) {
+ t->rindex++;
+ *s = NULL;
+ return 0;
+ }
+
+ if (t->rindex+2 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_STRING)
+ return -1;
+
+ error = 1;
+ for (n = 0, c = (char*) (t->data+t->rindex+1); t->rindex+1+n < t->length; n++, c++)
+ if (!*c) {
+ error = 0;
+ break;
+ }
+
+ if (error)
+ return -1;
+
+ *s = (char*) (t->data+t->rindex+1);
+
+ t->rindex += n+2;
+ return 0;
+}
+
+int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {
+ assert(t && i);
+
+ if (t->rindex+5 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_U32)
+ return -1;
+
+ memcpy(i, t->data+t->rindex+1, 4);
+ *i = ntohl(*i);
+ t->rindex += 5;
+ return 0;
+}
+
+int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {
+ assert(t && c);
+
+ if (t->rindex+2 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_U8)
+ return -1;
+
+ *c = t->data[t->rindex+1];
+ t->rindex +=2;
+ return 0;
+}
+
+int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
+ assert(t && ss);
+
+ if (t->rindex+7 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_SAMPLE_SPEC)
+ return -1;
+
+ ss->format = t->data[t->rindex+1];
+ ss->channels = t->data[t->rindex+2];
+ memcpy(&ss->rate, t->data+t->rindex+3, 4);
+ ss->rate = ntohl(ss->rate);
+
+ if (!pa_sample_spec_valid(ss))
+ return -1;
+
+ t->rindex += 7;
+ return 0;
+}
+
+int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
+ uint32_t len;
+ assert(t && p);
+
+ if (t->rindex+5+length > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_ARBITRARY)
+ return -1;
+
+ memcpy(&len, t->data+t->rindex+1, 4);
+ if (ntohl(len) != length)
+ return -1;
+
+ *p = t->data+t->rindex+5;
+ t->rindex += 5+length;
+ return 0;
+}
+
+int pa_tagstruct_eof(pa_tagstruct*t) {
+ assert(t);
+ return t->rindex >= t->length;
+}
+
+const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
+ assert(t && t->dynamic && l);
+ *l = t->length;
+ return t->data;
+}
+
+int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) {
+ assert(t && b);
+
+ if (t->rindex+1 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE)
+ *b = 1;
+ else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE)
+ *b = 0;
+ else
+ return -1;
+
+ t->rindex +=1;
+ return 0;
+}
+
+int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
+
+ if (t->rindex+9 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_TIMEVAL)
+ return -1;
+
+ memcpy(&tv->tv_sec, t->data+t->rindex+1, 4);
+ tv->tv_sec = ntohl(tv->tv_sec);
+ memcpy(&tv->tv_usec, t->data+t->rindex+5, 4);
+ tv->tv_usec = ntohl(tv->tv_usec);
+ t->rindex += 9;
+ return 0;
+
+}
+
+int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {
+ uint32_t tmp;
+ assert(t && u);
+
+ if (t->rindex+9 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_USEC)
+ return -1;
+
+ memcpy(&tmp, t->data+t->rindex+1, 4);
+ *u = (pa_usec_t) ntohl(tmp) << 32;
+ memcpy(&tmp, t->data+t->rindex+5, 4);
+ *u |= (pa_usec_t) ntohl(tmp);
+ t->rindex +=9;
+ return 0;
+}
+
+int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
+ uint32_t tmp;
+ assert(t && u);
+
+ if (t->rindex+9 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_U64)
+ return -1;
+
+ memcpy(&tmp, t->data+t->rindex+1, 4);
+ *u = (pa_usec_t) ntohl(tmp) << 32;
+ memcpy(&tmp, t->data+t->rindex+5, 4);
+ *u |= (pa_usec_t) ntohl(tmp);
+ t->rindex +=9;
+ return 0;
+}
+
+int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
+ unsigned i;
+
+ assert(t);
+ assert(map);
+
+ if (t->rindex+2 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_CHANNEL_MAP)
+ return -1;
+
+ if ((map->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
+ return -1;
+
+ if (t->rindex+2+map->channels > t->length)
+ return -1;
+
+ for (i = 0; i < map->channels; i ++)
+ map->map[i] = (int8_t) t->data[t->rindex + 2 + i];
+
+ if (!pa_channel_map_valid(map))
+ return -1;
+
+ t->rindex += 2 + map->channels;
+ return 0;
+}
+
+int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
+ unsigned i;
+ pa_volume_t vol;
+
+ assert(t);
+ assert(cvolume);
+
+ if (t->rindex+2 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != PA_TAG_CVOLUME)
+ return -1;
+
+ if ((cvolume->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
+ return -1;
+
+ if (t->rindex+2+cvolume->channels*sizeof(pa_volume_t) > t->length)
+ return -1;
+
+ for (i = 0; i < cvolume->channels; i ++) {
+ memcpy(&vol, t->data + t->rindex + 2 + i * sizeof(pa_volume_t), sizeof(pa_volume_t));
+ cvolume->values[i] = (pa_volume_t) ntohl(vol);
+ }
+
+ if (!pa_cvolume_valid(cvolume))
+ return -1;
+
+ t->rindex += 2 + cvolume->channels * sizeof(pa_volume_t);
+ return 0;
+}
+
+void pa_tagstruct_put(pa_tagstruct *t, ...) {
+ va_list va;
+ assert(t);
+
+ va_start(va, t);
+
+ for (;;) {
+ int tag = va_arg(va, int);
+
+ if (tag == PA_TAG_INVALID)
+ break;
+
+ switch (tag) {
+ case PA_TAG_STRING:
+ case PA_TAG_STRING_NULL:
+ pa_tagstruct_puts(t, va_arg(va, char*));
+ break;
+
+ case PA_TAG_U32:
+ pa_tagstruct_putu32(t, va_arg(va, uint32_t));
+ break;
+
+ case PA_TAG_U8:
+ pa_tagstruct_putu8(t, (uint8_t) va_arg(va, int));
+ break;
+
+ case PA_TAG_U64:
+ pa_tagstruct_putu64(t, va_arg(va, uint64_t));
+ break;
+
+ case PA_TAG_SAMPLE_SPEC:
+ pa_tagstruct_put_sample_spec(t, va_arg(va, pa_sample_spec*));
+ break;
+
+ case PA_TAG_ARBITRARY: {
+ void *p = va_arg(va, void*);
+ size_t size = va_arg(va, size_t);
+ pa_tagstruct_put_arbitrary(t, p, size);
+ break;
+ }
+
+ case PA_TAG_BOOLEAN_TRUE:
+ case PA_TAG_BOOLEAN_FALSE:
+ pa_tagstruct_put_boolean(t, va_arg(va, int));
+ break;
+
+ case PA_TAG_TIMEVAL:
+ pa_tagstruct_put_timeval(t, va_arg(va, struct timeval*));
+ break;
+
+ case PA_TAG_USEC:
+ pa_tagstruct_put_usec(t, va_arg(va, pa_usec_t));
+ break;
+
+ case PA_TAG_CHANNEL_MAP:
+ pa_tagstruct_put_channel_map(t, va_arg(va, pa_channel_map *));
+ break;
+
+ case PA_TAG_CVOLUME:
+ pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *));
+ break;
+
+ default:
+ abort();
+ }
+ }
+
+ va_end(va);
+}
+
+int pa_tagstruct_get(pa_tagstruct *t, ...) {
+ va_list va;
+ int ret = 0;
+
+ assert(t);
+
+ va_start(va, t);
+ while (ret == 0) {
+ int tag = va_arg(va, int);
+
+ if (tag == PA_TAG_INVALID)
+ break;
+
+ switch (tag) {
+ case PA_TAG_STRING:
+ case PA_TAG_STRING_NULL:
+ ret = pa_tagstruct_gets(t, va_arg(va, const char**));
+ break;
+
+ case PA_TAG_U32:
+ ret = pa_tagstruct_getu32(t, va_arg(va, uint32_t*));
+ break;
+
+ case PA_TAG_U8:
+ ret = pa_tagstruct_getu8(t, va_arg(va, uint8_t*));
+ break;
+
+ case PA_TAG_U64:
+ ret = pa_tagstruct_getu64(t, va_arg(va, uint64_t*));
+ break;
+
+ case PA_TAG_SAMPLE_SPEC:
+ ret = pa_tagstruct_get_sample_spec(t, va_arg(va, pa_sample_spec*));
+ break;
+
+ case PA_TAG_ARBITRARY: {
+ const void **p = va_arg(va, const void**);
+ size_t size = va_arg(va, size_t);
+ ret = pa_tagstruct_get_arbitrary(t, p, size);
+ break;
+ }
+
+ case PA_TAG_BOOLEAN_TRUE:
+ case PA_TAG_BOOLEAN_FALSE:
+ ret = pa_tagstruct_get_boolean(t, va_arg(va, int*));
+ break;
+
+ case PA_TAG_TIMEVAL:
+ ret = pa_tagstruct_get_timeval(t, va_arg(va, struct timeval*));
+ break;
+
+ case PA_TAG_USEC:
+ ret = pa_tagstruct_get_usec(t, va_arg(va, pa_usec_t*));
+ break;
+
+ case PA_TAG_CHANNEL_MAP:
+ ret = pa_tagstruct_get_channel_map(t, va_arg(va, pa_channel_map *));
+ break;
+
+ case PA_TAG_CVOLUME:
+ ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
+ break;
+
+
+ default:
+ abort();
+ }
+
+ }
+
+ va_end(va);
+ return ret;
+}
diff --git a/src/polypcore/tagstruct.h b/src/polypcore/tagstruct.h
new file mode 100644
index 00000000..59db67b0
--- /dev/null
+++ b/src/polypcore/tagstruct.h
@@ -0,0 +1,92 @@
+#ifndef footagstructhfoo
+#define footagstructhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <polyp/sample.h>
+#include <polyp/channelmap.h>
+#include <polyp/volume.h>
+
+typedef struct pa_tagstruct pa_tagstruct;
+
+enum {
+ PA_TAG_INVALID = 0,
+ PA_TAG_STRING = 't',
+ PA_TAG_STRING_NULL = 'N',
+ PA_TAG_U32 = 'L',
+ PA_TAG_U8 = 'B',
+ PA_TAG_U64 = 'R',
+ PA_TAG_SAMPLE_SPEC = 'a',
+ PA_TAG_ARBITRARY = 'x',
+ PA_TAG_BOOLEAN_TRUE = '1',
+ PA_TAG_BOOLEAN_FALSE = '0',
+ PA_TAG_BOOLEAN = PA_TAG_BOOLEAN_TRUE,
+ PA_TAG_TIMEVAL = 'T',
+ PA_TAG_USEC = 'U' /* 64bit unsigned */,
+ PA_TAG_CHANNEL_MAP = 'm',
+ PA_TAG_CVOLUME = 'v'
+};
+
+
+
+pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length);
+void pa_tagstruct_free(pa_tagstruct*t);
+uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l);
+
+int pa_tagstruct_eof(pa_tagstruct*t);
+const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l);
+
+void pa_tagstruct_put(pa_tagstruct *t, ...);
+
+void pa_tagstruct_puts(pa_tagstruct*t, const char *s);
+void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c);
+void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i);
+void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t i);
+void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss);
+void pa_tagstruct_put_arbitrary(pa_tagstruct*t, const void *p, size_t length);
+void pa_tagstruct_put_boolean(pa_tagstruct*t, int b);
+void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv);
+void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u);
+void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map);
+void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume);
+
+int pa_tagstruct_get(pa_tagstruct *t, ...);
+
+int pa_tagstruct_gets(pa_tagstruct*t, const char **s);
+int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c);
+int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i);
+int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *i);
+int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss);
+int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length);
+int pa_tagstruct_get_boolean(pa_tagstruct *t, int *b);
+int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv);
+int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u);
+int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map);
+int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v);
+
+
+#endif
diff --git a/src/polypcore/tokenizer.c b/src/polypcore/tokenizer.c
new file mode 100644
index 00000000..5e0c1b16
--- /dev/null
+++ b/src/polypcore/tokenizer.c
@@ -0,0 +1,88 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <stdlib.h>
+
+#include "tokenizer.h"
+#include "dynarray.h"
+#include "xmalloc.h"
+#include "gccmacro.h"
+
+struct pa_tokenizer {
+ pa_dynarray *dynarray;
+};
+
+static void token_free(void *p, PA_GCC_UNUSED void *userdata) {
+ pa_xfree(p);
+}
+
+static void parse(pa_dynarray*a, const char *s, unsigned args) {
+ int infty = 0;
+ const char delimiter[] = " \t\n\r";
+ const char *p;
+ assert(a && s);
+
+ if (args == 0)
+ infty = 1;
+
+ p = s+strspn(s, delimiter);
+ while (*p && (infty || args >= 2)) {
+ size_t l = strcspn(p, delimiter);
+ char *n = pa_xstrndup(p, l);
+ pa_dynarray_append(a, n);
+ p += l;
+ p += strspn(p, delimiter);
+ args--;
+ }
+
+ if (args && *p) {
+ char *n = pa_xstrdup(p);
+ pa_dynarray_append(a, n);
+ }
+}
+
+pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) {
+ pa_tokenizer *t;
+
+ t = pa_xmalloc(sizeof(pa_tokenizer));
+ t->dynarray = pa_dynarray_new();
+ assert(t->dynarray);
+
+ parse(t->dynarray, s, args);
+ return t;
+}
+
+void pa_tokenizer_free(pa_tokenizer *t) {
+ assert(t);
+ pa_dynarray_free(t->dynarray, token_free, NULL);
+ pa_xfree(t);
+}
+
+const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) {
+ assert(t);
+ return pa_dynarray_get(t->dynarray, i);
+}
diff --git a/src/polypcore/tokenizer.h b/src/polypcore/tokenizer.h
new file mode 100644
index 00000000..bedacb8a
--- /dev/null
+++ b/src/polypcore/tokenizer.h
@@ -0,0 +1,32 @@
+#ifndef footokenizerhfoo
+#define footokenizerhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct pa_tokenizer pa_tokenizer;
+
+pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args);
+void pa_tokenizer_free(pa_tokenizer *t);
+
+const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i);
+
+#endif
diff --git a/src/polypcore/util.c b/src/polypcore/util.c
new file mode 100644
index 00000000..4b6edb97
--- /dev/null
+++ b/src/polypcore/util.c
@@ -0,0 +1,1176 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <stdarg.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <samplerate.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#include "winsock.h"
+
+#include "util.h"
+#include "xmalloc.h"
+#include "log.h"
+
+#ifndef OS_IS_WIN32
+#define PA_RUNTIME_PATH_PREFIX "/tmp/polypaudio-"
+#define PATH_SEP '/'
+#else
+#define PA_RUNTIME_PATH_PREFIX "%TEMP%\\polypaudio-"
+#define PATH_SEP '\\'
+#endif
+
+#ifdef OS_IS_WIN32
+
+#define POLYP_ROOTENV "POLYP_ROOT"
+
+int pa_set_root(HANDLE handle) {
+ char library_path[MAX_PATH + sizeof(POLYP_ROOTENV) + 1], *sep;
+
+ strcpy(library_path, POLYP_ROOTENV "=");
+
+ if (!GetModuleFileName(handle, library_path + sizeof(POLYP_ROOTENV), MAX_PATH))
+ return 0;
+
+ sep = strrchr(library_path, '\\');
+ if (sep)
+ *sep = '\0';
+
+ if (_putenv(library_path) < 0)
+ return 0;
+
+ return 1;
+}
+
+#endif
+
+/** Make a file descriptor nonblock. Doesn't do any error checking */
+void pa_make_nonblock_fd(int fd) {
+#ifdef O_NONBLOCK
+ int v;
+ assert(fd >= 0);
+
+ if ((v = fcntl(fd, F_GETFL)) >= 0)
+ if (!(v & O_NONBLOCK))
+ fcntl(fd, F_SETFL, v|O_NONBLOCK);
+#elif defined(OS_IS_WIN32)
+ u_long arg = 1;
+ if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
+ if (WSAGetLastError() == WSAENOTSOCK)
+ pa_log_warn(__FILE__": WARNING: Only sockets can be made non-blocking!\n");
+ }
+#else
+ pa_log_warn(__FILE__": WARNING: Non-blocking I/O not supported.!\n");
+#endif
+}
+
+/** Creates a directory securely */
+int pa_make_secure_dir(const char* dir) {
+ struct stat st;
+ assert(dir);
+
+#ifdef OS_IS_WIN32
+ if (mkdir(dir) < 0)
+#else
+ if (mkdir(dir, 0700) < 0)
+#endif
+ if (errno != EEXIST)
+ return -1;
+
+#ifdef HAVE_LSTAT
+ if (lstat(dir, &st) < 0)
+#else
+ if (stat(dir, &st) < 0)
+#endif
+ goto fail;
+
+#ifndef OS_IS_WIN32
+ if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700))
+ goto fail;
+#else
+ fprintf(stderr, "FIXME: pa_make_secure_dir()\n");
+#endif
+
+ return 0;
+
+fail:
+ rmdir(dir);
+ return -1;
+}
+
+/* Creates a the parent directory of the specified path securely */
+int pa_make_secure_parent_dir(const char *fn) {
+ int ret = -1;
+ char *slash, *dir = pa_xstrdup(fn);
+
+ slash = pa_path_get_filename(dir);
+ if (slash == fn)
+ goto finish;
+ *(slash-1) = 0;
+
+ if (pa_make_secure_dir(dir) < 0)
+ goto finish;
+
+ ret = 0;
+
+finish:
+ pa_xfree(dir);
+ return ret;
+}
+
+
+/** Calls read() in a loop. Makes sure that as much as 'size' bytes,
+ * unless EOF is reached or an error occured */
+ssize_t pa_loop_read(int fd, void*data, size_t size) {
+ ssize_t ret = 0;
+ assert(fd >= 0 && data && size);
+
+ while (size > 0) {
+ ssize_t r;
+
+ if ((r = read(fd, data, size)) < 0)
+ return r;
+
+ if (r == 0)
+ break;
+
+ ret += r;
+ data = (uint8_t*) data + r;
+ size -= r;
+ }
+
+ return ret;
+}
+
+/** Similar to pa_loop_read(), but wraps write() */
+ssize_t pa_loop_write(int fd, const void*data, size_t size) {
+ ssize_t ret = 0;
+ assert(fd >= 0 && data && size);
+
+ while (size > 0) {
+ ssize_t r;
+
+ if ((r = write(fd, data, size)) < 0)
+ return r;
+
+ if (r == 0)
+ break;
+
+ ret += r;
+ data = (const uint8_t*) data + r;
+ size -= r;
+ }
+
+ return ret;
+}
+
+/* Print a warning messages in case that the given signal is not
+ * blocked or trapped */
+void pa_check_signal_is_blocked(int sig) {
+#ifdef HAVE_SIGACTION
+ struct sigaction sa;
+ sigset_t set;
+
+ /* If POSIX threads are supported use thread-aware
+ * pthread_sigmask() function, to check if the signal is
+ * blocked. Otherwise fall back to sigprocmask() */
+
+#ifdef HAVE_PTHREAD
+ if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) {
+#endif
+ if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) {
+ pa_log(__FILE__": sigprocmask() failed: %s\n", strerror(errno));
+ return;
+ }
+#ifdef HAVE_PTHREAD
+ }
+#endif
+
+ if (sigismember(&set, sig))
+ return;
+
+ /* Check whether the signal is trapped */
+
+ if (sigaction(sig, NULL, &sa) < 0) {
+ pa_log(__FILE__": sigaction() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ if (sa.sa_handler != SIG_DFL)
+ return;
+
+ pa_log(__FILE__": WARNING: %s is not trapped. This might cause malfunction!\n", pa_strsignal(sig));
+#else /* HAVE_SIGACTION */
+ pa_log(__FILE__": WARNING: %s might not be trapped. This might cause malfunction!\n", pa_strsignal(sig));
+#endif
+}
+
+/* The following function is based on an example from the GNU libc
+ * documentation. This function is similar to GNU's asprintf(). */
+char *pa_sprintf_malloc(const char *format, ...) {
+ int size = 100;
+ char *c = NULL;
+
+ assert(format);
+
+ for(;;) {
+ int r;
+ va_list ap;
+
+ c = pa_xrealloc(c, size);
+
+ va_start(ap, format);
+ r = vsnprintf(c, size, format, ap);
+ va_end(ap);
+
+ if (r > -1 && r < size)
+ return c;
+
+ if (r > -1) /* glibc 2.1 */
+ size = r+1;
+ else /* glibc 2.0 */
+ size *= 2;
+ }
+}
+
+/* Same as the previous function, but use a va_list instead of an
+ * ellipsis */
+char *pa_vsprintf_malloc(const char *format, va_list ap) {
+ int size = 100;
+ char *c = NULL;
+
+ assert(format);
+
+ for(;;) {
+ int r;
+ c = pa_xrealloc(c, size);
+ r = vsnprintf(c, size, format, ap);
+
+ if (r > -1 && r < size)
+ return c;
+
+ if (r > -1) /* glibc 2.1 */
+ size = r+1;
+ else /* glibc 2.0 */
+ size *= 2;
+ }
+}
+
+/* Return the current username in the specified string buffer. */
+char *pa_get_user_name(char *s, size_t l) {
+ char *p;
+ char buf[1024];
+
+#ifdef HAVE_PWD_H
+ struct passwd pw, *r;
+#endif
+
+ assert(s && l > 0);
+
+ if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) {
+#ifdef HAVE_PWD_H
+
+#ifdef HAVE_GETPWUID_R
+ if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
+#else
+ /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
+ * that do not support getpwuid_r. */
+ if ((r = getpwuid(getuid())) == NULL) {
+#endif
+ snprintf(s, l, "%lu", (unsigned long) getuid());
+ return s;
+ }
+
+ p = r->pw_name;
+
+#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */
+ DWORD size = sizeof(buf);
+
+ if (!GetUserName(buf, &size))
+ return NULL;
+
+ p = buf;
+
+#else /* HAVE_PWD_H */
+ return NULL;
+#endif /* HAVE_PWD_H */
+ }
+
+ return pa_strlcpy(s, p, l);
+}
+
+/* Return the current hostname in the specified buffer. */
+char *pa_get_host_name(char *s, size_t l) {
+ assert(s && l > 0);
+ if (gethostname(s, l) < 0) {
+ pa_log(__FILE__": gethostname(): %s\n", strerror(errno));
+ return NULL;
+ }
+ s[l-1] = 0;
+ return s;
+}
+
+/* Return the home directory of the current user */
+char *pa_get_home_dir(char *s, size_t l) {
+ char *e;
+
+#ifdef HAVE_PWD_H
+ char buf[1024];
+ struct passwd pw, *r;
+#endif
+
+ assert(s && l);
+
+ if ((e = getenv("HOME")))
+ return pa_strlcpy(s, e, l);
+
+ if ((e = getenv("USERPROFILE")))
+ return pa_strlcpy(s, e, l);
+
+#ifdef HAVE_PWD_H
+#ifdef HAVE_GETPWUID_R
+ if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
+ pa_log(__FILE__": getpwuid_r() failed\n");
+#else
+ /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
+ * that do not support getpwuid_r. */
+ if ((r = getpwuid(getuid())) == NULL) {
+ pa_log(__FILE__": getpwuid_r() failed\n");
+#endif
+ return NULL;
+ }
+
+ return pa_strlcpy(s, r->pw_dir, l);
+#else /* HAVE_PWD_H */
+ return NULL;
+#endif
+}
+
+/* Similar to OpenBSD's strlcpy() function */
+char *pa_strlcpy(char *b, const char *s, size_t l) {
+ assert(b && s && l > 0);
+
+ strncpy(b, s, l);
+ b[l-1] = 0;
+ return b;
+}
+
+int pa_gettimeofday(struct timeval *tv) {
+#ifdef HAVE_GETTIMEOFDAY
+ return gettimeofday(tv, NULL);
+#elif defined(OS_IS_WIN32)
+ /*
+ * Copied from implementation by Steven Edwards (LGPL).
+ * Found on wine mailing list.
+ */
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+#define EPOCHFILETIME (116444736000000000i64)
+#else
+#define EPOCHFILETIME (116444736000000000LL)
+#endif
+
+ FILETIME ft;
+ LARGE_INTEGER li;
+ __int64 t;
+
+ if (tv) {
+ GetSystemTimeAsFileTime(&ft);
+ li.LowPart = ft.dwLowDateTime;
+ li.HighPart = ft.dwHighDateTime;
+ t = li.QuadPart; /* In 100-nanosecond intervals */
+ t -= EPOCHFILETIME; /* Offset to the Epoch time */
+ t /= 10; /* In microseconds */
+ tv->tv_sec = (long)(t / 1000000);
+ tv->tv_usec = (long)(t % 1000000);
+ }
+
+ return 0;
+#else
+#error "Platform lacks gettimeofday() or equivalent function."
+#endif
+}
+
+/* Calculate the difference between the two specfified timeval
+ * timestamsps. */
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
+ pa_usec_t r;
+ assert(a && b);
+
+ /* Check which whan is the earlier time and swap the two arguments if reuqired. */
+ if (pa_timeval_cmp(a, b) < 0) {
+ const struct timeval *c;
+ c = a;
+ a = b;
+ b = c;
+ }
+
+ /* Calculate the second difference*/
+ r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000;
+
+ /* Calculate the microsecond difference */
+ if (a->tv_usec > b->tv_usec)
+ r += ((pa_usec_t) a->tv_usec - b->tv_usec);
+ else if (a->tv_usec < b->tv_usec)
+ r -= ((pa_usec_t) b->tv_usec - a->tv_usec);
+
+ return r;
+}
+
+/* Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {
+ assert(a && b);
+
+ if (a->tv_sec < b->tv_sec)
+ return -1;
+
+ if (a->tv_sec > b->tv_sec)
+ return 1;
+
+ if (a->tv_usec < b->tv_usec)
+ return -1;
+
+ if (a->tv_usec > b->tv_usec)
+ return 1;
+
+ return 0;
+}
+
+/* Return the time difference between now and the specified timestamp */
+pa_usec_t pa_timeval_age(const struct timeval *tv) {
+ struct timeval now;
+ assert(tv);
+ pa_gettimeofday(&now);
+ return pa_timeval_diff(&now, tv);
+}
+
+/* Add the specified time inmicroseconds to the specified timeval structure */
+void pa_timeval_add(struct timeval *tv, pa_usec_t v) {
+ unsigned long secs;
+ assert(tv);
+
+ secs = (v/1000000);
+ tv->tv_sec += (unsigned long) secs;
+ v -= secs*1000000;
+
+ tv->tv_usec += v;
+
+ /* Normalize */
+ while (tv->tv_usec >= 1000000) {
+ tv->tv_sec++;
+ tv->tv_usec -= 1000000;
+ }
+}
+
+#define NICE_LEVEL (-15)
+
+/* Raise the priority of the current process as much as possible and
+sensible: set the nice level to -15 and enable realtime scheduling if
+supported.*/
+void pa_raise_priority(void) {
+
+#ifdef HAVE_SYS_RESOURCE_H
+ if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0)
+ pa_log_warn(__FILE__": setpriority() failed: %s\n", strerror(errno));
+ else
+ pa_log_info(__FILE__": Successfully gained nice level %i.\n", NICE_LEVEL);
+#endif
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ {
+ struct sched_param sp;
+
+ if (sched_getparam(0, &sp) < 0) {
+ pa_log(__FILE__": sched_getparam() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ sp.sched_priority = 1;
+ if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) {
+ pa_log_warn(__FILE__": sched_setscheduler() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ pa_log_info(__FILE__": Successfully enabled SCHED_FIFO scheduling.\n");
+ }
+#endif
+
+#ifdef OS_IS_WIN32
+ if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
+ pa_log_warn(__FILE__": SetPriorityClass() failed: 0x%08X\n", GetLastError());
+ else
+ pa_log_info(__FILE__": Successfully gained high priority class.\n");
+#endif
+}
+
+/* Reset the priority to normal, inverting the changes made by pa_raise_priority() */
+void pa_reset_priority(void) {
+#ifdef OS_IS_WIN32
+ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+#endif
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ {
+ struct sched_param sp;
+ sched_getparam(0, &sp);
+ sp.sched_priority = 0;
+ sched_setscheduler(0, SCHED_OTHER, &sp);
+ }
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+ setpriority(PRIO_PROCESS, 0, 0);
+#endif
+}
+
+/* Set the FD_CLOEXEC flag for a fd */
+int pa_fd_set_cloexec(int fd, int b) {
+
+#ifdef FD_CLOEXEC
+ int v;
+ assert(fd >= 0);
+
+ if ((v = fcntl(fd, F_GETFD, 0)) < 0)
+ return -1;
+
+ v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0);
+
+ if (fcntl(fd, F_SETFD, v) < 0)
+ return -1;
+#endif
+
+ return 0;
+}
+
+/* Return the binary file name of the current process. Works on Linux
+ * only. This shoul be used for eyecandy only, don't rely on return
+ * non-NULL! */
+char *pa_get_binary_name(char *s, size_t l) {
+
+#ifdef HAVE_READLINK
+ char path[PATH_MAX];
+ int i;
+ assert(s && l);
+
+ /* This works on Linux only */
+
+ snprintf(path, sizeof(path), "/proc/%u/exe", (unsigned) getpid());
+ if ((i = readlink(path, s, l-1)) < 0)
+ return NULL;
+
+ s[i] = 0;
+ return s;
+#elif defined(OS_IS_WIN32)
+ char path[PATH_MAX];
+ if (!GetModuleFileName(NULL, path, PATH_MAX))
+ return NULL;
+ pa_strlcpy(s, pa_path_get_filename(path), l);
+ return s;
+#else
+ return NULL;
+#endif
+}
+
+/* Return a pointer to the filename inside a path (which is the last
+ * component). */
+char *pa_path_get_filename(const char *p) {
+ char *fn;
+
+ if ((fn = strrchr(p, PATH_SEP)))
+ return fn+1;
+
+ return (char*) p;
+}
+
+/* Try to parse a boolean string value.*/
+int pa_parse_boolean(const char *v) {
+
+ if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+ return 1;
+ else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+ return 0;
+
+ return -1;
+}
+
+/* Split the specified string wherever one of the strings in delimiter
+ * occurs. Each time it is called returns a newly allocated string
+ * with pa_xmalloc(). The variable state points to, should be
+ * initiallized to NULL before the first call. */
+char *pa_split(const char *c, const char *delimiter, const char**state) {
+ const char *current = *state ? *state : c;
+ size_t l;
+
+ if (!*current)
+ return NULL;
+
+ l = strcspn(current, delimiter);
+ *state = current+l;
+
+ if (**state)
+ (*state)++;
+
+ return pa_xstrndup(current, l);
+}
+
+/* What is interpreted as whitespace? */
+#define WHITESPACE " \t\n"
+
+/* Split a string into words. Otherwise similar to pa_split(). */
+char *pa_split_spaces(const char *c, const char **state) {
+ const char *current = *state ? *state : c;
+ size_t l;
+
+ if (!*current || *c == 0)
+ return NULL;
+
+ current += strspn(current, WHITESPACE);
+ l = strcspn(current, WHITESPACE);
+
+ *state = current+l;
+
+ return pa_xstrndup(current, l);
+}
+
+/* Return the name of an UNIX signal. Similar to GNU's strsignal() */
+const char *pa_strsignal(int sig) {
+ switch(sig) {
+ case SIGINT: return "SIGINT";
+ case SIGTERM: return "SIGTERM";
+#ifdef SIGUSR1
+ case SIGUSR1: return "SIGUSR1";
+#endif
+#ifdef SIGUSR2
+ case SIGUSR2: return "SIGUSR2";
+#endif
+#ifdef SIGXCPU
+ case SIGXCPU: return "SIGXCPU";
+#endif
+#ifdef SIGPIPE
+ case SIGPIPE: return "SIGPIPE";
+#endif
+#ifdef SIGCHLD
+ case SIGCHLD: return "SIGCHLD";
+#endif
+#ifdef SIGHUP
+ case SIGHUP: return "SIGHUP";
+#endif
+ default: return "UNKNOWN SIGNAL";
+ }
+}
+
+#ifdef HAVE_GRP_H
+
+/* Check whether the specified GID and the group name match */
+static int is_group(gid_t gid, const char *name) {
+ struct group group, *result = NULL;
+ long n;
+ void *data;
+ int r = -1;
+
+#ifdef HAVE_GETGRGID_R
+#ifdef _SC_GETGR_R_SIZE_MAX
+ n = sysconf(_SC_GETGR_R_SIZE_MAX);
+#else
+ n = -1;
+#endif
+ if (n < 0) n = 512;
+ data = pa_xmalloc(n);
+
+ if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) {
+ pa_log(__FILE__ ": getgrgid_r(%u) failed: %s\n", gid, strerror(errno));
+ goto finish;
+ }
+
+
+ r = strcmp(name, result->gr_name) == 0;
+
+finish:
+ pa_xfree(data);
+#else
+ /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
+ * support getgrgid_r. */
+ if ((result = getgrgid(gid)) == NULL) {
+ pa_log(__FILE__ ": getgrgid(%u) failed: %s\n", gid, strerror(errno));
+ goto finish;
+ }
+
+ r = strcmp(name, result->gr_name) == 0;
+
+finish:
+#endif
+
+ return r;
+}
+
+/* Check the current user is member of the specified group */
+int pa_uid_in_group(const char *name, gid_t *gid) {
+ GETGROUPS_T *gids, tgid;
+ int n = sysconf(_SC_NGROUPS_MAX);
+ int r = -1, i;
+
+ assert(n > 0);
+
+ gids = pa_xmalloc(sizeof(GETGROUPS_T)*n);
+
+ if ((n = getgroups(n, gids)) < 0) {
+ pa_log(__FILE__": getgroups() failed: %s\n", strerror(errno));
+ goto finish;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (is_group(gids[i], name) > 0) {
+ *gid = gids[i];
+ r = 1;
+ goto finish;
+ }
+ }
+
+ if (is_group(tgid = getgid(), name) > 0) {
+ *gid = tgid;
+ r = 1;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+
+ pa_xfree(gids);
+ return r;
+}
+
+#else /* HAVE_GRP_H */
+
+int pa_uid_in_group(const char *name, gid_t *gid) {
+ return -1;
+}
+
+#endif
+
+/* Lock or unlock a file entirely.
+ (advisory on UNIX, mandatory on Windows) */
+int pa_lock_fd(int fd, int b) {
+#ifdef F_SETLKW
+ struct flock flock;
+
+ /* Try a R/W lock first */
+
+ flock.l_type = b ? F_WRLCK : F_UNLCK;
+ flock.l_whence = SEEK_SET;
+ flock.l_start = 0;
+ flock.l_len = 0;
+
+ if (fcntl(fd, F_SETLKW, &flock) >= 0)
+ return 0;
+
+ /* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */
+ if (b && errno == EBADF) {
+ flock.l_type = F_RDLCK;
+ if (fcntl(fd, F_SETLKW, &flock) >= 0)
+ return 0;
+ }
+
+ pa_log(__FILE__": %slock failed: %s\n", !b ? "un" : "", strerror(errno));
+#endif
+
+#ifdef OS_IS_WIN32
+ HANDLE h = (HANDLE)_get_osfhandle(fd);
+
+ if (b && LockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
+ return 0;
+ if (!b && UnlockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
+ return 0;
+
+ pa_log(__FILE__": %slock failed: 0x%08X\n", !b ? "un" : "", GetLastError());
+#endif
+
+ return -1;
+}
+
+/* Remove trailing newlines from a string */
+char* pa_strip_nl(char *s) {
+ assert(s);
+
+ s[strcspn(s, "\r\n")] = 0;
+ return s;
+}
+
+/* Create a temporary lock file and lock it. */
+int pa_lock_lockfile(const char *fn) {
+ int fd = -1;
+ assert(fn);
+
+ for (;;) {
+ struct stat st;
+
+ if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
+ pa_log(__FILE__": failed to create lock file '%s': %s\n", fn, strerror(errno));
+ goto fail;
+ }
+
+ if (pa_lock_fd(fd, 1) < 0) {
+ pa_log(__FILE__": failed to lock file '%s'.\n", fn);
+ goto fail;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ pa_log(__FILE__": failed to fstat() file '%s'.\n", fn);
+ goto fail;
+ }
+
+ /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */
+ if (st.st_nlink >= 1)
+ break;
+
+ if (pa_lock_fd(fd, 0) < 0) {
+ pa_log(__FILE__": failed to unlock file '%s'.\n", fn);
+ goto fail;
+ }
+
+ if (close(fd) < 0) {
+ pa_log(__FILE__": failed to close file '%s'.\n", fn);
+ goto fail;
+ }
+
+ fd = -1;
+ }
+
+ return fd;
+
+fail:
+
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
+/* Unlock a temporary lcok file */
+int pa_unlock_lockfile(const char *fn, int fd) {
+ int r = 0;
+ assert(fn && fd >= 0);
+
+ if (unlink(fn) < 0) {
+ pa_log_warn(__FILE__": WARNING: unable to remove lock file '%s': %s\n", fn, strerror(errno));
+ r = -1;
+ }
+
+ if (pa_lock_fd(fd, 0) < 0) {
+ pa_log_warn(__FILE__": WARNING: failed to unlock file '%s'.\n", fn);
+ r = -1;
+ }
+
+ if (close(fd) < 0) {
+ pa_log_warn(__FILE__": WARNING: failed to close lock file '%s': %s\n", fn, strerror(errno));
+ r = -1;
+ }
+
+ return r;
+}
+
+/* Try to open a configuration file. If "env" is specified, open the
+ * value of the specified environment variable. Otherwise look for a
+ * file "local" in the home directory or a file "global" in global
+ * file system. If "result" is non-NULL, a pointer to a newly
+ * allocated buffer containing the used configuration file is
+ * stored there.*/
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
+ const char *fn;
+ char h[PATH_MAX];
+
+#ifdef OS_IS_WIN32
+ char buf[PATH_MAX];
+
+ if (!getenv(POLYP_ROOTENV))
+ pa_set_root(NULL);
+#endif
+
+ if (env && (fn = getenv(env))) {
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
+ return NULL;
+ fn = buf;
+#endif
+
+ if (result)
+ *result = pa_xstrdup(fn);
+
+ return fopen(fn, "r");
+ }
+
+ if (local && pa_get_home_dir(h, sizeof(h))) {
+ FILE *f;
+ char *lfn;
+
+ fn = lfn = pa_sprintf_malloc("%s/%s", h, local);
+
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
+ return NULL;
+ fn = buf;
+#endif
+
+ f = fopen(fn, "r");
+
+ if (f || errno != ENOENT) {
+ if (result)
+ *result = pa_xstrdup(fn);
+ pa_xfree(lfn);
+ return f;
+ }
+
+ pa_xfree(lfn);
+ }
+
+ if (!global) {
+ if (result)
+ *result = NULL;
+ errno = ENOENT;
+ return NULL;
+ }
+
+#ifdef OS_IS_WIN32
+ if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+ return NULL;
+ global = buf;
+#endif
+
+ if (result)
+ *result = pa_xstrdup(global);
+
+ return fopen(global, "r");
+}
+
+/* Format the specified data as a hexademical string */
+char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) {
+ size_t i = 0, j = 0;
+ const char hex[] = "0123456789abcdef";
+ assert(d && s && slength > 0);
+
+ while (i < dlength && j+3 <= slength) {
+ s[j++] = hex[*d >> 4];
+ s[j++] = hex[*d & 0xF];
+
+ d++;
+ i++;
+ }
+
+ s[j < slength ? j : slength] = 0;
+ return s;
+}
+
+/* Convert a hexadecimal digit to a number or -1 if invalid */
+static int hexc(char c) {
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+
+ return -1;
+}
+
+/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */
+size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
+ size_t j = 0;
+ assert(p && d);
+
+ while (j < dlength && *p) {
+ int b;
+
+ if ((b = hexc(*(p++))) < 0)
+ return (size_t) -1;
+
+ d[j] = (uint8_t) (b << 4);
+
+ if (!*p)
+ return (size_t) -1;
+
+ if ((b = hexc(*(p++))) < 0)
+ return (size_t) -1;
+
+ d[j] |= (uint8_t) b;
+ j++;
+ }
+
+ return j;
+}
+
+/* Return the fully qualified domain name in *s */
+char *pa_get_fqdn(char *s, size_t l) {
+ char hn[256];
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo *a, hints;
+#endif
+
+ if (!pa_get_host_name(hn, sizeof(hn)))
+ return NULL;
+
+#ifdef HAVE_GETADDRINFO
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+
+ if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname)
+ return pa_strlcpy(s, hn, l);
+
+ pa_strlcpy(s, a->ai_canonname, l);
+ freeaddrinfo(a);
+ return s;
+#else
+ return pa_strlcpy(s, hn, l);
+#endif
+}
+
+/* Returns nonzero when *s starts with *pfx */
+int pa_startswith(const char *s, const char *pfx) {
+ size_t l;
+ assert(s && pfx);
+ l = strlen(pfx);
+
+ return strlen(s) >= l && strncmp(s, pfx, l) == 0;
+}
+
+/* if fn is null return the polypaudio run time path in s (/tmp/polypaudio)
+ * if fn is non-null and starts with / return fn in s
+ * otherwise append fn to the run time path and return it in s */
+char *pa_runtime_path(const char *fn, char *s, size_t l) {
+ char u[256];
+
+#ifndef OS_IS_WIN32
+ if (fn && *fn == '/')
+#else
+ if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\')
+#endif
+ return pa_strlcpy(s, fn, l);
+
+ if (fn)
+ snprintf(s, l, "%s%s%c%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PATH_SEP, fn);
+ else
+ snprintf(s, l, "%s%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
+
+#ifdef OS_IS_WIN32
+ {
+ char buf[l];
+ strcpy(buf, s);
+ ExpandEnvironmentStrings(buf, s, l);
+ }
+#endif
+
+ return s;
+}
+
+/* Wait t milliseconds */
+int pa_msleep(unsigned long t) {
+#ifdef OS_IS_WIN32
+ Sleep(t);
+ return 0;
+#elif defined(HAVE_NANOSLEEP)
+ struct timespec ts;
+
+ ts.tv_sec = t/1000;
+ ts.tv_nsec = (t % 1000) * 1000000;
+
+ return nanosleep(&ts, NULL);
+#else
+#error "Platform lacks a sleep function."
+#endif
+}
+
+/* Convert the string s to a signed integer in *ret_i */
+int pa_atoi(const char *s, int32_t *ret_i) {
+ char *x = NULL;
+ long l;
+ assert(s && ret_i);
+
+ l = strtol(s, &x, 0);
+
+ if (!x || *x)
+ return -1;
+
+ *ret_i = (int32_t) l;
+
+ return 0;
+}
+
+/* Convert the string s to an unsigned integer in *ret_u */
+int pa_atou(const char *s, uint32_t *ret_u) {
+ char *x = NULL;
+ unsigned long l;
+ assert(s && ret_u);
+
+ l = strtoul(s, &x, 0);
+
+ if (!x || *x)
+ return -1;
+
+ *ret_u = (uint32_t) l;
+
+ return 0;
+}
diff --git a/src/polypcore/util.h b/src/polypcore/util.h
new file mode 100644
index 00000000..3dc6c945
--- /dev/null
+++ b/src/polypcore/util.h
@@ -0,0 +1,99 @@
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "gccmacro.h"
+#include <polyp/sample.h>
+
+struct timeval;
+
+void pa_make_nonblock_fd(int fd);
+
+int pa_make_secure_dir(const char* dir);
+int pa_make_secure_parent_dir(const char *fn);
+
+ssize_t pa_loop_read(int fd, void*data, size_t size);
+ssize_t pa_loop_write(int fd, const void*data, size_t size);
+
+void pa_check_signal_is_blocked(int sig);
+
+char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
+char *pa_vsprintf_malloc(const char *format, va_list ap);
+
+char *pa_strlcpy(char *b, const char *s, size_t l);
+
+char *pa_get_user_name(char *s, size_t l);
+char *pa_get_host_name(char *s, size_t l);
+char *pa_get_fqdn(char *s, size_t l);
+char *pa_get_binary_name(char *s, size_t l);
+char *pa_get_home_dir(char *s, size_t l);
+
+char *pa_path_get_filename(const char *p);
+
+int pa_gettimeofday(struct timeval *tv);
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b);
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b);
+pa_usec_t pa_timeval_age(const struct timeval *tv);
+void pa_timeval_add(struct timeval *tv, pa_usec_t v);
+
+void pa_raise_priority(void);
+void pa_reset_priority(void);
+
+int pa_fd_set_cloexec(int fd, int b);
+
+int pa_parse_boolean(const char *s);
+
+char *pa_split(const char *c, const char*delimiters, const char **state);
+char *pa_split_spaces(const char *c, const char **state);
+
+char *pa_strip_nl(char *s);
+
+const char *pa_strsignal(int sig);
+
+int pa_uid_in_group(const char *name, gid_t *gid);
+
+int pa_lock_fd(int fd, int b);
+
+int pa_lock_lockfile(const char *fn);
+int pa_unlock_lockfile(const char *fn, int fd);
+
+FILE *pa_open_config_file(const char *env, const char *global, const char *local, char **result);
+
+char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
+size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
+
+int pa_startswith(const char *s, const char *pfx);
+
+char *pa_runtime_path(const char *fn, char *s, size_t l);
+
+int pa_msleep(unsigned long t);
+
+int pa_atoi(const char *s, int32_t *ret_i);
+int pa_atou(const char *s, uint32_t *ret_u);
+
+#endif
diff --git a/src/polypcore/winsock.h b/src/polypcore/winsock.h
new file mode 100644
index 00000000..b1e0f7d4
--- /dev/null
+++ b/src/polypcore/winsock.h
@@ -0,0 +1,23 @@
+#ifndef foowinsockhfoo
+#define foowinsockhfoo
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+
+#define ESHUTDOWN WSAESHUTDOWN
+#define ECONNRESET WSAECONNRESET
+#define ECONNABORTED WSAECONNABORTED
+#define ENETRESET WSAENETRESET
+#define EINPROGRESS WSAEINPROGRESS
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#define ETIMEDOUT WSAETIMEDOUT
+#define ECONNREFUSED WSAECONNREFUSED
+#define EHOSTUNREACH WSAEHOSTUNREACH
+
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#endif
diff --git a/src/polypcore/x11prop.c b/src/polypcore/x11prop.c
new file mode 100644
index 00000000..e57fc136
--- /dev/null
+++ b/src/polypcore/x11prop.c
@@ -0,0 +1,70 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include "x11prop.h"
+
+
+void pa_x11_set_prop(Display *d, const char *name, const char *data) {
+ Atom a = XInternAtom(d, name, False);
+ XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, strlen(data)+1);
+}
+
+void pa_x11_del_prop(Display *d, const char *name) {
+ Atom a = XInternAtom(d, name, False);
+ XDeleteProperty(d, RootWindow(d, 0), a);
+}
+
+char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l) {
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems;
+ unsigned long nbytes_after;
+ unsigned char *prop = NULL;
+ char *ret = NULL;
+
+ Atom a = XInternAtom(d, name, False);
+ if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (l+2)/4, False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success)
+ goto finish;
+
+ if (actual_type != XA_STRING)
+ goto finish;
+
+ memcpy(p, prop, nitems);
+ p[nitems] = 0;
+
+ ret = p;
+
+finish:
+
+ if (prop)
+ XFree(prop);
+
+ return ret;
+}
diff --git a/src/polypcore/x11prop.h b/src/polypcore/x11prop.h
new file mode 100644
index 00000000..5531c640
--- /dev/null
+++ b/src/polypcore/x11prop.h
@@ -0,0 +1,33 @@
+#ifndef foox11prophfoo
+#define foox11prophfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+
+#include <X11/Xlib.h>
+
+void pa_x11_set_prop(Display *d, const char *name, const char *data);
+void pa_x11_del_prop(Display *d, const char *name);
+char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l);
+
+#endif
diff --git a/src/polypcore/x11wrap.c b/src/polypcore/x11wrap.c
new file mode 100644
index 00000000..e20a50a6
--- /dev/null
+++ b/src/polypcore/x11wrap.c
@@ -0,0 +1,235 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "llist.h"
+#include "x11wrap.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "props.h"
+
+typedef struct pa_x11_internal pa_x11_internal;
+
+struct pa_x11_internal {
+ PA_LLIST_FIELDS(pa_x11_internal);
+ pa_x11_wrapper *wrapper;
+ pa_io_event* io_event;
+ int fd;
+};
+
+struct pa_x11_wrapper {
+ pa_core *core;
+ int ref;
+
+ char *property_name;
+ Display *display;
+
+ pa_defer_event* defer_event;
+ pa_io_event* io_event;
+
+ PA_LLIST_HEAD(pa_x11_client, clients);
+ PA_LLIST_HEAD(pa_x11_internal, internals);
+};
+
+struct pa_x11_client {
+ PA_LLIST_FIELDS(pa_x11_client);
+ pa_x11_wrapper *wrapper;
+ int (*callback)(pa_x11_wrapper *w, XEvent *e, void *userdata);
+ void *userdata;
+};
+
+/* Dispatch all pending X11 events */
+static void work(pa_x11_wrapper *w) {
+ assert(w && w->ref >= 1);
+
+ while (XPending(w->display)) {
+ pa_x11_client *c;
+ XEvent e;
+ XNextEvent(w->display, &e);
+
+ for (c = w->clients; c; c = c->next) {
+ assert(c->callback);
+ if (c->callback(w, &e, c->userdata) != 0)
+ break;
+ }
+ }
+}
+
+/* IO notification event for the X11 display connection */
+static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+ pa_x11_wrapper *w = userdata;
+ assert(m && e && fd >= 0 && w && w->ref >= 1);
+ work(w);
+}
+
+/* Deferred notification event. Called once each main loop iteration */
+static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+ pa_x11_wrapper *w = userdata;
+ assert(m && e && w && w->ref >= 1);
+ work(w);
+}
+
+/* IO notification event for X11 internal connections */
+static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+ pa_x11_wrapper *w = userdata;
+ assert(m && e && fd >= 0 && w && w->ref >= 1);
+
+ XProcessInternalConnection(w->display, fd);
+}
+
+/* Add a new IO source for the specified X11 internal connection */
+static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) {
+ pa_x11_internal *i;
+ assert(i && fd >= 0);
+
+ i = pa_xmalloc(sizeof(pa_x11_internal));
+ i->wrapper = w;
+ i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w);
+ i->fd = fd;
+
+ PA_LLIST_PREPEND(pa_x11_internal, w->internals, i);
+ return i;
+}
+
+/* Remove an IO source for an X11 internal connection */
+static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) {
+ assert(i);
+
+ PA_LLIST_REMOVE(pa_x11_internal, w->internals, i);
+ w->core->mainloop->io_free(i->io_event);
+ pa_xfree(i);
+}
+
+/* Implementation of XConnectionWatchProc */
+static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) {
+ pa_x11_wrapper *w = (pa_x11_wrapper*) userdata;
+ assert(display && w && fd >= 0);
+
+ if (opening)
+ *watch_data = (XPointer) x11_internal_add(w, fd);
+ else
+ x11_internal_remove(w, (pa_x11_internal*) *watch_data);
+}
+
+static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {
+ pa_x11_wrapper*w;
+ Display *d;
+ int r;
+
+ if (!(d = XOpenDisplay(name))) {
+ pa_log(__FILE__": XOpenDisplay() failed\n");
+ return NULL;
+ }
+
+ w = pa_xmalloc(sizeof(pa_x11_wrapper));
+ w->core = c;
+ w->ref = 1;
+ w->property_name = pa_xstrdup(t);
+ w->display = d;
+
+ PA_LLIST_HEAD_INIT(pa_x11_client, w->clients);
+ PA_LLIST_HEAD_INIT(pa_x11_internal, w->internals);
+
+ w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w);
+ w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w);
+
+ XAddConnectionWatch(d, x11_watch, (XPointer) w);
+
+ r = pa_property_set(c, w->property_name, w);
+ assert(r >= 0);
+
+ return w;
+}
+
+static void x11_wrapper_free(pa_x11_wrapper*w) {
+ int r;
+ assert(w);
+
+ r = pa_property_remove(w->core, w->property_name);
+ assert(r >= 0);
+
+ assert(!w->clients);
+
+ XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
+ XCloseDisplay(w->display);
+
+ w->core->mainloop->io_free(w->io_event);
+ w->core->mainloop->defer_free(w->defer_event);
+
+ while (w->internals)
+ x11_internal_remove(w, w->internals);
+
+ pa_xfree(w->property_name);
+ pa_xfree(w);
+}
+
+pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {
+ char t[256];
+ pa_x11_wrapper *w;
+ assert(c);
+
+ snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : "");
+ if ((w = pa_property_get(c, t)))
+ return pa_x11_wrapper_ref(w);
+
+ return x11_wrapper_new(c, name, t);
+}
+
+pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) {
+ assert(w && w->ref >= 1);
+ w->ref++;
+ return w;
+}
+
+void pa_x11_wrapper_unref(pa_x11_wrapper* w) {
+ assert(w && w->ref >= 1);
+
+ if (!(--w->ref))
+ x11_wrapper_free(w);
+}
+
+Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {
+ assert(w && w->ref >= 1);
+ return w->display;
+}
+
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata) {
+ pa_x11_client *c;
+ assert(w && w->ref >= 1);
+
+ c = pa_xmalloc(sizeof(pa_x11_client));
+ c->wrapper = w;
+ c->callback = cb;
+ c->userdata = userdata;
+
+ PA_LLIST_PREPEND(pa_x11_client, w->clients, c);
+
+ return c;
+}
+
+void pa_x11_client_free(pa_x11_client *c) {
+ assert(c && c->wrapper && c->wrapper->ref >= 1);
+
+ PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c);
+ pa_xfree(c);
+}
diff --git a/src/polypcore/x11wrap.h b/src/polypcore/x11wrap.h
new file mode 100644
index 00000000..15645168
--- /dev/null
+++ b/src/polypcore/x11wrap.h
@@ -0,0 +1,52 @@
+#ifndef foox11wraphfoo
+#define foox11wraphfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <X11/Xlib.h>
+
+#include "core.h"
+
+typedef struct pa_x11_wrapper pa_x11_wrapper;
+
+/* Return the X11 wrapper for this core. In case no wrapper was
+ existant before, allocate a new one */
+pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name);
+
+/* Increase the wrapper's reference count by one */
+pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w);
+
+/* Decrease the reference counter of an X11 wrapper object */
+void pa_x11_wrapper_unref(pa_x11_wrapper* w);
+
+/* Return the X11 display object for this connection */
+Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w);
+
+typedef struct pa_x11_client pa_x11_client;
+
+/* Register an X11 client, that is called for each X11 event */
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata);
+
+/* Free an X11 client object */
+void pa_x11_client_free(pa_x11_client *c);
+
+#endif
diff --git a/src/polypcore/xmalloc.c b/src/polypcore/xmalloc.c
new file mode 100644
index 00000000..bf366347
--- /dev/null
+++ b/src/polypcore/xmalloc.c
@@ -0,0 +1,122 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdlib.h>
+#include <signal.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include "memory.h"
+#include "util.h"
+#include "xmalloc.h"
+#include "gccmacro.h"
+
+/* Make sure not to allocate more than this much memory. */
+#define MAX_ALLOC_SIZE (1024*1024*20) /* 20MB */
+
+/* #undef malloc */
+/* #undef free */
+/* #undef realloc */
+/* #undef strndup */
+/* #undef strdup */
+
+static void oom(void) PA_GCC_NORETURN;
+
+/** called in case of an OOM situation. Prints an error message and
+ * exits */
+static void oom(void) {
+ static const char e[] = "Not enough memory\n";
+ pa_loop_write(STDERR_FILENO, e, sizeof(e)-1);
+#ifdef SIGQUIT
+ raise(SIGQUIT);
+#endif
+ _exit(1);
+}
+
+void* pa_xmalloc(size_t size) {
+ void *p;
+ assert(size > 0);
+ assert(size < MAX_ALLOC_SIZE);
+
+ if (!(p = malloc(size)))
+ oom();
+
+ return p;
+}
+
+void* pa_xmalloc0(size_t size) {
+ void *p;
+ assert(size > 0);
+ assert(size < MAX_ALLOC_SIZE);
+
+ if (!(p = calloc(1, size)))
+ oom();
+
+ return p;
+}
+
+void *pa_xrealloc(void *ptr, size_t size) {
+ void *p;
+ assert(size > 0);
+ assert(size < MAX_ALLOC_SIZE);
+
+ if (!(p = realloc(ptr, size)))
+ oom();
+ return p;
+}
+
+void* pa_xmemdup(const void *p, size_t l) {
+ if (!p)
+ return NULL;
+ else {
+ char *r = pa_xmalloc(l);
+ memcpy(r, p, l);
+ return r;
+ }
+}
+
+char *pa_xstrdup(const char *s) {
+ if (!s)
+ return NULL;
+
+ return pa_xmemdup(s, strlen(s)+1);
+}
+
+char *pa_xstrndup(const char *s, size_t l) {
+ if (!s)
+ return NULL;
+ else {
+ char *r;
+ size_t t = strlen(s);
+
+ if (t > l)
+ t = l;
+
+ r = pa_xmemdup(s, t+1);
+ r[t] = 0;
+ return r;
+ }
+}
+
diff --git a/src/polypcore/xmalloc.h b/src/polypcore/xmalloc.h
new file mode 100644
index 00000000..2946011a
--- /dev/null
+++ b/src/polypcore/xmalloc.h
@@ -0,0 +1,58 @@
+#ifndef foomemoryhfoo
+#define foomemoryhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
+
+void* pa_xmalloc(size_t l);
+void *pa_xmalloc0(size_t l);
+void *pa_xrealloc(void *ptr, size_t size);
+#define pa_xfree free
+
+char *pa_xstrdup(const char *s);
+char *pa_xstrndup(const char *s, size_t l);
+
+void* pa_xmemdup(const void *p, size_t l);
+
+/** Internal helper for pa_xnew() */
+static inline void* pa_xnew_internal(unsigned n, size_t k) {
+ assert(n < INT_MAX/k);
+ return pa_xmalloc(n*k);
+}
+
+/** Allocate n new structures of the specified type. */
+#define pa_xnew(type, n) ((type*) pa_xnew_internal((n), sizeof(type)))
+
+/** Internal helper for pa_xnew0() */
+static inline void* pa_xnew0_internal(unsigned n, size_t k) {
+ assert(n < INT_MAX/k);
+ return pa_xmalloc0(n*k);
+}
+
+/** Same as pa_xnew() but set the memory to zero */
+#define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type)))
+
+#endif
diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c
new file mode 100644
index 00000000..97a8a0dd
--- /dev/null
+++ b/src/tests/cpulimit-test.c
@@ -0,0 +1,92 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the
+ License, or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with polypaudio; 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 <assert.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include "../daemon/cpulimit.h"
+#include <polyp/mainloop.h>
+#include <polypcore/gccmacro.h>
+
+#ifdef TEST2
+#include <polyp/mainloop-signal.h>
+#endif
+
+/* A simple example for testing the cpulimit subsystem */
+
+static time_t start;
+
+#ifdef TEST2
+
+static void func(pa_mainloop_api *m, PA_GCC_UNUSED pa_signal_event *e, PA_GCC_UNUSED int sig, PA_GCC_UNUSED void *userdata) {
+ time_t now;
+ time(&now);
+
+ if ((now - start) >= 30) {
+ m->quit(m, 1);
+ fprintf(stderr, "Test failed\n");
+ } else
+ raise(SIGUSR1);
+}
+
+#endif
+
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
+ pa_mainloop *m;
+
+ m = pa_mainloop_new();
+ assert(m);
+
+ pa_cpu_limit_init(pa_mainloop_get_api(m));
+
+ time(&start);
+
+#ifdef TEST2
+ pa_signal_init(pa_mainloop_get_api(m));
+ pa_signal_new(SIGUSR1, func, NULL);
+ raise(SIGUSR1);
+ pa_mainloop_run(m, NULL);
+ pa_signal_done();
+#else
+ for (;;) {
+ time_t now;
+ time(&now);
+
+ if ((now - start) >= 30) {
+ fprintf(stderr, "Test failed\n");
+ break;
+ }
+ }
+#endif
+
+ pa_cpu_limit_done();
+
+ pa_mainloop_free(m);
+
+ return 0;
+}
diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c
new file mode 100644
index 00000000..f62c9693
--- /dev/null
+++ b/src/tests/mainloop-test.c
@@ -0,0 +1,146 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <assert.h>
+
+#include <polypcore/util.h>
+#include <polypcore/gccmacro.h>
+
+#ifdef GLIB_MAIN_LOOP
+
+#include <glib.h>
+#include <polyp/glib-mainloop.h>
+
+static GMainLoop* glib_main_loop = NULL;
+
+#if GLIB_MAJOR_VERSION >= 2
+#define GLIB20
+#else
+#undef GLIB20
+#endif
+
+
+#else /* GLIB_MAIN_LOOP */
+#include <polyp/mainloop.h>
+#endif /* GLIB_MAIN_LOOP */
+
+static pa_defer_event *de;
+
+static void iocb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+ unsigned char c;
+ read(fd, &c, sizeof(c));
+ fprintf(stderr, "IO EVENT: %c\n", c < 32 ? '.' : c);
+ a->defer_enable(de, 1);
+}
+
+static void dcb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
+ fprintf(stderr, "DEFER EVENT\n");
+ a->defer_enable(e, 0);
+}
+
+static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ fprintf(stderr, "TIME EVENT\n");
+
+#if defined(GLIB_MAIN_LOOP) && defined(GLIB20)
+ g_main_loop_quit(glib_main_loop);
+#elif defined(GLIB_MAIN_LOOP)
+ g_main_quit(glib_main_loop);
+#else
+ a->quit(a, 0);
+#endif
+}
+
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
+ pa_mainloop_api *a;
+ pa_io_event *ioe;
+ pa_time_event *te;
+ struct timeval tv;
+
+#ifdef GLIB_MAIN_LOOP
+ pa_glib_mainloop *g;
+
+#ifdef GLIB20
+ glib_main_loop = g_main_loop_new(NULL, FALSE);
+ assert(glib_main_loop);
+
+ g = pa_glib_mainloop_new(NULL);
+#else /* GLIB20 */
+ glib_main_loop = g_main_new(FALSE);
+ assert(glib_main_loop);
+
+ g = pa_glib_mainloop_new();
+#endif /* GLIB20 */
+ assert(g);
+
+ a = pa_glib_mainloop_get_api(g);
+ assert(a);
+#else /* GLIB_MAIN_LOOP */
+ pa_mainloop *m;
+
+ m = pa_mainloop_new();
+ assert(m);
+
+ a = pa_mainloop_get_api(m);
+ assert(a);
+#endif /* GLIB_MAIN_LOOP */
+
+ ioe = a->io_new(a, 0, PA_IO_EVENT_INPUT, iocb, NULL);
+ assert(ioe);
+
+ de = a->defer_new(a, dcb, NULL);
+ assert(de);
+
+ pa_gettimeofday(&tv);
+ tv.tv_sec += 10;
+ te = a->time_new(a, &tv, tcb, NULL);
+
+#if defined(GLIB_MAIN_LOOP) && defined(GLIB20)
+ g_main_loop_run(glib_main_loop);
+#elif defined(GLIB_MAIN_LOOP)
+ g_main_run(glib_main_loop);
+#else
+ pa_mainloop_run(m, NULL);
+#endif
+
+ a->time_free(te);
+ a->defer_free(de);
+ a->io_free(ioe);
+
+#ifdef GLIB_MAIN_LOOP
+ pa_glib_mainloop_free(g);
+#ifdef GLIB20
+ g_main_loop_unref(glib_main_loop);
+#else
+ g_main_destroy(glib_main_loop);
+#endif
+#else
+ pa_mainloop_free(m);
+#endif
+
+ return 0;
+}
diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c
new file mode 100644
index 00000000..861c38c1
--- /dev/null
+++ b/src/tests/mcalign-test.c
@@ -0,0 +1,96 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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.
+
+ polypaudio 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 polypaudio; 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 <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <polypcore/util.h>
+#include <polypcore/mcalign.h>
+#include <polypcore/gccmacro.h>
+
+/* A simple program for testing pa_mcalign */
+
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
+ pa_mcalign *a = pa_mcalign_new(11, NULL);
+ pa_memchunk c;
+
+ pa_memchunk_reset(&c);
+
+ srand(time(NULL));
+
+ for (;;) {
+ ssize_t r;
+ size_t l;
+
+ if (!c.memblock) {
+ c.memblock = pa_memblock_new(2048, NULL);
+ c.index = c.length = 0;
+ }
+
+ assert(c.index < c.memblock->length);
+
+ l = c.memblock->length - c.index;
+
+ l = l <= 1 ? l : rand() % (l-1) +1 ;
+
+ if ((r = read(STDIN_FILENO, (uint8_t*) c.memblock->data + c.index, l)) <= 0) {
+ fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
+ break;
+ }
+
+ c.length = r;
+ pa_mcalign_push(a, &c);
+ fprintf(stderr, "Read %d bytes\n", r);
+
+ c.index += r;
+
+ if (c.index >= c.memblock->length) {
+ pa_memblock_unref(c.memblock);
+ pa_memchunk_reset(&c);
+ }
+
+ for (;;) {
+ pa_memchunk t;
+
+ if (pa_mcalign_pop(a, &t) < 0)
+ break;
+
+ pa_loop_write(STDOUT_FILENO, (uint8_t*) t.memblock->data + t.index, t.length);
+ fprintf(stderr, "Wrote %lu bytes.\n", (unsigned long) t.length);
+
+ pa_memblock_unref(t.memblock);
+ }
+ }
+
+ pa_mcalign_free(a);
+
+ if (c.memblock)
+ pa_memblock_unref(c.memblock);
+}
diff --git a/src/tests/pacat-simple.c b/src/tests/pacat-simple.c
new file mode 100644
index 00000000..8b3a7b22
--- /dev/null
+++ b/src/tests/pacat-simple.c
@@ -0,0 +1,101 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <polyp/polyplib-simple.h>
+#include <polyp/polyplib-error.h>
+#include <polypcore/gccmacro.h>
+
+#define BUFSIZE 1024
+
+int main(PA_GCC_UNUSED int argc, char*argv[]) {
+
+ /* The Sample format to use */
+ static const pa_sample_spec ss = {
+ .format = PA_SAMPLE_S16LE,
+ .rate = 44100,
+ .channels = 2
+ };
+
+ pa_simple *s = NULL;
+ int ret = 1;
+ int error;
+
+ /* Create a new playback stream */
+ if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, &error))) {
+ fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
+ goto finish;
+ }
+
+ for (;;) {
+ uint8_t buf[BUFSIZE];
+ ssize_t r;
+
+#if 0
+ pa_usec_t latency;
+
+ if ((latency = pa_simple_get_playback_latency(s, &error)) == (pa_usec_t) -1) {
+ fprintf(stderr, __FILE__": pa_simple_get_playback_latency() failed: %s\n", pa_strerror(error));
+ goto finish;
+ }
+
+ fprintf(stderr, "%0.0f usec \r", (float)latency);
+#endif
+
+ /* Read some data ... */
+ if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) {
+ if (r == 0) /* EOF */
+ break;
+
+ fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno));
+ goto finish;
+ }
+
+ /* ... and play it */
+ if (pa_simple_write(s, buf, r, &error) < 0) {
+ fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
+ goto finish;
+ }
+ }
+
+ /* Make sure that every single sample was played */
+ if (pa_simple_drain(s, &error) < 0) {
+ fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+
+ if (s)
+ pa_simple_free(s);
+
+ return ret;
+}
diff --git a/src/tests/parec-simple.c b/src/tests/parec-simple.c
new file mode 100644
index 00000000..10eaea8d
--- /dev/null
+++ b/src/tests/parec-simple.c
@@ -0,0 +1,100 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <polyp/polyplib-simple.h>
+#include <polyp/polyplib-error.h>
+#include <polypcore/gccmacro.h>
+
+#define BUFSIZE 1024
+
+/* A simple routine calling UNIX write() in a loop */
+static ssize_t loop_write(int fd, const void*data, size_t size) {
+ ssize_t ret = 0;
+
+ while (size > 0) {
+ ssize_t r;
+
+ if ((r = write(fd, data, size)) < 0)
+ return r;
+
+ if (r == 0)
+ break;
+
+ ret += r;
+ data = (const uint8_t*) data + r;
+ size -= r;
+ }
+
+ return ret;
+}
+
+int main(PA_GCC_UNUSED int argc, char*argv[]) {
+ /* The sample type to use */
+ static const pa_sample_spec ss = {
+ .format = PA_SAMPLE_S16LE,
+ .rate = 44100,
+ .channels = 2
+ };
+ pa_simple *s = NULL;
+ int ret = 1;
+ int error;
+
+ /* Create the recording stream */
+ if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, &error))) {
+ fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
+ goto finish;
+ }
+
+ for (;;) {
+ uint8_t buf[BUFSIZE];
+ ssize_t r;
+
+ /* Record some data ... */
+ if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
+ fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
+ goto finish;
+ }
+
+ /* And write it to STDOUT */
+ if ((r = loop_write(STDOUT_FILENO, buf, sizeof(buf))) <= 0) {
+ fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
+ goto finish;
+ }
+ }
+
+ ret = 0;
+
+finish:
+
+ if (s)
+ pa_simple_free(s);
+
+ return ret;
+}
diff --git a/src/tests/strlist-test.c b/src/tests/strlist-test.c
new file mode 100644
index 00000000..14543112
--- /dev/null
+++ b/src/tests/strlist-test.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+
+#include <polypcore/strlist.h>
+#include <polypcore/xmalloc.h>
+#include <polypcore/gccmacro.h>
+
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char* argv[]) {
+ char *t, *u;
+ pa_strlist *l = NULL;
+
+ l = pa_strlist_prepend(l, "e");
+ l = pa_strlist_prepend(l, "d");
+ l = pa_strlist_prepend(l, "c");
+ l = pa_strlist_prepend(l, "b");
+ l = pa_strlist_prepend(l, "a");
+
+ t = pa_strlist_tostring(l);
+ pa_strlist_free(l);
+
+ fprintf(stderr, "1: %s\n", t);
+
+ l = pa_strlist_parse(t);
+ pa_xfree(t);
+
+ t = pa_strlist_tostring(l);
+ fprintf(stderr, "2: %s\n", t);
+ pa_xfree(t);
+
+ l = pa_strlist_pop(l, &u);
+ fprintf(stderr, "3: %s\n", u);
+ pa_xfree(u);
+
+ l = pa_strlist_remove(l, "c");
+
+ t = pa_strlist_tostring(l);
+ fprintf(stderr, "4: %s\n", t);
+ pa_xfree(t);
+
+ pa_strlist_free(l);
+
+ return 0;
+}
diff --git a/src/tests/voltest.c b/src/tests/voltest.c
new file mode 100644
index 00000000..58f1da00
--- /dev/null
+++ b/src/tests/voltest.c
@@ -0,0 +1,20 @@
+/* $Id$ */
+
+#include <stdio.h>
+
+#include <polyp/volume.h>
+#include <polypcore/gccmacro.h>
+
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
+ pa_volume_t v;
+
+ for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) {
+
+ double dB = pa_sw_volume_to_dB(v);
+ double f = pa_sw_volume_to_linear(v);
+
+ printf("Volume: %3i; percent: %i%%; decibel %0.2f; linear = %0.2f; volume(decibel): %3i; volume(linear): %3i\n",
+ v, (v*100)/PA_VOLUME_NORM, dB, f, pa_sw_volume_from_dB(dB), pa_sw_volume_from_linear(f));
+
+ }
+}
diff --git a/src/utils/esdcompat.sh.in b/src/utils/esdcompat.sh.in
new file mode 100755
index 00000000..76023f52
--- /dev/null
+++ b/src/utils/esdcompat.sh.in
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+# $Id$
+#
+# This file is part of polypaudio.
+#
+# polypaudio 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 of the License, or
+# (at your option) any later version.
+#
+# polypaudio 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with polypaudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+VERSION_STRING="@PACKAGE_NAME@ esd wrapper @PACKAGE_VERSION@"
+
+fail() {
+ echo "ERROR: $1"
+ exit 1
+}
+
+ARGS=" --log-target=syslog"
+
+for N in $(seq $#) ; do
+
+ case "$1" in
+ "")
+ ;;
+
+ -v|--version)
+ echo "$VERSION_STRING"
+ exit 0
+ ;;
+
+ -h|--help)
+ cat <<EOF
+$VERSION_STRING
+
+Usage: $0 [options]
+
+ -v --version print version information
+ -h --help show this help
+
+Ignored directives:
+
+ -tcp use tcp/ip sockets in addition to unix domain
+ -promiscuous don't require authentication
+ -d DEVICE force esd to use sound device DEVICE
+ -b run server in 8 bit sound mode
+ -r RATE run server at sample rate of RATE
+ -as SECS free audio device after SECS of inactivity
+ -unix use unix domain sockets instead of tcp/ip
+ -public make tcp/ip access public (other than localhost)
+ -terminate terminate esd daemone after last client exits
+ -nobeeps disable startup beeps
+ -trust start esd even if use of /tmp/.esd can be insecure
+ -port PORT listen for connections at PORT (only for tcp/ip)
+ -bind ADDRESS binds to ADDRESS (only for tcp/ip)
+EOF
+ exit 0
+ ;;
+
+ -spawnpid)
+ shift
+ ARGS="$ARGS '-Lmodule-esound-compat-spawnpid pid=$1'"
+ ;;
+
+ -spawnfd)
+ shift
+ ARGS="$ARGS '-Lmodule-esound-compat-spawnfd fd=$1'"
+ ;;
+
+ -unix|-b|-public|-terminate|-nobeeps|-trust|-tcp|-promiscuous)
+ # Ignore these commands
+ ;;
+
+ -d|-r|-as|-port|-bind)
+ # Ignore these commands and their arguments
+ shift
+
+ ;;
+
+ *)
+ fail "Unknown command: $1"
+ ;;
+ esac
+
+ shift
+done
+
+eval "exec '@POLYPAUDIO_BINARY@'$ARGS"
diff --git a/src/utils/pabrowse.c b/src/utils/pabrowse.c
new file mode 100644
index 00000000..634c308a
--- /dev/null
+++ b/src/utils/pabrowse.c
@@ -0,0 +1,139 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <assert.h>
+#include <signal.h>
+
+#include <polyp/mainloop.h>
+#include <polyp/mainloop-signal.h>
+#include <polyp/polyplib-browser.h>
+#include <polyp/typeid.h>
+
+static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
+ fprintf(stderr, "Got signal, exiting\n");
+ m->quit(m, 0);
+}
+
+static void dump_server(const pa_browse_info *i) {
+ char t[16];
+
+ if (i->cookie)
+ snprintf(t, sizeof(t), "0x%08x", *i->cookie);
+
+ printf("server: %s\n"
+ "server-version: %s\n"
+ "user-name: %s\n"
+ "fqdn: %s\n"
+ "cookie: %s\n",
+ i->server,
+ i->server_version ? i->server_version : "n/a",
+ i->user_name ? i->user_name : "n/a",
+ i->fqdn ? i->fqdn : "n/a",
+ i->cookie ? t : "n/a");
+}
+
+static void dump_device(const pa_browse_info *i) {
+ char t[16], ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
+ if (i->sample_spec)
+ pa_sample_spec_snprint(ss, sizeof(ss), i->sample_spec);
+
+ if (i->typeid)
+ pa_typeid_to_string(*i->typeid, t, sizeof(t));
+
+ printf("device: %s\n"
+ "description: %s\n"
+ "type: %s\n"
+ "sample spec: %s\n",
+ i->device,
+ i->description ? i->description : "n/a",
+ i->typeid ? t : "n/a",
+ i->sample_spec ? ss : "n/a");
+
+}
+
+static void browser_callback(pa_browser *b, pa_browse_opcode c, const pa_browse_info *i, void *userdata) {
+ assert(b && i);
+
+ switch (c) {
+
+ case PA_BROWSE_NEW_SERVER:
+ printf("\n=> new server <%s>\n", i->name);
+ dump_server(i);
+ break;
+
+ case PA_BROWSE_NEW_SINK:
+ printf("\n=> new sink <%s>\n", i->name);
+ dump_server(i);
+ dump_device(i);
+ break;
+
+ case PA_BROWSE_NEW_SOURCE:
+ printf("\n=> new source <%s>\n", i->name);
+ dump_server(i);
+ dump_device(i);
+ break;
+
+ case PA_BROWSE_REMOVE:
+ printf("\n=> removed service <%s>\n", i->name);
+ break;
+
+ default:
+ ;
+ }
+}
+
+
+int main(int argc, char *argv[]) {
+ pa_mainloop *mainloop = NULL;
+ pa_browser *browser = NULL;
+ int ret = 1, r;
+
+ if (!(mainloop = pa_mainloop_new()))
+ goto finish;
+
+ r = pa_signal_init(pa_mainloop_get_api(mainloop));
+ assert(r == 0);
+ pa_signal_new(SIGINT, exit_signal_callback, NULL);
+ pa_signal_new(SIGTERM, exit_signal_callback, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (!(browser = pa_browser_new(pa_mainloop_get_api(mainloop))))
+ goto finish;
+
+ pa_browser_set_callback(browser, browser_callback, NULL);
+
+ ret = 0;
+ pa_mainloop_run(mainloop, &ret);
+
+finish:
+ if (mainloop) {
+ pa_signal_done();
+ pa_mainloop_free(mainloop);
+ }
+
+ return ret;
+}
diff --git a/src/utils/pacat.c b/src/utils/pacat.c
new file mode 100644
index 00000000..bd2b64fd
--- /dev/null
+++ b/src/utils/pacat.c
@@ -0,0 +1,541 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <polyp/polyplib.h>
+#include <polyp/polyplib-error.h>
+#include <polyp/mainloop.h>
+#include <polyp/mainloop-signal.h>
+#include <polyp/polyplib-version.h>
+
+#if PA_API_VERSION != 8
+#error Invalid Polypaudio API version
+#endif
+
+static enum { RECORD, PLAYBACK } mode = PLAYBACK;
+
+static pa_context *context = NULL;
+static pa_stream *stream = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+
+static void *buffer = NULL;
+static size_t buffer_length = 0, buffer_index = 0;
+
+static pa_io_event* stdio_event = NULL;
+
+static char *stream_name = NULL, *client_name = NULL, *device = NULL;
+
+static int verbose = 0;
+static pa_volume_t volume = PA_VOLUME_NORM;
+
+static pa_sample_spec sample_spec = {
+ .format = PA_SAMPLE_S16LE,
+ .rate = 44100,
+ .channels = 2
+};
+
+/* A shortcut for terminating the application */
+static void quit(int ret) {
+ assert(mainloop_api);
+ mainloop_api->quit(mainloop_api, ret);
+}
+
+/* Write some data to the stream */
+static void do_stream_write(size_t length) {
+ size_t l;
+ assert(length);
+
+ if (!buffer || !buffer_length)
+ return;
+
+ l = length;
+ if (l > buffer_length)
+ l = buffer_length;
+
+ pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0);
+ buffer_length -= l;
+ buffer_index += l;
+
+ if (!buffer_length) {
+ free(buffer);
+ buffer = NULL;
+ buffer_index = buffer_length = 0;
+ }
+}
+
+/* This is called whenever new data may be written to the stream */
+static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
+ assert(s && length);
+
+ if (stdio_event)
+ mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
+
+ if (!buffer)
+ return;
+
+ do_stream_write(length);
+}
+
+/* This is called whenever new data may is available */
+static void stream_read_callback(pa_stream *s, const void*data, size_t length, void *userdata) {
+ assert(s && data && length);
+
+ if (stdio_event)
+ mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
+
+ if (buffer) {
+ fprintf(stderr, "Buffer overrun, dropping incoming data\n");
+ return;
+ }
+
+ buffer = malloc(buffer_length = length);
+ assert(buffer);
+ memcpy(buffer, data, length);
+ buffer_index = 0;
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ switch (pa_stream_get_state(s)) {
+ case PA_STREAM_CREATING:
+ case PA_STREAM_TERMINATED:
+ break;
+
+ case PA_STREAM_READY:
+ if (verbose)
+ fprintf(stderr, "Stream successfully created\n");
+ break;
+
+ case PA_STREAM_FAILED:
+ default:
+ fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+ quit(1);
+ }
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+ assert(c);
+
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+
+ case PA_CONTEXT_READY:
+
+ assert(c && !stream);
+
+ if (verbose)
+ fprintf(stderr, "Connection established.\n");
+
+ stream = pa_stream_new(c, stream_name, &sample_spec, NULL);
+ assert(stream);
+
+ pa_stream_set_state_callback(stream, stream_state_callback, NULL);
+ pa_stream_set_write_callback(stream, stream_write_callback, NULL);
+ pa_stream_set_read_callback(stream, stream_read_callback, NULL);
+
+ if (mode == PLAYBACK) {
+ pa_cvolume cv;
+ pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, PA_CHANNELS_MAX, volume));
+ } else
+ pa_stream_connect_record(stream, device, NULL, 0);
+
+ break;
+
+ case PA_CONTEXT_TERMINATED:
+ quit(0);
+ break;
+
+ case PA_CONTEXT_FAILED:
+ default:
+ fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ }
+}
+
+/* Connection draining complete */
+static void context_drain_complete(pa_context*c, void *userdata) {
+ pa_context_disconnect(c);
+}
+
+/* Stream draining complete */
+static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
+ pa_operation *o;
+
+ if (!success) {
+ fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
+ quit(1);
+ }
+
+ if (verbose)
+ fprintf(stderr, "Playback stream drained.\n");
+
+ pa_stream_disconnect(stream);
+ pa_stream_unref(stream);
+ stream = NULL;
+
+ if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+ pa_context_disconnect(context);
+ else {
+ pa_operation_unref(o);
+
+ if (verbose)
+ fprintf(stderr, "Draining connection to server.\n");
+ }
+}
+
+/* New data on STDIN **/
+static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+ size_t l, w = 0;
+ ssize_t r;
+ assert(a == mainloop_api && e && stdio_event == e);
+
+ if (buffer) {
+ mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
+ return;
+ }
+
+ if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
+ l = 4096;
+
+ buffer = malloc(l);
+ assert(buffer);
+ if ((r = read(fd, buffer, l)) <= 0) {
+ if (r == 0) {
+ if (verbose)
+ fprintf(stderr, "Got EOF.\n");
+ pa_operation_unref(pa_stream_drain(stream, stream_drain_complete, NULL));
+ } else {
+ fprintf(stderr, "read() failed: %s\n", strerror(errno));
+ quit(1);
+ }
+
+ mainloop_api->io_free(stdio_event);
+ stdio_event = NULL;
+ return;
+ }
+
+ buffer_length = r;
+ buffer_index = 0;
+
+ if (w)
+ do_stream_write(w);
+}
+
+/* Some data may be written to STDOUT */
+static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+ ssize_t r;
+ assert(a == mainloop_api && e && stdio_event == e);
+
+ if (!buffer) {
+ mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
+ return;
+ }
+
+ assert(buffer_length);
+
+ if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
+ fprintf(stderr, "write() failed: %s\n", strerror(errno));
+ quit(1);
+
+ mainloop_api->io_free(stdio_event);
+ stdio_event = NULL;
+ return;
+ }
+
+ buffer_length -= r;
+ buffer_index += r;
+
+ if (!buffer_length) {
+ free(buffer);
+ buffer = NULL;
+ buffer_length = buffer_index = 0;
+ }
+}
+
+/* UNIX signal to quit recieved */
+static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
+ if (verbose)
+ fprintf(stderr, "Got signal, exiting.\n");
+ quit(0);
+
+}
+
+/* Show the current latency */
+static void stream_get_latency_callback(pa_stream *s, const pa_latency_info *i, void *userdata) {
+ pa_usec_t total;
+ int negative = 0;
+ assert(s);
+
+ if (!i) {
+ fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
+ quit(1);
+ return;
+ }
+
+ total = pa_stream_get_latency(s, i, &negative);
+
+ fprintf(stderr, "Latency: buffer: %0.0f usec; sink: %0.0f usec; source: %0.0f usec; transport: %0.0f usec; total: %0.0f usec; synchronized clocks: %s.\n",
+ (float) i->buffer_usec, (float) i->sink_usec, (float) i->source_usec, (float) i->transport_usec, (float) total * (negative?-1:1),
+ i->synchronized_clocks ? "yes" : "no");
+}
+
+/* Someone requested that the latency is shown */
+static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
+ fprintf(stderr, "Got SIGUSR1, requesting latency.\n");
+ pa_operation_unref(pa_stream_get_latency_info(stream, stream_get_latency_callback, NULL));
+}
+
+
+static void help(const char *argv0) {
+
+ printf("%s [options]\n\n"
+ " -h, --help Show this help\n"
+ " --version Show version\n\n"
+ " -r, --record Create a connection for recording\n"
+ " -p, --playback Create a connection for playback\n\n"
+ " -v, --verbose Enable verbose operations\n\n"
+ " -s, --server=SERVER The name of the server to connect to\n"
+ " -d, --device=DEVICE The name of the sink/source to connect to\n"
+ " -n, --client-name=NAME How to call this client on the server\n"
+ " --stream-name=NAME How to call this stream on the server\n"
+ " --volume=VOLUME Specify the initial (linear) volume in range 0...256\n"
+ " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
+ " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
+ " float32be, ulaw, alaw (defaults to s16ne)\n"
+ " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
+ " (defaults to 2)\n",
+ argv0);
+}
+
+enum {
+ ARG_VERSION = 256,
+ ARG_STREAM_NAME,
+ ARG_VOLUME,
+ ARG_SAMPLERATE,
+ ARG_SAMPLEFORMAT,
+ ARG_CHANNELS
+};
+
+int main(int argc, char *argv[]) {
+ pa_mainloop* m = NULL;
+ int ret = 1, r, c;
+ char *bn, *server = NULL;
+
+ static const struct option long_options[] = {
+ {"record", 0, NULL, 'r'},
+ {"playback", 0, NULL, 'p'},
+ {"device", 1, NULL, 'd'},
+ {"server", 1, NULL, 's'},
+ {"client-name", 1, NULL, 'n'},
+ {"stream-name", 1, NULL, ARG_STREAM_NAME},
+ {"version", 0, NULL, ARG_VERSION},
+ {"help", 0, NULL, 'h'},
+ {"verbose", 0, NULL, 'v'},
+ {"volume", 1, NULL, ARG_VOLUME},
+ {"rate", 1, NULL, ARG_SAMPLERATE},
+ {"format", 1, NULL, ARG_SAMPLEFORMAT},
+ {"channels", 1, NULL, ARG_CHANNELS},
+ {NULL, 0, NULL, 0}
+ };
+
+ if (!(bn = strrchr(argv[0], '/')))
+ bn = argv[0];
+ else
+ bn++;
+
+ if (strstr(bn, "rec") || strstr(bn, "mon"))
+ mode = RECORD;
+ else if (strstr(bn, "cat") || strstr(bn, "play"))
+ mode = PLAYBACK;
+
+ while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
+
+ switch (c) {
+ case 'h' :
+ help(bn);
+ ret = 0;
+ goto quit;
+
+ case ARG_VERSION:
+ printf("pacat "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
+ ret = 0;
+ goto quit;
+
+ case 'r':
+ mode = RECORD;
+ break;
+
+ case 'p':
+ mode = PLAYBACK;
+ break;
+
+ case 'd':
+ free(device);
+ device = strdup(optarg);
+ break;
+
+ case 's':
+ free(server);
+ server = strdup(optarg);
+ break;
+
+ case 'n':
+ free(client_name);
+ client_name = strdup(optarg);
+ break;
+
+ case ARG_STREAM_NAME:
+ free(stream_name);
+ stream_name = strdup(optarg);
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case ARG_VOLUME: {
+ int v = atoi(optarg);
+ volume = v < 0 ? 0 : v;
+ break;
+ }
+
+ case ARG_CHANNELS:
+ sample_spec.channels = atoi(optarg);
+ break;
+
+ case ARG_SAMPLEFORMAT:
+ sample_spec.format = pa_parse_sample_format(optarg);
+ break;
+
+ case ARG_SAMPLERATE:
+ sample_spec.rate = atoi(optarg);
+ break;
+
+ default:
+ goto quit;
+ }
+ }
+
+ if (!client_name)
+ client_name = strdup(bn);
+
+ if (!stream_name)
+ stream_name = strdup(client_name);
+
+ if (!pa_sample_spec_valid(&sample_spec)) {
+ fprintf(stderr, "Invalid sample specification\n");
+ goto quit;
+ }
+
+ if (verbose) {
+ char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
+ fprintf(stderr, "Opening a %s stream with sample specification '%s'.\n", mode == RECORD ? "recording" : "playback", t);
+ }
+
+ /* Set up a new main loop */
+ if (!(m = pa_mainloop_new())) {
+ fprintf(stderr, "pa_mainloop_new() failed.\n");
+ goto quit;
+ }
+
+ mainloop_api = pa_mainloop_get_api(m);
+
+ r = pa_signal_init(mainloop_api);
+ assert(r == 0);
+ pa_signal_new(SIGINT, exit_signal_callback, NULL);
+ pa_signal_new(SIGTERM, exit_signal_callback, NULL);
+#ifdef SIGUSR1
+ pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
+#endif
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ if (!(stdio_event = mainloop_api->io_new(mainloop_api,
+ mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
+ mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
+ mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
+ fprintf(stderr, "source_io() failed.\n");
+ goto quit;
+ }
+
+ /* Create a new connection context */
+ if (!(context = pa_context_new(mainloop_api, client_name))) {
+ fprintf(stderr, "pa_context_new() failed.\n");
+ goto quit;
+ }
+
+ pa_context_set_state_callback(context, context_state_callback, NULL);
+
+ /* Connect the context */
+ pa_context_connect(context, server, 1, NULL);
+
+ /* Run the main loop */
+ if (pa_mainloop_run(m, &ret) < 0) {
+ fprintf(stderr, "pa_mainloop_run() failed.\n");
+ goto quit;
+ }
+
+quit:
+ if (stream)
+ pa_stream_unref(stream);
+
+ if (context)
+ pa_context_unref(context);
+
+ if (stdio_event) {
+ assert(mainloop_api);
+ mainloop_api->io_free(stdio_event);
+ }
+
+ if (m) {
+ pa_signal_done();
+ pa_mainloop_free(m);
+ }
+
+ free(buffer);
+
+ free(server);
+ free(device);
+ free(client_name);
+ free(stream_name);
+
+ return ret;
+}
diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c
new file mode 100644
index 00000000..4054bc2c
--- /dev/null
+++ b/src/utils/pacmd.c
@@ -0,0 +1,183 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <assert.h>
+#include <signal.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/un.h>
+
+#include <polypcore/util.h>
+#include <polypcore/log.h>
+#include <polypcore/pid.h>
+
+int main(PA_GCC_UNUSED int main, PA_GCC_UNUSED char*argv[]) {
+ pid_t pid ;
+ int fd = -1;
+ int ret = 1, i;
+ struct sockaddr_un sa;
+ char ibuf[256], obuf[256];
+ size_t ibuf_index, ibuf_length, obuf_index, obuf_length;
+ fd_set ifds, ofds;
+
+ if (pa_pid_file_check_running(&pid) < 0) {
+ pa_log(__FILE__": no Polypaudio daemon running\n");
+ goto fail;
+ }
+
+ if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ pa_log(__FILE__": socket(PF_UNIX, SOCK_STREAM, 0): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ pa_runtime_path("cli", sa.sun_path, sizeof(sa.sun_path));
+
+ for (i = 0; i < 5; i++) {
+ int r;
+
+ if ((r = connect(fd, (struct sockaddr*) &sa, sizeof(sa))) < 0 && (errno != ECONNREFUSED && errno != ENOENT)) {
+ pa_log(__FILE__": connect() failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (r >= 0)
+ break;
+
+ if (pa_pid_file_kill(SIGUSR2, NULL) < 0) {
+ pa_log(__FILE__": failed to kill Polypaudio daemon.\n");
+ goto fail;
+ }
+
+ pa_msleep(50);
+ }
+
+ if (i >= 5) {
+ pa_log(__FILE__": daemon not responding.\n");
+ goto fail;
+ }
+
+ ibuf_index = ibuf_length = obuf_index = obuf_length = 0;
+
+
+ FD_ZERO(&ifds);
+ FD_SET(0, &ifds);
+ FD_SET(fd, &ifds);
+
+ FD_ZERO(&ofds);
+
+ for (;;) {
+ if (select(FD_SETSIZE, &ifds, &ofds, NULL, NULL) < 0) {
+ pa_log(__FILE__": select() failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (FD_ISSET(0, &ifds)) {
+ ssize_t r;
+ assert(!ibuf_length);
+
+ if ((r = read(0, ibuf, sizeof(ibuf))) <= 0) {
+ if (r == 0)
+ break;
+
+ pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ ibuf_length = (size_t) r;
+ ibuf_index = 0;
+ }
+
+ if (FD_ISSET(fd, &ifds)) {
+ ssize_t r;
+ assert(!obuf_length);
+
+ if ((r = read(fd, obuf, sizeof(obuf))) <= 0) {
+ if (r == 0)
+ break;
+
+ pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ obuf_length = (size_t) r;
+ obuf_index = 0;
+ }
+
+ if (FD_ISSET(1, &ofds)) {
+ ssize_t r;
+ assert(obuf_length);
+
+ if ((r = write(1, obuf + obuf_index, obuf_length)) < 0) {
+ pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ obuf_length -= (size_t) r;
+ obuf_index += obuf_index;
+
+ }
+
+ if (FD_ISSET(fd, &ofds)) {
+ ssize_t r;
+ assert(ibuf_length);
+
+ if ((r = write(fd, ibuf + ibuf_index, ibuf_length)) < 0) {
+ pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ ibuf_length -= (size_t) r;
+ ibuf_index += obuf_index;
+
+ }
+
+ FD_ZERO(&ifds);
+ FD_ZERO(&ofds);
+
+ if (obuf_length <= 0)
+ FD_SET(fd, &ifds);
+ else
+ FD_SET(1, &ofds);
+
+ if (ibuf_length <= 0)
+ FD_SET(0, &ifds);
+ else
+ FD_SET(fd, &ofds);
+ }
+
+
+ ret = 0;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return ret;
+}
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
new file mode 100644
index 00000000..23bd924b
--- /dev/null
+++ b/src/utils/pactl.c
@@ -0,0 +1,784 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include <sndfile.h>
+
+#include <polyp/polyplib.h>
+#include <polyp/polyplib-error.h>
+#include <polyp/mainloop.h>
+#include <polyp/mainloop-signal.h>
+#include <polyp/sample.h>
+
+#if PA_API_VERSION != 8
+#error Invalid Polypaudio API version
+#endif
+
+#define BUFSIZE 1024
+
+static pa_context *context = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+
+static char *device = NULL, *sample_name = NULL;
+
+static SNDFILE *sndfile = NULL;
+static pa_stream *sample_stream = NULL;
+static pa_sample_spec sample_spec;
+static size_t sample_length = 0;
+
+static int actions = 1;
+
+static int nl = 0;
+
+static enum {
+ NONE,
+ EXIT,
+ STAT,
+ UPLOAD_SAMPLE,
+ PLAY_SAMPLE,
+ REMOVE_SAMPLE,
+ LIST
+} action = NONE;
+
+static void quit(int ret) {
+ assert(mainloop_api);
+ mainloop_api->quit(mainloop_api, ret);
+}
+
+
+static void context_drain_complete(pa_context *c, void *userdata) {
+ pa_context_disconnect(c);
+}
+
+static void drain(void) {
+ pa_operation *o;
+ if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+ pa_context_disconnect(context);
+ else
+ pa_operation_unref(o);
+}
+
+
+static void complete_action(void) {
+ assert(actions > 0);
+
+ if (!(--actions))
+ drain();
+}
+
+static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
+ char s[128];
+ if (!i) {
+ fprintf(stderr, "Failed to get statistics: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ pa_bytes_snprint(s, sizeof(s), i->memblock_total_size);
+ printf("Currently in use: %u blocks containing %s bytes total.\n", i->memblock_total, s);
+
+ pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size);
+ printf("Allocated during whole lifetime: %u blocks containing %s bytes total.\n", i->memblock_allocated, s);
+
+ pa_bytes_snprint(s, sizeof(s), i->scache_size);
+ printf("Sample cache size: %s\n", s);
+
+ complete_action();
+}
+
+static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
+ char s[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
+ if (!i) {
+ fprintf(stderr, "Failed to get server information: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
+
+ printf("User name: %s\n"
+ "Host Name: %s\n"
+ "Server Name: %s\n"
+ "Server Version: %s\n"
+ "Default Sample Specification: %s\n"
+ "Default Sink: %s\n"
+ "Default Source: %s\n"
+ "Cookie: %08x\n",
+ i->user_name,
+ i->host_name,
+ i->server_name,
+ i->server_version,
+ s,
+ i->default_sink_name,
+ i->default_source_name,
+ i->cookie);
+
+ complete_action();
+}
+
+static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
+ char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ if (is_last < 0) {
+ fprintf(stderr, "Failed to get sink information: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last) {
+ complete_action();
+ return;
+ }
+
+ assert(i);
+
+ if (nl)
+ printf("\n");
+ nl = 1;
+
+ printf("*** Sink #%u ***\n"
+ "Name: %s\n"
+ "Driver: %s\n"
+ "Description: %s\n"
+ "Sample Specification: %s\n"
+ "Channel Map: %s\n"
+ "Owner Module: %u\n"
+ "Volume: %s\n"
+ "Monitor Source: %u\n"
+ "Latency: %0.0f usec\n",
+ i->index,
+ i->name,
+ i->driver,
+ i->description,
+ pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+ i->owner_module,
+ pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ i->monitor_source,
+ (double) i->latency);
+
+}
+
+static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
+ char s[PA_SAMPLE_SPEC_SNPRINT_MAX], t[32], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ if (is_last < 0) {
+ fprintf(stderr, "Failed to get source information: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last) {
+ complete_action();
+ return;
+ }
+
+ assert(i);
+
+ if (nl)
+ printf("\n");
+ nl = 1;
+
+ snprintf(t, sizeof(t), "%u", i->monitor_of_sink);
+
+ printf("*** Source #%u ***\n"
+ "Name: %s\n"
+ "Driver: %s\n"
+ "Description: %s\n"
+ "Sample Specification: %s\n"
+ "Channel Map: %s\n"
+ "Owner Module: %u\n"
+ "Monitor of Sink: %s\n"
+ "Latency: %0.0f usec\n",
+ i->index,
+ i->driver,
+ i->name,
+ i->description,
+ pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+ i->owner_module,
+ i->monitor_of_sink != PA_INVALID_INDEX ? t : "no",
+ (double) i->latency);
+
+}
+
+static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
+ char t[32];
+
+ if (is_last < 0) {
+ fprintf(stderr, "Failed to get module information: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last) {
+ complete_action();
+ return;
+ }
+
+ assert(i);
+
+ if (nl)
+ printf("\n");
+ nl = 1;
+
+ snprintf(t, sizeof(t), "%u", i->n_used);
+
+ printf("*** Module #%u ***\n"
+ "Name: %s\n"
+ "Argument: %s\n"
+ "Usage counter: %s\n"
+ "Auto unload: %s\n",
+ i->index,
+ i->name,
+ i->argument,
+ i->n_used != PA_INVALID_INDEX ? t : "n/a",
+ i->auto_unload ? "yes" : "no");
+}
+
+static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
+ char t[32];
+
+ if (is_last < 0) {
+ fprintf(stderr, "Failed to get client information: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last) {
+ complete_action();
+ return;
+ }
+
+ assert(i);
+
+ if (nl)
+ printf("\n");
+ nl = 1;
+
+ snprintf(t, sizeof(t), "%u", i->owner_module);
+
+ printf("*** Client #%u ***\n"
+ "Name: %s\n"
+ "Driver: %s\n"
+ "Owner Module: %s\n",
+ i->index,
+ i->name,
+ i->driver,
+ i->owner_module != PA_INVALID_INDEX ? t : "n/a");
+}
+
+static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
+ char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ if (is_last < 0) {
+ fprintf(stderr, "Failed to get sink input information: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last) {
+ complete_action();
+ return;
+ }
+
+ assert(i);
+
+ if (nl)
+ printf("\n");
+ nl = 1;
+
+ snprintf(t, sizeof(t), "%u", i->owner_module);
+ snprintf(k, sizeof(k), "%u", i->client);
+
+ printf("*** Sink Input #%u ***\n"
+ "Name: %s\n"
+ "Driver: %s\n"
+ "Owner Module: %s\n"
+ "Client: %s\n"
+ "Sink: %u\n"
+ "Sample Specification: %s\n"
+ "Channel Map: %s\n"
+ "Volume: %s\n"
+ "Buffer Latency: %0.0f usec\n"
+ "Sink Latency: %0.0f usec\n"
+ "Resample method: %s\n",
+ i->index,
+ i->name,
+ i->driver,
+ i->owner_module != PA_INVALID_INDEX ? t : "n/a",
+ i->client != PA_INVALID_INDEX ? k : "n/a",
+ i->sink,
+ pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+ pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ (double) i->buffer_usec,
+ (double) i->sink_usec,
+ i->resample_method ? i->resample_method : "n/a");
+}
+
+
+static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
+ char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ if (is_last < 0) {
+ fprintf(stderr, "Failed to get source output information: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last) {
+ complete_action();
+ return;
+ }
+
+ assert(i);
+
+ if (nl)
+ printf("\n");
+ nl = 1;
+
+
+ snprintf(t, sizeof(t), "%u", i->owner_module);
+ snprintf(k, sizeof(k), "%u", i->client);
+
+ printf("*** Source Output #%u ***\n"
+ "Name: %s\n"
+ "Driver: %s\n"
+ "Owner Module: %s\n"
+ "Client: %s\n"
+ "Source: %u\n"
+ "Sample Specification: %s\n"
+ "Channel Map: %s\n"
+ "Buffer Latency: %0.0f usec\n"
+ "Source Latency: %0.0f usec\n"
+ "Resample method: %s\n",
+ i->index,
+ i->name,
+ i->driver,
+ i->owner_module != PA_INVALID_INDEX ? t : "n/a",
+ i->client != PA_INVALID_INDEX ? k : "n/a",
+ i->source,
+ pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+ (double) i->buffer_usec,
+ (double) i->source_usec,
+ i->resample_method ? i->resample_method : "n/a");
+}
+
+static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
+ char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ if (is_last < 0) {
+ fprintf(stderr, "Failed to get sample information: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last) {
+ complete_action();
+ return;
+ }
+
+ assert(i);
+
+ if (nl)
+ printf("\n");
+ nl = 1;
+
+
+ pa_bytes_snprint(t, sizeof(t), i->bytes);
+
+ printf("*** Sample #%u ***\n"
+ "Name: %s\n"
+ "Volume: %s\n"
+ "Sample Specification: %s\n"
+ "Channel Map: %s\n"
+ "Duration: %0.1fs\n"
+ "Size: %s\n"
+ "Lazy: %s\n"
+ "Filename: %s\n",
+ i->index,
+ i->name,
+ pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+ pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : "n/a",
+ pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : "n/a",
+ (double) i->duration/1000000,
+ t,
+ i->lazy ? "yes" : "no",
+ i->filename ? i->filename : "n/a");
+}
+
+static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata) {
+ if (is_last < 0) {
+ fprintf(stderr, "Failed to get autoload information: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last) {
+ complete_action();
+ return;
+ }
+
+ assert(i);
+
+ if (nl)
+ printf("\n");
+ nl = 1;
+
+ printf("*** Autoload Entry #%u ***\n"
+ "Name: %s\n"
+ "Type: %s\n"
+ "Module: %s\n"
+ "Argument: %s\n",
+ i->index,
+ i->name,
+ i->type == PA_AUTOLOAD_SINK ? "sink" : "source",
+ i->module,
+ i->argument);
+}
+
+static void simple_callback(pa_context *c, int success, void *userdata) {
+ if (!success) {
+ fprintf(stderr, "Failure: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ complete_action();
+}
+
+static void stream_state_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ switch (pa_stream_get_state(s)) {
+ case PA_STREAM_CREATING:
+ case PA_STREAM_READY:
+ break;
+
+ case PA_STREAM_TERMINATED:
+ drain();
+ break;
+
+ case PA_STREAM_FAILED:
+ default:
+ fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+ quit(1);
+ }
+}
+
+static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
+ sf_count_t l;
+ float *d;
+ assert(s && length && sndfile);
+
+ d = malloc(length);
+ assert(d);
+
+ assert(sample_length >= length);
+ l = length/pa_frame_size(&sample_spec);
+
+ if ((sf_readf_float(sndfile, d, l)) != l) {
+ free(d);
+ fprintf(stderr, "Premature end of file\n");
+ quit(1);
+ }
+
+ pa_stream_write(s, d, length, free, 0);
+
+ sample_length -= length;
+
+ if (sample_length <= 0) {
+ pa_stream_set_write_callback(sample_stream, NULL, NULL);
+ pa_stream_finish_upload(sample_stream);
+ }
+}
+
+static void context_state_callback(pa_context *c, void *userdata) {
+ assert(c);
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+
+ case PA_CONTEXT_READY:
+ switch (action) {
+ case STAT:
+ actions = 2;
+ pa_operation_unref(pa_context_stat(c, stat_callback, NULL));
+ pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
+ break;
+
+ case PLAY_SAMPLE:
+ pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL));
+ break;
+
+ case REMOVE_SAMPLE:
+ pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
+ break;
+
+ case UPLOAD_SAMPLE:
+ sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
+ assert(sample_stream);
+
+ pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
+ pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
+ pa_stream_connect_upload(sample_stream, sample_length);
+ break;
+
+ case EXIT:
+ pa_context_exit_daemon(c);
+ drain();
+
+ case LIST:
+ actions = 8;
+ pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
+ pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
+ pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
+ pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
+ pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
+ pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
+ pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
+ pa_operation_unref(pa_context_get_autoload_info_list(c, get_autoload_info_callback, NULL));
+ break;
+
+ default:
+ assert(0);
+ }
+ break;
+
+ case PA_CONTEXT_TERMINATED:
+ quit(0);
+ break;
+
+ case PA_CONTEXT_FAILED:
+ default:
+ fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ }
+}
+
+static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
+ fprintf(stderr, "Got SIGINT, exiting.\n");
+ quit(0);
+}
+
+static void help(const char *argv0) {
+
+ printf("%s [options] stat\n"
+ "%s [options] list\n"
+ "%s [options] exit\n"
+ "%s [options] upload-sample FILENAME [NAME]\n"
+ "%s [options] play-sample NAME [SINK]\n"
+ "%s [options] remove-sample NAME\n\n"
+ " -h, --help Show this help\n"
+ " --version Show version\n\n"
+ " -s, --server=SERVER The name of the server to connect to\n"
+ " -n, --client-name=NAME How to call this client on the server\n",
+ argv0, argv0, argv0, argv0, argv0, argv0);
+}
+
+enum { ARG_VERSION = 256 };
+
+int main(int argc, char *argv[]) {
+ pa_mainloop* m = NULL;
+ char tmp[PATH_MAX];
+ int ret = 1, r, c;
+ char *server = NULL, *client_name = NULL, *bn;
+
+ static const struct option long_options[] = {
+ {"server", 1, NULL, 's'},
+ {"client-name", 1, NULL, 'n'},
+ {"version", 0, NULL, ARG_VERSION},
+ {"help", 0, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if (!(bn = strrchr(argv[0], '/')))
+ bn = argv[0];
+ else
+ bn++;
+
+ while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'h' :
+ help(bn);
+ ret = 0;
+ goto quit;
+
+ case ARG_VERSION:
+ printf("pactl "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
+ ret = 0;
+ goto quit;
+
+ case 's':
+ free(server);
+ server = strdup(optarg);
+ break;
+
+ case 'n':
+ free(client_name);
+ client_name = strdup(optarg);
+ break;
+
+ default:
+ goto quit;
+ }
+ }
+
+ if (!client_name)
+ client_name = strdup(bn);
+
+ if (optind < argc) {
+ if (!strcmp(argv[optind], "stat"))
+ action = STAT;
+ else if (!strcmp(argv[optind], "exit"))
+ action = EXIT;
+ else if (!strcmp(argv[optind], "list"))
+ action = LIST;
+ else if (!strcmp(argv[optind], "upload-sample")) {
+ struct SF_INFO sfinfo;
+ action = UPLOAD_SAMPLE;
+
+ if (optind+1 >= argc) {
+ fprintf(stderr, "Please specify a sample file to load\n");
+ goto quit;
+ }
+
+ if (optind+2 < argc)
+ sample_name = strdup(argv[optind+2]);
+ else {
+ char *f = strrchr(argv[optind+1], '/');
+ size_t n;
+ if (f)
+ f++;
+ else
+ f = argv[optind];
+
+ n = strcspn(f, ".");
+ strncpy(tmp, f, n);
+ tmp[n] = 0;
+ sample_name = strdup(tmp);
+ }
+
+ memset(&sfinfo, 0, sizeof(sfinfo));
+ if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfinfo))) {
+ fprintf(stderr, "Failed to open sound file.\n");
+ goto quit;
+ }
+
+ sample_spec.format = PA_SAMPLE_FLOAT32;
+ sample_spec.rate = sfinfo.samplerate;
+ sample_spec.channels = sfinfo.channels;
+
+ sample_length = sfinfo.frames*pa_frame_size(&sample_spec);
+ } else if (!strcmp(argv[optind], "play-sample")) {
+ action = PLAY_SAMPLE;
+ if (optind+1 >= argc) {
+ fprintf(stderr, "You have to specify a sample name to play\n");
+ goto quit;
+ }
+
+ sample_name = strdup(argv[optind+1]);
+
+ if (optind+2 < argc)
+ device = strdup(argv[optind+2]);
+
+ } else if (!strcmp(argv[optind], "remove-sample")) {
+ action = REMOVE_SAMPLE;
+ if (optind+1 >= argc) {
+ fprintf(stderr, "You have to specify a sample name to remove\n");
+ goto quit;
+ }
+
+ sample_name = strdup(argv[optind+1]);
+ }
+ }
+
+ if (action == NONE) {
+ fprintf(stderr, "No valid command specified.\n");
+ goto quit;
+ }
+
+ if (!(m = pa_mainloop_new())) {
+ fprintf(stderr, "pa_mainloop_new() failed.\n");
+ goto quit;
+ }
+
+ mainloop_api = pa_mainloop_get_api(m);
+
+ r = pa_signal_init(mainloop_api);
+ assert(r == 0);
+ pa_signal_new(SIGINT, exit_signal_callback, NULL);
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ if (!(context = pa_context_new(mainloop_api, client_name))) {
+ fprintf(stderr, "pa_context_new() failed.\n");
+ goto quit;
+ }
+
+ pa_context_set_state_callback(context, context_state_callback, NULL);
+ pa_context_connect(context, server, 1, NULL);
+
+ if (pa_mainloop_run(m, &ret) < 0) {
+ fprintf(stderr, "pa_mainloop_run() failed.\n");
+ goto quit;
+ }
+
+quit:
+ if (sample_stream)
+ pa_stream_unref(sample_stream);
+
+ if (context)
+ pa_context_unref(context);
+
+ if (m) {
+ pa_signal_done();
+ pa_mainloop_free(m);
+ }
+
+ if (sndfile)
+ sf_close(sndfile);
+
+ free(server);
+ free(device);
+ free(sample_name);
+
+ return ret;
+}
diff --git a/src/utils/paplay.c b/src/utils/paplay.c
new file mode 100644
index 00000000..ddc1cbc1
--- /dev/null
+++ b/src/utils/paplay.c
@@ -0,0 +1,386 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <sndfile.h>
+
+#include <polyp/polyplib.h>
+#include <polyp/polyplib-error.h>
+#include <polyp/mainloop.h>
+#include <polyp/mainloop-signal.h>
+#include <polyp/polyplib-version.h>
+
+#if PA_API_VERSION != 8
+#error Invalid Polypaudio API version
+#endif
+
+static pa_context *context = NULL;
+static pa_stream *stream = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+
+static char *stream_name = NULL, *client_name = NULL, *device = NULL;
+
+static int verbose = 0;
+static pa_volume_t volume = PA_VOLUME_NORM;
+
+static SNDFILE* sndfile = NULL;
+
+static pa_sample_spec sample_spec = { 0, 0, 0 };
+
+static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames);
+
+/* A shortcut for terminating the application */
+static void quit(int ret) {
+ assert(mainloop_api);
+ mainloop_api->quit(mainloop_api, ret);
+}
+
+/* Connection draining complete */
+static void context_drain_complete(pa_context*c, void *userdata) {
+ pa_context_disconnect(c);
+}
+
+/* Stream draining complete */
+static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
+ pa_operation *o;
+
+ if (!success) {
+ fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
+ quit(1);
+ }
+
+ if (verbose)
+ fprintf(stderr, "Playback stream drained.\n");
+
+ pa_stream_disconnect(stream);
+ pa_stream_unref(stream);
+ stream = NULL;
+
+ if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+ pa_context_disconnect(context);
+ else {
+ pa_operation_unref(o);
+
+ if (verbose)
+ fprintf(stderr, "Draining connection to server.\n");
+ }
+}
+
+/* This is called whenever new data may be written to the stream */
+static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
+ size_t k;
+ sf_count_t f, n;
+ void *data;
+ assert(s && length);
+
+ if (!sndfile)
+ return;
+
+ k = pa_frame_size(&sample_spec);
+
+ data = malloc(length);
+
+ n = length/k;
+
+ f = readf_function(sndfile, data, n);
+
+ if (f > 0)
+ pa_stream_write(s, data, f*k, free, 0);
+
+ if (f < n) {
+ sf_close(sndfile);
+ sndfile = NULL;
+ pa_operation_unref(pa_stream_drain(s, stream_drain_complete, NULL));
+ }
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+ assert(s);
+
+ switch (pa_stream_get_state(s)) {
+ case PA_STREAM_CREATING:
+ case PA_STREAM_TERMINATED:
+ break;
+
+ case PA_STREAM_READY:
+ if (verbose)
+ fprintf(stderr, "Stream successfully created\n");
+ break;
+
+ case PA_STREAM_FAILED:
+ default:
+ fprintf(stderr, "Stream errror: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+ quit(1);
+ }
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+ assert(c);
+
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+
+ case PA_CONTEXT_READY: {
+ pa_cvolume cv;
+
+ assert(c && !stream);
+
+ if (verbose)
+ fprintf(stderr, "Connection established.\n");
+
+ stream = pa_stream_new(c, stream_name, &sample_spec, NULL);
+ assert(stream);
+
+ pa_stream_set_state_callback(stream, stream_state_callback, NULL);
+ pa_stream_set_write_callback(stream, stream_write_callback, NULL);
+ pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, PA_CHANNELS_MAX, volume));
+
+ break;
+ }
+
+ case PA_CONTEXT_TERMINATED:
+ quit(0);
+ break;
+
+ case PA_CONTEXT_FAILED:
+ default:
+ fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ }
+}
+
+/* UNIX signal to quit recieved */
+static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
+ if (verbose)
+ fprintf(stderr, "Got SIGINT, exiting.\n");
+ quit(0);
+
+}
+
+static void help(const char *argv0) {
+
+ printf("%s [options] [FILE]\n\n"
+ " -h, --help Show this help\n"
+ " --version Show version\n\n"
+ " -v, --verbose Enable verbose operations\n\n"
+ " -s, --server=SERVER The name of the server to connect to\n"
+ " -d, --device=DEVICE The name of the sink/source to connect to\n"
+ " -n, --client-name=NAME How to call this client on the server\n"
+ " --stream-name=NAME How to call this stream on the server\n"
+ " --volume=VOLUME Specify the initial (linear) volume in range 0...256\n",
+ argv0);
+}
+
+enum {
+ ARG_VERSION = 256,
+ ARG_STREAM_NAME,
+ ARG_VOLUME
+};
+
+int main(int argc, char *argv[]) {
+ pa_mainloop* m = NULL;
+ int ret = 1, r, c;
+ char *bn, *server = NULL;
+ const char *filename;
+ SF_INFO sfinfo;
+
+ static const struct option long_options[] = {
+ {"device", 1, NULL, 'd'},
+ {"server", 1, NULL, 's'},
+ {"client-name", 1, NULL, 'n'},
+ {"stream-name", 1, NULL, ARG_STREAM_NAME},
+ {"version", 0, NULL, ARG_VERSION},
+ {"help", 0, NULL, 'h'},
+ {"verbose", 0, NULL, 'v'},
+ {"volume", 1, NULL, ARG_VOLUME},
+ {NULL, 0, NULL, 0}
+ };
+
+ if (!(bn = strrchr(argv[0], '/')))
+ bn = argv[0];
+ else
+ bn++;
+
+ while ((c = getopt_long(argc, argv, "d:s:n:hv", long_options, NULL)) != -1) {
+
+ switch (c) {
+ case 'h' :
+ help(bn);
+ ret = 0;
+ goto quit;
+
+ case ARG_VERSION:
+ printf("paplay "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
+ ret = 0;
+ goto quit;
+
+ case 'd':
+ free(device);
+ device = strdup(optarg);
+ break;
+
+ case 's':
+ free(server);
+ server = strdup(optarg);
+ break;
+
+ case 'n':
+ free(client_name);
+ client_name = strdup(optarg);
+ break;
+
+ case ARG_STREAM_NAME:
+ free(stream_name);
+ stream_name = strdup(optarg);
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case ARG_VOLUME: {
+ int v = atoi(optarg);
+ volume = v < 0 ? 0 : v;
+ break;
+ }
+
+ default:
+ goto quit;
+ }
+ }
+
+
+ filename = optind < argc ? argv[optind] : "STDIN";
+
+
+ if (!client_name)
+ client_name = strdup(bn);
+
+ if (!stream_name)
+ stream_name = strdup(filename);
+
+ memset(&sfinfo, 0, sizeof(sfinfo));
+
+ if (optind < argc)
+ sndfile = sf_open(filename, SFM_READ, &sfinfo);
+ else
+ sndfile = sf_open_fd(STDIN_FILENO, SFM_READ, &sfinfo, 0);
+
+ if (!sndfile) {
+ fprintf(stderr, "Failed to open file '%s'\n", filename);
+ goto quit;
+ }
+
+ sample_spec.rate = sfinfo.samplerate;
+ sample_spec.channels = sfinfo.channels;
+
+ switch (sfinfo.format & 0xFF) {
+ case SF_FORMAT_PCM_16:
+ case SF_FORMAT_PCM_U8:
+ case SF_FORMAT_ULAW:
+ case SF_FORMAT_ALAW:
+ sample_spec.format = PA_SAMPLE_S16NE;
+ readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
+ break;
+ case SF_FORMAT_FLOAT:
+ default:
+ sample_spec.format = PA_SAMPLE_FLOAT32NE;
+ readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
+ break;
+ }
+
+ if (verbose) {
+ char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
+ fprintf(stderr, "Using sample spec '%s'\n", t);
+ }
+
+ /* Set up a new main loop */
+ if (!(m = pa_mainloop_new())) {
+ fprintf(stderr, "pa_mainloop_new() failed.\n");
+ goto quit;
+ }
+
+ mainloop_api = pa_mainloop_get_api(m);
+
+ r = pa_signal_init(mainloop_api);
+ assert(r == 0);
+ pa_signal_new(SIGINT, exit_signal_callback, NULL);
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ /* Create a new connection context */
+ if (!(context = pa_context_new(mainloop_api, client_name))) {
+ fprintf(stderr, "pa_context_new() failed.\n");
+ goto quit;
+ }
+
+ pa_context_set_state_callback(context, context_state_callback, NULL);
+
+ /* Connect the context */
+ pa_context_connect(context, server, 1, NULL);
+
+ /* Run the main loop */
+ if (pa_mainloop_run(m, &ret) < 0) {
+ fprintf(stderr, "pa_mainloop_run() failed.\n");
+ goto quit;
+ }
+
+quit:
+ if (stream)
+ pa_stream_unref(stream);
+
+ if (context)
+ pa_context_unref(context);
+
+ if (m) {
+ pa_signal_done();
+ pa_mainloop_free(m);
+ }
+
+ free(server);
+ free(device);
+ free(client_name);
+ free(stream_name);
+
+ if (sndfile)
+ sf_close(sndfile);
+
+ return ret;
+}
diff --git a/src/utils/pax11publish.c b/src/utils/pax11publish.c
new file mode 100644
index 00000000..63ee4821
--- /dev/null
+++ b/src/utils/pax11publish.c
@@ -0,0 +1,217 @@
+/* $Id$ */
+
+/***
+ This file is part of polypaudio.
+
+ polypaudio 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 of the License,
+ or (at your option) any later version.
+
+ polypaudio 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with polypaudio; 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 <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <assert.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include <polypcore/util.h>
+#include <polypcore/log.h>
+#include <polypcore/authkey.h>
+#include <polypcore/native-common.h>
+#include <polyp/client-conf.h>
+#include <polypcore/x11prop.h>
+
+int main(int argc, char *argv[]) {
+ const char *dname = NULL, *sink = NULL, *source = NULL, *server = NULL, *cookie_file = PA_NATIVE_COOKIE_FILE;
+ int c, ret = 1;
+ Display *d = NULL;
+ enum { DUMP, EXPORT, IMPORT, REMOVE } mode = DUMP;
+
+ while ((c = getopt(argc, argv, "deiD:S:O:I:c:hr")) != -1) {
+ switch (c) {
+ case 'D' :
+ dname = optarg;
+ break;
+ case 'h':
+ printf("%s [-D display] [-S server] [-O sink] [-I source] [-c file] [-d|-e|-i|-r]\n\n"
+ " -d Show current Polypaudio data attached to X11 display (default)\n"
+ " -e Export local Polypaudio data to X11 display\n"
+ " -i Import Polypaudio data from X11 display to local environment variables and cookie file.\n"
+ " -r Remove Polypaudio data from X11 display\n",
+ pa_path_get_filename(argv[0]));
+ ret = 0;
+ goto finish;
+ case 'd':
+ mode = DUMP;
+ break;
+ case 'e':
+ mode = EXPORT;
+ break;
+ case 'i':
+ mode = IMPORT;
+ break;
+ case 'r':
+ mode = REMOVE;
+ break;
+ case 'c':
+ cookie_file = optarg;
+ break;
+ case 'I':
+ source = optarg;
+ break;
+ case 'O':
+ sink = optarg;
+ break;
+ case 'S':
+ server = optarg;
+ break;
+ default:
+ fprintf(stderr, "Failed to parse command line.\n");
+ goto finish;
+ }
+ }
+
+ if (!(d = XOpenDisplay(dname))) {
+ pa_log(__FILE__": XOpenDisplay() failed\n");
+ goto finish;
+ }
+
+ switch (mode) {
+ case DUMP: {
+ char t[1024];
+ if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t)))
+ printf("Server: %s\n", t);
+ if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t)))
+ printf("Source: %s\n", t);
+ if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t)))
+ printf("Sink: %s\n", t);
+ if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t)))
+ printf("Cookie: %s\n", t);
+
+ break;
+ }
+
+ case IMPORT: {
+ char t[1024];
+ if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t)))
+ printf("POLYP_SERVER='%s'\nexport POLYP_SERVER\n", t);
+ if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t)))
+ printf("POLYP_SOURCE='%s'\nexport POLYP_SOURCE\n", t);
+ if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t)))
+ printf("POLYP_SINK='%s'\nexport POLYP_SINK\n", t);
+
+ if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) {
+ uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+ size_t l;
+ if ((l = pa_parsehex(t, cookie, sizeof(cookie))) != sizeof(cookie)) {
+ fprintf(stderr, "Failed to parse cookie data\n");
+ goto finish;
+ }
+
+ if (pa_authkey_save(cookie_file, cookie, l) < 0) {
+ fprintf(stderr, "Failed to save cookie data\n");
+ goto finish;
+ }
+ }
+
+ break;
+ }
+
+ case EXPORT: {
+ pa_client_conf *conf = pa_client_conf_new();
+ uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+ char hx[PA_NATIVE_COOKIE_LENGTH*2+1];
+ assert(conf);
+
+ if (pa_client_conf_load(conf, NULL) < 0) {
+ fprintf(stderr, "Failed to load client configuration file.\n");
+ goto finish;
+ }
+
+ if (pa_client_conf_env(conf) < 0) {
+ fprintf(stderr, "Failed to read environment configuration data.\n");
+ goto finish;
+ }
+
+ pa_x11_del_prop(d, "POLYP_SERVER");
+ pa_x11_del_prop(d, "POLYP_SINK");
+ pa_x11_del_prop(d, "POLYP_SOURCE");
+ pa_x11_del_prop(d, "POLYP_ID");
+ pa_x11_del_prop(d, "POLYP_COOKIE");
+
+ if (server)
+ pa_x11_set_prop(d, "POLYP_SERVER", server);
+ else if (conf->default_server)
+ pa_x11_set_prop(d, "POLYP_SERVER", conf->default_server);
+ else {
+ char hn[256];
+ if (!pa_get_fqdn(hn, sizeof(hn))) {
+ fprintf(stderr, "Failed to get FQDN.\n");
+ goto finish;
+ }
+
+ pa_x11_set_prop(d, "POLYP_SERVER", hn);
+ }
+
+ if (sink)
+ pa_x11_set_prop(d, "POLYP_SINK", sink);
+ else if (conf->default_sink)
+ pa_x11_set_prop(d, "POLYP_SINK", conf->default_sink);
+
+ if (source)
+ pa_x11_set_prop(d, "POLYP_SOURCE", source);
+ if (conf->default_source)
+ pa_x11_set_prop(d, "POLYP_SOURCE", conf->default_source);
+
+ pa_client_conf_free(conf);
+
+ if (pa_authkey_load_auto(cookie_file, cookie, sizeof(cookie)) < 0) {
+ fprintf(stderr, "Failed to load cookie data\n");
+ goto finish;
+ }
+
+ pa_x11_set_prop(d, "POLYP_COOKIE", pa_hexstr(cookie, sizeof(cookie), hx, sizeof(hx)));
+ break;
+ }
+
+ case REMOVE:
+ pa_x11_del_prop(d, "POLYP_SERVER");
+ pa_x11_del_prop(d, "POLYP_SINK");
+ pa_x11_del_prop(d, "POLYP_SOURCE");
+ pa_x11_del_prop(d, "POLYP_ID");
+ pa_x11_del_prop(d, "POLYP_COOKIE");
+ break;
+
+ default:
+ fprintf(stderr, "No yet implemented.\n");
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+
+ if (d) {
+ XSync(d, False);
+ XCloseDisplay(d);
+ }
+
+ return ret;
+}