summaryrefslogtreecommitdiffstats
path: root/polyp
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2004-07-17 14:12:30 +0000
committerLennart Poettering <lennart@poettering.net>2004-07-17 14:12:30 +0000
commit41f6aea8fdbc744c13bc461056a2d694a5c4d06f (patch)
tree1e23911c1f61c66df3fba272af53475508a7a7ce /polyp
parent563201e128d600ec1ae4a33ca81d03b776501f82 (diff)
rename src to polyp
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@90 fefdeb5f-60dc-0310-8127-8f9354f1896f
Diffstat (limited to 'polyp')
-rw-r--r--polyp/Makefile.am363
-rw-r--r--polyp/alsa-util.c98
-rw-r--r--polyp/alsa-util.h35
-rw-r--r--polyp/authkey.c151
-rw-r--r--polyp/authkey.h31
-rw-r--r--polyp/cli-command.c557
-rw-r--r--polyp/cli-command.h32
-rw-r--r--polyp/cli.c148
-rw-r--r--polyp/cli.h36
-rw-r--r--polyp/client.c79
-rw-r--r--polyp/client.h51
-rw-r--r--polyp/clitext.c203
-rw-r--r--polyp/clitext.h35
-rw-r--r--polyp/cmdline.c111
-rw-r--r--polyp/cmdline.h36
-rw-r--r--polyp/core.c88
-rw-r--r--polyp/core.h45
-rwxr-xr-xpolyp/depmod.py73
-rw-r--r--polyp/dynarray.c98
-rw-r--r--polyp/dynarray.h37
-rw-r--r--polyp/endianmacros.h62
-rw-r--r--polyp/esound.h213
-rw-r--r--polyp/hashmap.c170
-rw-r--r--polyp/hashmap.h37
-rw-r--r--polyp/idxset.c397
-rw-r--r--polyp/idxset.h63
-rw-r--r--polyp/iochannel.c222
-rw-r--r--polyp/iochannel.h50
-rw-r--r--polyp/ioline.c220
-rw-r--r--polyp/ioline.h35
-rw-r--r--polyp/main.c182
-rw-r--r--polyp/mainloop-api.c60
-rw-r--r--polyp/mainloop-api.h65
-rw-r--r--polyp/mainloop-signal.c163
-rw-r--r--polyp/mainloop-signal.h33
-rw-r--r--polyp/mainloop.c553
-rw-r--r--polyp/mainloop.h37
-rw-r--r--polyp/memblock.c113
-rw-r--r--polyp/memblock.h49
-rw-r--r--polyp/memblockq.c326
-rw-r--r--polyp/memblockq.h82
-rw-r--r--polyp/memchunk.c149
-rw-r--r--polyp/memchunk.h41
-rw-r--r--polyp/modargs.c288
-rw-r--r--polyp/modargs.h42
-rw-r--r--polyp/module-alsa-sink.c259
-rw-r--r--polyp/module-alsa-source.c237
-rw-r--r--polyp/module-cli.c73
-rw-r--r--polyp/module-oss-mmap.c404
-rw-r--r--polyp/module-oss.c304
-rw-r--r--polyp/module-pipe-sink.c218
-rw-r--r--polyp/module-protocol-stub.c165
-rw-r--r--polyp/module.c161
-rw-r--r--polyp/module.h55
-rw-r--r--polyp/namereg.c136
-rw-r--r--polyp/namereg.h38
-rw-r--r--polyp/native-common.h68
-rw-r--r--polyp/oss-util.c163
-rw-r--r--polyp/oss-util.h32
-rw-r--r--polyp/pacat-simple.c83
-rw-r--r--polyp/pacat.c336
-rw-r--r--polyp/packet.c72
-rw-r--r--polyp/packet.h41
-rw-r--r--polyp/pactl.c166
-rw-r--r--polyp/parec-simple.c94
-rw-r--r--polyp/pdispatch.c247
-rw-r--r--polyp/pdispatch.h52
-rwxr-xr-xpolyp/polypaudio.pa41
-rw-r--r--polyp/polyplib-def.h40
-rw-r--r--polyp/polyplib-error.c53
-rw-r--r--polyp/polyplib-error.h29
-rw-r--r--polyp/polyplib-simple.c259
-rw-r--r--polyp/polyplib-simple.h49
-rw-r--r--polyp/polyplib.c973
-rw-r--r--polyp/polyplib.h94
-rw-r--r--polyp/protocol-cli.c85
-rw-r--r--polyp/protocol-cli.h35
-rw-r--r--polyp/protocol-esound.c847
-rw-r--r--polyp/protocol-esound.h35
-rw-r--r--polyp/protocol-native.c863
-rw-r--r--polyp/protocol-native.h35
-rw-r--r--polyp/protocol-simple.c444
-rw-r--r--polyp/protocol-simple.h35
-rw-r--r--polyp/pstream-util.c60
-rw-r--r--polyp/pstream-util.h35
-rw-r--r--polyp/pstream.c457
-rw-r--r--polyp/pstream.h51
-rw-r--r--polyp/queue.c109
-rw-r--r--polyp/queue.h34
-rw-r--r--polyp/resampler.c180
-rw-r--r--polyp/resampler.h37
-rw-r--r--polyp/sample-util.c144
-rw-r--r--polyp/sample-util.h48
-rw-r--r--polyp/sample.c99
-rw-r--r--polyp/sample.h64
-rw-r--r--polyp/sconv-s16be.c34
-rw-r--r--polyp/sconv-s16be.h28
-rw-r--r--polyp/sconv-s16le.c82
-rw-r--r--polyp/sconv-s16le.h28
-rw-r--r--polyp/sconv.c137
-rw-r--r--polyp/sconv.h33
-rw-r--r--polyp/sink-input.c163
-rw-r--r--polyp/sink-input.h67
-rw-r--r--polyp/sink.c292
-rw-r--r--polyp/sink.h67
-rw-r--r--polyp/sioman.c42
-rw-r--r--polyp/sioman.h28
-rw-r--r--polyp/socket-client.c236
-rw-r--r--polyp/socket-client.h41
-rw-r--r--polyp/socket-server.c209
-rw-r--r--polyp/socket-server.h41
-rw-r--r--polyp/socket-util.c216
-rw-r--r--polyp/socket-util.h41
-rw-r--r--polyp/source-output.c101
-rw-r--r--polyp/source-output.h58
-rw-r--r--polyp/source.c131
-rw-r--r--polyp/source.h61
-rw-r--r--polyp/strbuf.c164
-rw-r--r--polyp/strbuf.h36
-rw-r--r--polyp/tagstruct.c241
-rw-r--r--polyp/tagstruct.h53
-rw-r--r--polyp/tokenizer.c89
-rw-r--r--polyp/tokenizer.h32
-rw-r--r--polyp/util.c147
-rw-r--r--polyp/util.h38
125 files changed, 17534 insertions, 0 deletions
diff --git a/polyp/Makefile.am b/polyp/Makefile.am
new file mode 100644
index 00000000..c4da7342
--- /dev/null
+++ b/polyp/Makefile.am
@@ -0,0 +1,363 @@
+# $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 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 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.
+
+AM_CFLAGS=-ansi -D_GNU_SOURCE
+
+EXTRA_DIST = polypaudio.run depmod.py
+bin_PROGRAMS = polypaudio pacat pactl
+noinst_PROGRAMS = pacat-simple parec-simple
+
+pkginclude_HEADERS=polyplib.h \
+ polyplib-def.h \
+ polyplib-simple.h \
+ polyplib-error.h \
+ mainloop-api.h \
+ mainloop.h \
+ mainloop-signal.h \
+ sample.h
+
+pkglib_LTLIBRARIES=libiochannel.la \
+ libsocket-server.la \
+ libsocket-client.la \
+ libpstream.la \
+ libpacket.la \
+ liboss-util.la \
+ libalsa-util.la \
+ libioline.la \
+ libcli.la \
+ libprotocol-cli.la \
+ libtagstruct.la \
+ libpstream-util.la \
+ libpdispatch.la \
+ libauthkey.la \
+ libsocket-util.la \
+ libprotocol-simple.la \
+ libprotocol-esound.la \
+ libprotocol-native.la \
+ module-cli.la \
+ module-cli-protocol-tcp.la \
+ module-cli-protocol-unix.la \
+ module-pipe-sink.la \
+ module-alsa-sink.la \
+ module-alsa-source.la \
+ module-oss.la \
+ module-oss-mmap.la \
+ module-simple-protocol-tcp.la \
+ module-simple-protocol-unix.la \
+ module-esound-protocol-tcp.la \
+ module-esound-protocol-unix.la \
+ module-native-protocol-tcp.la \
+ module-native-protocol-unix.la
+
+lib_LTLIBRARIES=libpolyp.la \
+ libpolyp-simple.la \
+ libpolyp-error.la \
+ libpolyp-mainloop.la
+
+polypaudio_SOURCES = idxset.c idxset.h \
+ queue.c queue.h \
+ strbuf.c strbuf.h \
+ main.c \
+ mainloop.c mainloop.h \
+ memblock.c memblock.h \
+ sample.c sample.h \
+ sample-util.c sample-util.h \
+ memblockq.c memblockq.h \
+ client.c client.h \
+ core.c core.h \
+ source-output.c source-output.h \
+ sink-input.c sink-input.h \
+ source.c source.h \
+ sink.c sink.h \
+ module.c module.h \
+ mainloop-signal.c mainloop-signal.h \
+ mainloop-api.c mainloop-api.h \
+ util.c util.h \
+ hashmap.c hashmap.h \
+ namereg.c namereg.h \
+ sconv.c sconv.h \
+ resampler.c resampler.h \
+ endianmacros.h \
+ memchunk.c memchunk.h \
+ sconv-s16le.c sconv-s16le.h \
+ sconv-s16be.c sconv-s16be.h \
+ sioman.c sioman.h \
+ modargs.c modargs.h \
+ cmdline.c cmdline.h \
+ cli-command.c cli-command.h \
+ clitext.c clitext.h \
+ tokenizer.c tokenizer.h \
+ dynarray.c dynarray.h
+
+polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS)
+polypaudio_INCLUDES = $(INCLTDL)
+polypaudio_LDADD = $(LIBLTDL) $(LIBSAMPLERATE_LIBS)
+polypaudio_LDFLAGS=-export-dynamic
+
+libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h
+libprotocol_simple_la_LDFLAGS = -avoid-version
+libprotocol_simple_la_LIBADD = libsocket-server.la libiochannel.la
+
+libsocket_server_la_SOURCES = socket-server.c socket-server.h
+libsocket_server_la_LDFLAGS = -avoid-version
+libsocket_server_la_LIBADD = libiochannel.la libsocket-util.la
+
+libsocket_client_la_SOURCES = socket-client.c socket-client.h
+libsocket_client_la_LDFLAGS = -avoid-version
+libsocket_client_la_LIBADD = libiochannel.la libsocket-util.la
+
+libpstream_la_SOURCES = pstream.c pstream.h
+libpstream_la_LDFLAGS = -avoid-version
+libpstream_la_LIBADD = libpacket.la libiochannel.la
+
+libpstream_util_la_SOURCES = pstream-util.c pstream-util.h
+libpstream_util_la_LDFLAGS = -avoid-version
+libpstream_util_la_LIBADD = libpacket.la libpstream.la libtagstruct.la
+
+libpdispatch_la_SOURCES = pdispatch.c pdispatch.h
+libpdispatch_la_LDFLAGS = -avoid-version
+libpdispatch_la_LIBADD = libtagstruct.la
+
+libiochannel_la_SOURCES = iochannel.c iochannel.h
+libiochannel_la_LDFLAGS = -avoid-version
+libiochannel_la_LIBADD = libsocket-util.la
+
+libpacket_la_SOURCES = packet.c packet.h
+libpacket_la_LDFLAGS = -avoid-version
+
+liboss_util_la_SOURCES = oss-util.c oss-util.h
+liboss_util_la_LDFLAGS = -avoid-version
+
+libalsa_util_la_SOURCES = alsa-util.c alsa-util.h
+libalsa_util_la_LDFLAGS = -avoid-version
+libalsa_util_la_LIBADD = $(ASOUNDLIB_LIBS)
+libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+libioline_la_SOURCES = ioline.c ioline.h
+libioline_la_LDFLAGS = -avoid-version
+libioline_la_LIBADD = libiochannel.la
+
+libcli_la_SOURCES = cli.c cli.h
+libcli_la_LDFLAGS = -avoid-version
+libcli_la_LIBADD = libiochannel.la libioline.la
+
+libprotocol_cli_la_SOURCES = protocol-cli.c protocol-cli.h
+libprotocol_cli_la_LDFLAGS = -avoid-version
+libprotocol_cli_la_LIBADD = libsocket-server.la libiochannel.la libcli.la
+
+libprotocol_native_la_SOURCES = protocol-native.c protocol-native.h native-common.h
+libprotocol_native_la_LDFLAGS = -avoid-version
+libprotocol_native_la_LIBADD = libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la
+
+libtagstruct_la_SOURCES = tagstruct.c tagstruct.h
+libtagstruct_la_LDFLAGS = -avoid-version
+
+libprotocol_esound_la_SOURCES = protocol-esound.c protocol-esound.h esound.h
+libprotocol_esound_la_LDFLAGS = -avoid-version
+libprotocol_esound_la_LIBADD = libsocket-server.la libiochannel.la libauthkey.la
+
+libauthkey_la_SOURCES = authkey.c authkey.h
+libauthkey_la_LDFLAGS = -avoid-version
+
+libsocket_util_la_SOURCES = socket-util.c socket-util.h
+libsocket_util_la_LDFLAGS = -avoid-version
+
+module_simple_protocol_tcp_la_SOURCES = 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 = libprotocol-simple.la libsocket-server.la
+
+module_simple_protocol_unix_la_SOURCES = 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 = libprotocol-simple.la libsocket-server.la libsocket-util.la
+
+module_cli_protocol_tcp_la_SOURCES = 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 = libprotocol-cli.la libsocket-server.la
+
+module_cli_protocol_unix_la_SOURCES = 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 = libprotocol-cli.la libsocket-server.la libsocket-util.la
+
+module_native_protocol_tcp_la_SOURCES = 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 = libprotocol-native.la libsocket-server.la
+
+module_native_protocol_unix_la_SOURCES = 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 = libprotocol-native.la libsocket-server.la libsocket-util.la
+
+module_esound_protocol_tcp_la_SOURCES = 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 = libprotocol-esound.la libsocket-server.la
+
+module_esound_protocol_unix_la_SOURCES = 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 = libprotocol-esound.la libsocket-server.la libsocket-util.la
+
+module_pipe_sink_la_SOURCES = module-pipe-sink.c
+module_pipe_sink_la_LDFLAGS = -module -avoid-version
+module_pipe_sink_la_LIBADD = libiochannel.la
+
+module_alsa_sink_la_SOURCES = module-alsa-sink.c
+module_alsa_sink_la_LDFLAGS = -module -avoid-version
+module_alsa_sink_la_LIBADD = $(ASOUNDLIB_LIBS) libalsa-util.la
+module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+module_alsa_source_la_SOURCES = module-alsa-source.c
+module_alsa_source_la_LDFLAGS = -module -avoid-version
+module_alsa_source_la_LIBADD = $(ASOUNDLIB_LIBS) libalsa-util.la
+module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+module_oss_la_SOURCES = module-oss.c
+module_oss_la_LDFLAGS = -module -avoid-version
+module_oss_la_LIBADD = libiochannel.la liboss-util.la
+
+module_oss_mmap_la_SOURCES = module-oss-mmap.c
+module_oss_mmap_la_LDFLAGS = -module -avoid-version
+module_oss_mmap_la_LIBADD = liboss-util.la
+
+module_cli_la_SOURCES = module-cli.c
+module_cli_la_LDFLAGS = -module -avoid-version
+module_cli_la_LIBADD = libcli.la libiochannel.la
+
+libpolyp_la_SOURCES = polyplib.c polyplib.h \
+ polyplib-def.h \
+ tagstruct.c tagstruct.h \
+ iochannel.c iochannel.h \
+ pstream.c pstream.h \
+ pstream-util.c pstream-util.h \
+ pdispatch.c pdispatch.h \
+ mainloop-api.c mainloop-api.h \
+ idxset.c idxset.h \
+ util.c util.h \
+ memblock.c memblock.h \
+ socket-client.c socket-client.h \
+ packet.c packet.h \
+ queue.c queue.h \
+ dynarray.c dynarray.h \
+ memchunk.c memchunk.h \
+ authkey.c authkey.h \
+ socket-util.c socket-util.h \
+ native-common.h
+libpolyp_la_CFLAGS = $(AM_CFLAGS)
+
+libpolyp_mainloop_la_SOURCES = mainloop-api.h mainloop-api.c \
+ mainloop.c mainloop.h \
+ mainloop-signal.c mainloop-signal.h
+libpolyp_mainloop_la_CFLAGS = $(AM_CFLAGS)
+
+libpolyp_error_la_SOURCES = polyplib-error.c polyplib-error.h
+libpolyp_error_la_CFLAGS = $(AM_CFLAGS)
+
+libpolyp_simple_la_SOURCES = polyplib-simple.c polyplib-simple.h
+libpolyp_simple_la_CFLAGS = $(AM_CFLAGS)
+libpolyp_simple_la_LIBADD = libpolyp.la libpolyp-mainloop.la
+
+pacat_SOURCES = pacat.c
+pacat_LDADD = libpolyp.la libpolyp-error.la libpolyp-mainloop.la
+pacat_CFLAGS = $(AM_CFLAGS)
+
+pactl_SOURCES = pactl.c
+pactl_LDADD = libpolyp.la libpolyp-error.la libpolyp-mainloop.la
+pactl_CFLAGS = $(AM_CFLAGS)
+
+pacat_simple_SOURCES = pacat-simple.c
+pacat_simple_LDADD = libpolyp-simple.la libpolyp-error.la
+pacat_simple_CFLAGS = $(AM_CFLAGS)
+
+parec_simple_SOURCES = parec-simple.c
+parec_simple_LDADD = libpolyp-simple.la libpolyp-error.la
+parec_simple_CFLAGS = $(AM_CFLAGS)
+
+if BUILD_LIBPOLYPCORE
+
+pkginclude_HEADERS+=cli-command.h\
+ client.h \
+ core.h \
+ dynarray.h \
+ endianmacros.h \
+ hashmap.h \
+ idxset.h \
+ iochannel.h \
+ memblock.h \
+ memblockq.h \
+ memchunk.h \
+ modargs.h \
+ module.h \
+ namereg.h \
+ queue.h \
+ resampler.h \
+ sample-util.h \
+ sink.h \
+ sink-input.h \
+ sioman.h \
+ socket-server.h \
+ socket-client.h \
+ socket-util.h \
+ source.h \
+ source-output.h \
+ strbuf.h \
+ tokenizer.h \
+ tagstruct.h \
+ util.h
+
+lib_LTLIBRARIES+= libpolypcore.la
+
+libpolypcore_la_SOURCES = idxset.c idxset.h \
+ queue.c queue.h \
+ strbuf.c strbuf.h \
+ mainloop.c mainloop.h \
+ memblock.c memblock.h \
+ sample.c sample.h \
+ sample-util.c sample-util.h \
+ memblockq.c memblockq.h \
+ client.c client.h \
+ core.c core.h \
+ source-output.c source-output.h \
+ sink-input.c sink-input.h \
+ source.c source.h \
+ sink.c sink.h \
+ module.c module.h \
+ mainloop-signal.c mainloop-signal.h \
+ mainloop-api.c mainloop-api.h \
+ util.c util.h \
+ hashmap.c hashmap.h \
+ namereg.c namereg.h \
+ sconv.c sconv.h \
+ resampler.c resampler.h \
+ endianmacros.h \
+ memchunk.c memchunk.h \
+ sconv-s16le.c sconv-s16le.h \
+ sconv-s16be.c sconv-s16be.h \
+ sioman.c sioman.h \
+ modargs.c modargs.h \
+ cli-command.c cli-command.h \
+ clitext.c clitext.h \
+ tokenizer.c tokenizer.h \
+ dynarray.c dynarray.h
+
+endif
diff --git a/polyp/alsa-util.c b/polyp/alsa-util.c
new file mode 100644
index 00000000..7f266df5
--- /dev/null
+++ b/polyp/alsa-util.c
@@ -0,0 +1,98 @@
+/* $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 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 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 <asoundlib.h>
+
+#include "alsa-util.h"
+#include "sample.h"
+
+int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *buffer_size) {
+ int ret = 0;
+ 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,
+ };
+
+ 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 ||
+ snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0 ||
+ snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, buffer_size) < 0 ||
+ snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
+ ret = -1;
+ }
+
+ if (hwparams)
+ snd_pcm_hw_params_free(hwparams);
+ return ret;
+}
+
+int pa_create_io_sources(snd_pcm_t *pcm_handle, struct pa_mainloop_api* m, void ***io_sources, unsigned *n_io_sources, void (*cb)(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata) {
+ unsigned i;
+ struct pollfd *pfds, *ppfd;
+ void **ios;
+ assert(pcm_handle && m && io_sources && n_io_sources && cb);
+
+ *n_io_sources = snd_pcm_poll_descriptors_count(pcm_handle);
+
+ pfds = malloc(sizeof(struct pollfd) * *n_io_sources);
+ assert(pfds);
+ if (snd_pcm_poll_descriptors(pcm_handle, pfds, *n_io_sources) < 0) {
+ free(pfds);
+ return -1;
+ }
+
+ *io_sources = malloc(sizeof(void*) * *n_io_sources);
+ assert(io_sources);
+
+ for (i = 0, ios = *io_sources, ppfd = pfds; i < *n_io_sources; i++, ios++, ppfd++) {
+ *ios = m->source_io(m, ppfd->fd,
+ ((ppfd->events & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) |
+ ((ppfd->events & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), cb, userdata);
+ assert(*ios);
+ }
+
+ free(pfds);
+ return 0;
+}
+
+void pa_free_io_sources(struct pa_mainloop_api* m, void **io_sources, unsigned n_io_sources) {
+ unsigned i;
+ void **ios;
+ assert(m && io_sources);
+
+ for (ios = io_sources, i = 0; i < n_io_sources; i++, ios++)
+ m->cancel_io(m, *ios);
+ free(io_sources);
+}
diff --git a/polyp/alsa-util.h b/polyp/alsa-util.h
new file mode 100644
index 00000000..03e427d9
--- /dev/null
+++ b/polyp/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 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 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 "sample.h"
+#include "mainloop-api.h"
+
+int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *buffer_size);
+
+int pa_create_io_sources(snd_pcm_t *pcm_handle, struct pa_mainloop_api *m, void ***io_sources, unsigned *n_io_sources, void (*cb)(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata);
+void pa_free_io_sources(struct pa_mainloop_api* m, void **io_sources, unsigned n_io_sources);
+
+#endif
diff --git a/polyp/authkey.c b/polyp/authkey.c
new file mode 100644
index 00000000..3180b8fd
--- /dev/null
+++ b/polyp/authkey.c
@@ -0,0 +1,151 @@
+/* $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 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 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 "authkey.h"
+#include "util.h"
+
+#define RANDOM_DEVICE "/dev/urandom"
+
+static int load(const char *fn, void *data, size_t length) {
+ int fd = -1, ret = -1;
+ ssize_t r;
+
+ assert(fn && data && length);
+
+ if ((fd = open(fn, O_RDONLY)) < 0)
+ goto finish;
+
+ if ((r = pa_loop_read(fd, data, length)) < 0 || (size_t) r != length) {
+ ret = -2;
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+ if (fd >= 0)
+ close(fd);
+
+ return ret;
+}
+
+static int generate(const char *fn, void *data, size_t length) {
+ int fd = -1, random_fd = -1, ret = -1;
+ ssize_t r;
+ assert(fn && data && length);
+
+ if ((fd = open(fn, O_WRONLY|O_EXCL|O_CREAT, S_IRUSR | S_IWUSR)) < 0)
+ goto finish;
+
+ if ((random_fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) {
+
+ if ((r = pa_loop_read(random_fd, data, length)) < 0 || (size_t) r != length) {
+ ret = -2;
+ goto finish;
+ }
+
+ } else {
+ uint8_t *p;
+ size_t l;
+ fprintf(stderr, "WARNING: Failed to open entropy device '"RANDOM_DEVICE"': %s, falling back to unsecure pseudo RNG.\n", strerror(errno));
+
+ srandom(time(NULL));
+
+ for (p = data, l = length; l > 0; p++, l--)
+ *p = (uint8_t) random();
+ }
+
+ if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) {
+ ret = -2;
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+ if (fd >= 0) {
+ if (ret != 0)
+ unlink(fn);
+ close(fd);
+ }
+ if (random_fd >= 0)
+ close(random_fd);
+
+ return ret;
+}
+
+int pa_authkey_load(const char *path, void *data, size_t length) {
+ int ret, i;
+
+ assert(path && data && length);
+
+ for (i = 0; i < 10; i++) {
+ if ((ret = load(path, data, length)) < 0)
+ if (ret == -1 && errno == ENOENT)
+ if ((ret = generate(path, data, length)) < 0)
+ if (ret == -1 && errno == EEXIST)
+ continue;
+ break;
+ }
+
+ if (ret < 0)
+ fprintf(stderr, "Failed to load authorization key '%s': %s\n", path, (ret == -1) ? strerror(errno) : "file corrupt");
+
+ return ret;
+}
+
+int pa_authkey_load_from_home(const char *fn, void *data, size_t length) {
+ char *home;
+ char path[PATH_MAX];
+
+ assert(fn && data && length);
+
+ if (!(home = getenv("HOME")))
+ return -2;
+
+ snprintf(path, sizeof(path), "%s/%s", home, fn);
+
+ return pa_authkey_load(path, data, length);
+}
+
+int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
+ assert(fn && data && length);
+
+ if (*fn == '/')
+ return pa_authkey_load(fn, data, length);
+ else
+ return pa_authkey_load_from_home(fn, data, length);
+}
diff --git a/polyp/authkey.h b/polyp/authkey.h
new file mode 100644
index 00000000..acdcc24d
--- /dev/null
+++ b/polyp/authkey.h
@@ -0,0 +1,31 @@
+#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 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 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_from_home(const char *fn, void *data, size_t length);
+int pa_authkey_load_auto(const char *fn, void *data, size_t length);
+
+#endif
diff --git a/polyp/cli-command.c b/polyp/cli-command.c
new file mode 100644
index 00000000..f3a2f8a0
--- /dev/null
+++ b/polyp/cli-command.c
@@ -0,0 +1,557 @@
+/* $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 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 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 "clitext.h"
+
+struct command {
+ const char *name;
+ int (*proc) (struct pa_core *c, struct pa_tokenizer*t, struct pa_strbuf *buf, int *fail, int *verbose);
+ const char *help;
+ unsigned args;
+};
+
+static int pa_cli_command_exit(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_help(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_modules(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_clients(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_sinks(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_sources(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_sink_inputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_source_outputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_stat(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_unload(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_sink_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_sink_input_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_sink_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_kill_sink_input(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
+
+static const struct command commands[] = {
+ { "exit", pa_cli_command_exit, "Terminate the daemon", 1 },
+ { "help", pa_cli_command_help, "Show this help", 1 },
+ { "modules", pa_cli_command_modules, "List loaded modules", 1 },
+ { "sinks", pa_cli_command_sinks, "List loaded sinks", 1 },
+ { "sources", pa_cli_command_sources, "List loaded sources", 1 },
+ { "clients", pa_cli_command_clients, "List loaded clients", 1 },
+ { "sink_inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 },
+ { "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", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
+ { "unload", pa_cli_command_unload, "Unload a module (args: index)", 2},
+ { "sink_volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
+ { "sink_input_volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3},
+ { "sink_default", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
+ { "source_default", 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},
+ { 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) {
+ long index;
+ char *x;
+ index = strtol(n, &x, 0);
+ if (!x || *x != 0 || index < 0)
+ return (uint32_t) PA_IDXSET_INVALID;
+
+ return (uint32_t) index;
+}
+
+static int pa_cli_command_exit(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ assert(c && c->mainloop && t);
+ c->mainloop->quit(c->mainloop, 0);
+ return 0;
+}
+
+static int pa_cli_command_help(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ 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, " %-20s %s\n", command->name, command->help);
+ return 0;
+}
+
+static int pa_cli_command_modules(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ char *s;
+ assert(c && t);
+ s = pa_module_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ free(s);
+ return 0;
+}
+
+static int pa_cli_command_clients(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ char *s;
+ assert(c && t);
+ s = pa_client_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ free(s);
+ return 0;
+}
+
+static int pa_cli_command_sinks(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ char *s;
+ assert(c && t);
+ s = pa_sink_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ free(s);
+ return 0;
+}
+
+static int pa_cli_command_sources(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ char *s;
+ assert(c && t);
+ s = pa_source_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ free(s);
+ return 0;
+}
+
+static int pa_cli_command_sink_inputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ char *s;
+ assert(c && t);
+ s = pa_sink_input_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ free(s);
+ return 0;
+}
+
+static int pa_cli_command_source_outputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ char *s;
+ assert(c && t);
+ s = pa_source_output_list_to_string(c);
+ assert(s);
+ pa_strbuf_puts(buf, s);
+ free(s);
+ return 0;
+}
+
+static int pa_cli_command_stat(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ assert(c && t);
+ pa_strbuf_printf(buf, "Memory blocks allocated: %u, total size: %u bytes.\n", pa_memblock_get_count(), pa_memblock_get_total());
+ return 0;
+}
+
+static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ assert(c && t);
+ pa_cli_command_stat(c, t, buf, fail, verbose);
+ pa_cli_command_modules(c, t, buf, fail, verbose);
+ pa_cli_command_sinks(c, t, buf, fail, verbose);
+ pa_cli_command_sources(c, t, buf, fail, verbose);
+ pa_cli_command_clients(c, t, buf, fail, verbose);
+ pa_cli_command_sink_inputs(c, t, buf, fail, verbose);
+ pa_cli_command_source_outputs(c, t, buf, fail, verbose);
+ return 0;
+}
+
+static int pa_cli_command_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ struct pa_module *m;
+ const char *name;
+ char txt[256];
+ 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;
+ }
+
+ if (*verbose) {
+ snprintf(txt, sizeof(txt), "Module successfully loaded, index: %u.\n", m->index);
+ pa_strbuf_puts(buf, txt);
+ }
+ return 0;
+}
+
+static int pa_cli_command_unload(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ struct pa_module *m;
+ uint32_t index;
+ 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;
+ }
+
+ index = (uint32_t) strtoul(i, &e, 10);
+ if (*e || !(m = pa_idxset_get_by_index(c->modules, index))) {
+ pa_strbuf_puts(buf, "Invalid module index.\n");
+ return -1;
+ }
+
+ pa_module_unload_request(c, m);
+ return 0;
+}
+
+static int pa_cli_command_sink_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ const char *n, *v;
+ char *x = NULL;
+ struct pa_sink *sink;
+ long volume;
+
+ 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;
+ }
+
+ volume = strtol(v, &x, 0);
+ if (!x || *x != 0 || volume < 0) {
+ pa_strbuf_puts(buf, "Failed to parse volume.\n");
+ return -1;
+ }
+
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
+ pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+ return -1;
+ }
+
+ sink->volume = (uint32_t) volume;
+ return 0;
+}
+
+static int pa_cli_command_sink_input_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ const char *n, *v;
+ struct pa_sink_input *si;
+ long volume;
+ uint32_t index;
+ char *x;
+
+ 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 ((index = 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;
+ }
+
+ x = NULL;
+ volume = strtol(v, &x, 0);
+ if (!x || *x != 0 || 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) index))) {
+ pa_strbuf_puts(buf, "No sink input found with this index.\n");
+ return -1;
+ }
+
+ si->volume = (uint32_t) volume;
+ return 0;
+}
+
+static int pa_cli_command_sink_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ const char *n;
+ struct pa_sink *sink;
+ 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;
+ }
+
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
+ pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+ return -1;
+ }
+
+ c->default_sink_index = sink->index;
+ return 0;
+}
+
+static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ const char *n;
+ struct pa_source *source;
+ 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;
+ }
+
+ if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
+ pa_strbuf_puts(buf, "No source found by this name or index.\n");
+ return -1;
+ }
+
+ c->default_source_index = source->index;
+ return 0;
+}
+
+static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ const char *n;
+ struct pa_client *client;
+ uint32_t index;
+ 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 ((index = 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, index))) {
+ 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(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ const char *n;
+ struct pa_sink_input *sink_input;
+ uint32_t index;
+ 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 ((index = 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, index))) {
+ 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(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
+ const char *n;
+ struct pa_source_output *source_output;
+ uint32_t index;
+ 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 ((index = 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, index))) {
+ pa_strbuf_puts(buf, "No source output found by this index.\n");
+ return -1;
+ }
+
+ pa_source_output_kill(source_output);
+ return 0;
+}
+
+int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose) {
+ const char *cs;
+
+ cs = s+strspn(s, whitespace);
+
+ if (*cs == '#' || !*cs)
+ return 0;
+ else if (*cs == '.') {
+ static const char fail_meta[] = ".fail";
+ static const char nofail_meta[] = ".nofail";
+ static const char verbose_meta[] = ".verbose";
+ static const char noverbose_meta[] = ".noverbose";
+
+ if (!strcmp(cs, verbose_meta))
+ *verbose = 1;
+ else if (!strcmp(cs, noverbose_meta))
+ *verbose = 0;
+ else if (!strcmp(cs, fail_meta))
+ *fail = 1;
+ else if (!strcmp(cs, nofail_meta))
+ *fail = 0;
+ else {
+ size_t l;
+ static const char include_meta[] = ".include";
+ 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, verbose) < 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;
+ struct pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
+ assert(t);
+ ret = command->proc(c, t, buf, fail, verbose);
+ 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(struct pa_core *c, const char *fn, struct pa_strbuf *buf, int *fail, int *verbose) {
+ 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;
+ }
+
+ if (*verbose)
+ pa_strbuf_printf(buf, "Executing file: '%s'\n", fn);
+
+ while (fgets(line, sizeof(line), f)) {
+ char *e = line + strcspn(line, linebreak);
+ *e = 0;
+
+ if (pa_cli_command_execute_line(c, line, buf, fail, verbose) < 0 && *fail)
+ goto fail;
+ }
+
+ if (*verbose)
+ pa_strbuf_printf(buf, "Executed file: '%s'\n", fn);
+
+ ret = 0;
+
+fail:
+ if (f)
+ fclose(f);
+
+ return ret;
+}
+
+int pa_cli_command_execute(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose) {
+ const char *p;
+ assert(c && s && buf && fail && verbose);
+
+ p = s;
+ while (*p) {
+ size_t l = strcspn(p, linebreak);
+ char *line = strndup(p, l);
+ assert(line);
+
+ if (pa_cli_command_execute_line(c, line, buf, fail, verbose) < 0&& *fail) {
+ free(line);
+ return -1;
+ }
+ free(line);
+
+ p += l;
+ p += strspn(p, linebreak);
+ }
+
+ return 0;
+}
diff --git a/polyp/cli-command.h b/polyp/cli-command.h
new file mode 100644
index 00000000..b3c601ad
--- /dev/null
+++ b/polyp/cli-command.h
@@ -0,0 +1,32 @@
+#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 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 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"
+
+int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose);
+int pa_cli_command_execute_file(struct pa_core *c, const char *fn, struct pa_strbuf *buf, int *fail, int *verbose);
+int pa_cli_command_execute(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose);
+
+#endif
diff --git a/polyp/cli.c b/polyp/cli.c
new file mode 100644
index 00000000..f2aa5a65
--- /dev/null
+++ b/polyp/cli.c
@@ -0,0 +1,148 @@
+/* $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 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 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 "clitext.h"
+#include "cli-command.h"
+
+struct pa_cli {
+ struct pa_core *core;
+ struct pa_ioline *line;
+
+ void (*eof_callback)(struct pa_cli *c, void *userdata);
+ void *userdata;
+
+ struct pa_client *client;
+
+ int fail, verbose, kill_requested, defer_kill;
+};
+
+static void line_callback(struct pa_ioline *line, const char *s, void *userdata);
+
+static const char prompt[] = ">>> ";
+
+static void client_kill(struct pa_client *c);
+
+struct pa_cli* pa_cli_new(struct pa_core *core, struct pa_iochannel *io, struct pa_module *m) {
+ char cname[256];
+ struct pa_cli *c;
+ assert(io);
+
+ c = malloc(sizeof(struct pa_cli));
+ assert(c);
+ 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, "CLI", 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");
+ pa_ioline_puts(c->line, prompt);
+
+ c->fail = c->kill_requested = c->defer_kill = 0;
+ c->verbose = 1;
+
+ return c;
+}
+
+void pa_cli_free(struct pa_cli *c) {
+ assert(c);
+ pa_ioline_free(c->line);
+ pa_client_free(c->client);
+ free(c);
+}
+
+static void client_kill(struct pa_client *client) {
+ struct pa_cli *c;
+ assert(client && client->userdata);
+ c = client->userdata;
+
+ fprintf(stderr, "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(struct pa_ioline *line, const char *s, void *userdata) {
+ struct pa_strbuf *buf;
+ struct pa_cli *c = userdata;
+ char *p;
+ assert(line && c);
+
+ if (!s) {
+ fprintf(stderr, "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->verbose);
+ c->defer_kill--;
+ pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf));
+ free(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(struct pa_cli *c, void (*cb)(struct pa_cli*c, void *userdata), void *userdata) {
+ assert(c && cb);
+ c->eof_callback = cb;
+ c->userdata = userdata;
+}
diff --git a/polyp/cli.h b/polyp/cli.h
new file mode 100644
index 00000000..9cfee0d8
--- /dev/null
+++ b/polyp/cli.h
@@ -0,0 +1,36 @@
+#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 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 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"
+
+struct pa_cli;
+
+struct pa_cli* pa_cli_new(struct pa_core *core, struct pa_iochannel *io, struct pa_module *m);
+void pa_cli_free(struct pa_cli *cli);
+
+void pa_cli_set_eof_callback(struct pa_cli *cli, void (*cb)(struct pa_cli*c, void *userdata), void *userdata);
+
+#endif
diff --git a/polyp/client.c b/polyp/client.c
new file mode 100644
index 00000000..0294c9e2
--- /dev/null
+++ b/polyp/client.c
@@ -0,0 +1,79 @@
+/* $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 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 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"
+
+struct pa_client *pa_client_new(struct pa_core *core, const char *protocol_name, char *name) {
+ struct pa_client *c;
+ int r;
+ assert(core);
+
+ c = malloc(sizeof(struct pa_client));
+ assert(c);
+ c->name = name ? strdup(name) : NULL;
+ c->owner = NULL;
+ c->core = core;
+ c->protocol_name = protocol_name;
+
+ c->kill = NULL;
+ c->userdata = NULL;
+
+ r = pa_idxset_put(core->clients, c, &c->index);
+ assert(c->index != PA_IDXSET_INVALID && r >= 0);
+
+ fprintf(stderr, "client: created %u \"%s\"\n", c->index, c->name);
+
+ return c;
+}
+
+void pa_client_free(struct pa_client *c) {
+ assert(c && c->core);
+
+ pa_idxset_remove_by_data(c->core->clients, c, NULL);
+ fprintf(stderr, "client: freed %u \"%s\"\n", c->index, c->name);
+ free(c->name);
+ free(c);
+}
+
+void pa_client_kill(struct pa_client *c) {
+ assert(c);
+ if (!c->kill) {
+ fprintf(stderr, "kill() operation not implemented for client %u\n", c->index);
+ return;
+ }
+
+ c->kill(c);
+}
+
+void pa_client_rename(struct pa_client *c, const char *name) {
+ assert(c);
+ free(c->name);
+ c->name = name ? strdup(name) : NULL;
+}
diff --git a/polyp/client.h b/polyp/client.h
new file mode 100644
index 00000000..c926208e
--- /dev/null
+++ b/polyp/client.h
@@ -0,0 +1,51 @@
+#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 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 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"
+
+struct pa_client {
+ uint32_t index;
+
+ struct pa_module *owner;
+ char *name;
+ struct pa_core *core;
+ const char *protocol_name;
+
+ void (*kill)(struct pa_client *c);
+ void *userdata;
+};
+
+struct pa_client *pa_client_new(struct pa_core *c, const char *protocol_name, char *name);
+
+/* This function should be called only by the code that created the client */
+void pa_client_free(struct pa_client *c);
+
+/* Code that didn't create the client should call this function to
+ * request destruction of the client */
+void pa_client_kill(struct pa_client *c);
+
+void pa_client_rename(struct pa_client *c, const char *name);
+
+#endif
diff --git a/polyp/clitext.c b/polyp/clitext.c
new file mode 100644
index 00000000..c1b9953b
--- /dev/null
+++ b/polyp/clitext.c
@@ -0,0 +1,203 @@
+/* $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 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 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 "clitext.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"
+
+char *pa_module_list_to_string(struct pa_core *c) {
+ struct pa_strbuf *s;
+ struct pa_module *m;
+ uint32_t index = PA_IDXSET_INVALID;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_ncontents(c->modules));
+
+ for (m = pa_idxset_first(c->modules, &index); m; m = pa_idxset_next(c->modules, &index))
+ pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n", m->index, m->name, m->argument);
+
+ return pa_strbuf_tostring_free(s);
+}
+
+char *pa_client_list_to_string(struct pa_core *c) {
+ struct pa_strbuf *s;
+ struct pa_client *client;
+ uint32_t index = PA_IDXSET_INVALID;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u client(s).\n", pa_idxset_ncontents(c->clients));
+
+ for (client = pa_idxset_first(c->clients, &index); client; client = pa_idxset_next(c->clients, &index)) {
+ pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tprotocol_name: <%s>\n", client->index, client->name, client->protocol_name);
+
+ 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(struct pa_core *c) {
+ struct pa_strbuf *s;
+ struct pa_sink *sink, *default_sink;
+ uint32_t index = PA_IDXSET_INVALID;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_ncontents(c->sinks));
+
+ default_sink = pa_sink_get_default(c);
+
+ for (sink = pa_idxset_first(c->sinks, &index); sink; sink = pa_idxset_next(c->sinks, &index)) {
+ char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH];
+ pa_sample_snprint(ss, sizeof(ss), &sink->sample_spec);
+ assert(sink->monitor_source);
+ pa_strbuf_printf(
+ s,
+ " %c index: %u\n\tname: <%s>\n\tvolume: <0x%04x>\n\tlatency: <%u usec>\n\tmonitor_source: <%u>\n\tsample_spec: <%s>\n",
+ sink == default_sink ? '*' : ' ',
+ sink->index, sink->name,
+ (unsigned) sink->volume,
+ pa_sink_get_latency(sink),
+ sink->monitor_source->index,
+ ss);
+
+ 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(struct pa_core *c) {
+ struct pa_strbuf *s;
+ struct pa_source *source, *default_source;
+ uint32_t index = PA_IDXSET_INVALID;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_ncontents(c->sources));
+
+ default_source = pa_source_get_default(c);
+
+ for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index)) {
+ char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH];
+ pa_sample_snprint(ss, sizeof(ss), &source->sample_spec);
+ pa_strbuf_printf(s, " %c index: %u\n\tname: <%s>\n\tsample_spec: <%s>\n", source == default_source ? '*' : ' ', source->index, source->name, ss);
+
+ 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(struct pa_core *c) {
+ struct pa_strbuf *s;
+ struct pa_source_output *o;
+ uint32_t index = PA_IDXSET_INVALID;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_ncontents(c->source_outputs));
+
+ for (o = pa_idxset_first(c->source_outputs, &index); o; o = pa_idxset_next(c->source_outputs, &index)) {
+ char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH];
+ pa_sample_snprint(ss, sizeof(ss), &o->sample_spec);
+ assert(o->source);
+ pa_strbuf_printf(
+ s, " index: %u\n\tname: <%s>\n\tsource: <%u>\n\tsample_spec: <%s>\n",
+ o->index,
+ o->name,
+ o->source->index,
+ ss);
+ if (o->owner)
+ pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index);
+ if (o->client)
+ pa_strbuf_printf(s, "\tclient: <%u>\n", o->client->index);
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
+
+char *pa_sink_input_list_to_string(struct pa_core *c) {
+ struct pa_strbuf *s;
+ struct pa_sink_input *i;
+ uint32_t index = PA_IDXSET_INVALID;
+ assert(c);
+
+ s = pa_strbuf_new();
+ assert(s);
+
+ pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_ncontents(c->sink_inputs));
+
+ for (i = pa_idxset_first(c->sink_inputs, &index); i; i = pa_idxset_next(c->sink_inputs, &index)) {
+ char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH];
+ pa_sample_snprint(ss, sizeof(ss), &i->sample_spec);
+ assert(i->sink);
+ pa_strbuf_printf(
+ s, " index: %u\n\tname: <%s>\n\tsink: <%u>\n\tvolume: <0x%04x>\n\tlatency: <%u usec>\n\tsample_spec: <%s>\n",
+ i->index,
+ i->name,
+ i->sink->index,
+ (unsigned) i->volume,
+ pa_sink_input_get_latency(i),
+ ss);
+
+ if (i->owner)
+ pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index);
+ if (i->client)
+ pa_strbuf_printf(s, "\tclient: <%u>\n", i->client->index);
+ }
+
+ return pa_strbuf_tostring_free(s);
+}
diff --git a/polyp/clitext.h b/polyp/clitext.h
new file mode 100644
index 00000000..b1718cb5
--- /dev/null
+++ b/polyp/clitext.h
@@ -0,0 +1,35 @@
+#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 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 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"
+
+char *pa_sink_input_list_to_string(struct pa_core *c);
+char *pa_source_output_list_to_string(struct pa_core *c);
+char *pa_sink_list_to_string(struct pa_core *core);
+char *pa_source_list_to_string(struct pa_core *c);
+char *pa_client_list_to_string(struct pa_core *c);
+char *pa_module_list_to_string(struct pa_core *c);
+
+#endif
+
diff --git a/polyp/cmdline.c b/polyp/cmdline.c
new file mode 100644
index 00000000..a3330145
--- /dev/null
+++ b/polyp/cmdline.c
@@ -0,0 +1,111 @@
+/* $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 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 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 "cmdline.h"
+#include "util.h"
+#include "strbuf.h"
+
+void pa_cmdline_help(const char *argv0) {
+ const char *e;
+
+ if ((e = strrchr(argv0, '/')))
+ e++;
+ else
+ e = argv0;
+
+ printf("%s [options]\n"
+ " -L MODULE Load the specified plugin module with the specified argument\n"
+ " -F FILE Run the specified script\n"
+ " -C Open a command line on the running TTY\n"
+ " -D Daemonize after loading the modules\n"
+ " -f Dont quit when the startup fails\n"
+ " -v Verbose startup\n"
+ " -h Show this help\n", e);
+}
+
+struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []) {
+ char c;
+ struct pa_cmdline *cmdline = NULL;
+ struct pa_strbuf *buf = NULL;
+ assert(argc && argv);
+
+ cmdline = malloc(sizeof(struct pa_cmdline));
+ assert(cmdline);
+ cmdline->daemonize = cmdline->help = cmdline->verbose = 0;
+ cmdline->fail = 1;
+
+ buf = pa_strbuf_new();
+ assert(buf);
+
+ while ((c = getopt(argc, argv, "L:F:CDhfv")) != -1) {
+ switch (c) {
+ case 'L':
+ pa_strbuf_printf(buf, "load %s\n", optarg);
+ break;
+ case 'F':
+ pa_strbuf_printf(buf, ".include %s\n", optarg);
+ break;
+ case 'C':
+ pa_strbuf_puts(buf, "load module-cli\n");
+ break;
+ case 'D':
+ cmdline->daemonize = 1;
+ break;
+ case 'h':
+ cmdline->help = 1;
+ break;
+ case 'f':
+ cmdline->fail = 0;
+ break;
+ case 'v':
+ cmdline->verbose = 0;
+ break;
+ default:
+ goto fail;
+ }
+ }
+
+ cmdline->cli_commands = pa_strbuf_tostring_free(buf);
+ return cmdline;
+
+fail:
+ if (cmdline)
+ pa_cmdline_free(cmdline);
+ if (buf)
+ pa_strbuf_free(buf);
+ return NULL;
+}
+
+void pa_cmdline_free(struct pa_cmdline *cmd) {
+ assert(cmd);
+ free(cmd->cli_commands);
+ free(cmd);
+}
diff --git a/polyp/cmdline.h b/polyp/cmdline.h
new file mode 100644
index 00000000..95ce91de
--- /dev/null
+++ b/polyp/cmdline.h
@@ -0,0 +1,36 @@
+#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 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 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.
+***/
+
+
+struct pa_cmdline {
+ int daemonize, help, fail, verbose;
+ char *cli_commands;
+};
+
+struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []);
+void pa_cmdline_free(struct pa_cmdline *cmd);
+
+void pa_cmdline_help(const char *argv0);
+
+#endif
diff --git a/polyp/core.c b/polyp/core.c
new file mode 100644
index 00000000..dc9525a8
--- /dev/null
+++ b/polyp/core.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 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 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 "core.h"
+#include "module.h"
+#include "sink.h"
+#include "source.h"
+#include "namereg.h"
+#include "util.h"
+
+struct pa_core* pa_core_new(struct pa_mainloop_api *m) {
+ struct pa_core* c;
+ c = malloc(sizeof(struct pa_core));
+ assert(c);
+
+ 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_index = c->default_sink_index = PA_IDXSET_INVALID;
+
+ c->modules = NULL;
+ c->namereg = NULL;
+
+ c->default_sample_spec.format = PA_SAMPLE_S16NE;
+ c->default_sample_spec.rate = 44100;
+ c->default_sample_spec.channels = 2;
+
+ pa_check_for_sigpipe();
+
+ return c;
+};
+
+void pa_core_free(struct 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_namereg_free(c);
+
+ free(c);
+};
+
diff --git a/polyp/core.h b/polyp/core.h
new file mode 100644
index 00000000..99d7d76a
--- /dev/null
+++ b/polyp/core.h
@@ -0,0 +1,45 @@
+#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 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 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 "idxset.h"
+#include "hashmap.h"
+#include "mainloop-api.h"
+#include "sample.h"
+
+struct pa_core {
+ struct pa_mainloop_api *mainloop;
+
+ struct pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules;
+
+ struct pa_hashmap *namereg;
+
+ uint32_t default_source_index, default_sink_index;
+
+ struct pa_sample_spec default_sample_spec;
+};
+
+struct pa_core* pa_core_new(struct pa_mainloop_api *m);
+void pa_core_free(struct pa_core*c);
+
+#endif
diff --git a/polyp/depmod.py b/polyp/depmod.py
new file mode 100755
index 00000000..85dc66a8
--- /dev/null
+++ b/polyp/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 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 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/polyp/dynarray.c b/polyp/dynarray.c
new file mode 100644
index 00000000..24306964
--- /dev/null
+++ b/polyp/dynarray.c
@@ -0,0 +1,98 @@
+/* $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 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 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"
+
+struct pa_dynarray {
+ void **data;
+ unsigned n_allocated, n_entries;
+};
+
+struct pa_dynarray* pa_dynarray_new(void) {
+ struct pa_dynarray *a;
+ a = malloc(sizeof(struct pa_dynarray));
+ assert(a);
+ a->data = NULL;
+ a->n_entries = 0;
+ a->n_allocated = 0;
+ return a;
+}
+
+void pa_dynarray_free(struct 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);
+
+ free(a->data);
+ free(a);
+}
+
+void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p) {
+ assert(a);
+
+ if (i >= a->n_allocated) {
+ unsigned n;
+
+ if (!p)
+ return;
+
+ n = i+100;
+ a->data = realloc(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(struct pa_dynarray*a, void *p) {
+ unsigned i = a->n_entries;
+ pa_dynarray_put(a, i, p);
+ return i;
+}
+
+void *pa_dynarray_get(struct pa_dynarray*a, unsigned i) {
+ assert(a);
+ if (i >= a->n_allocated)
+ return NULL;
+ assert(a->data);
+ return a->data[i];
+}
+
+unsigned pa_dynarray_ncontents(struct pa_dynarray*a) {
+ assert(a);
+ return a->n_entries;
+}
diff --git a/polyp/dynarray.h b/polyp/dynarray.h
new file mode 100644
index 00000000..56ec5386
--- /dev/null
+++ b/polyp/dynarray.h
@@ -0,0 +1,37 @@
+#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 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 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.
+***/
+
+struct pa_dynarray;
+
+struct pa_dynarray* pa_dynarray_new(void);
+void pa_dynarray_free(struct pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata);
+
+void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p);
+unsigned pa_dynarray_append(struct pa_dynarray*a, void *p);
+
+void *pa_dynarray_get(struct pa_dynarray*a, unsigned i);
+
+unsigned pa_dynarray_ncontents(struct pa_dynarray*a);
+
+#endif
diff --git a/polyp/endianmacros.h b/polyp/endianmacros.h
new file mode 100644
index 00000000..cd7b7d83
--- /dev/null
+++ b/polyp/endianmacros.h
@@ -0,0 +1,62 @@
+#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 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 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)(((int16_t) x >> 8) | ((int16_t) x << 8)))
+#define UINT16_SWAP(x) ((uint16_t)(((uint16_t) x >> 8) | ((uint16_t) x << 8)))
+#define INT32_SWAP(x) ((int32_t)(((int32_t) x >> 24) | ((int32_t) x << 24) | (((int32_t) x & 0xFF00) << 16) | (((int32_t) x) >> 16) & 0xFF00))
+#define UINT32_SWAP(x) ((uint32_t)(((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 16) | (((uint32_t) x) >> 16) & 0xFF00))
+
+#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))
+#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)
+#endif
+
+#endif
diff --git a/polyp/esound.h b/polyp/esound.h
new file mode 100644
index 00000000..01a2962f
--- /dev/null
+++ b/polyp/esound.h
@@ -0,0 +1,213 @@
+#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 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 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;
+
+/* switch endian order for cross platform playing */
+#define swap_endian_32(x) ((x >> 24) | ((x >> 8) & 0xFF00) | (((x & 0xFF00) << 8)) | (x << 24))
+#define maybe_swap_endian_32(c,x) ((c) ? swap_endian_32(x) : x)
+
+/* 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_t) swap_endian_32(ESD_ENDIAN_KEY))
+
+
+#endif
diff --git a/polyp/hashmap.c b/polyp/hashmap.c
new file mode 100644
index 00000000..2c7c92b5
--- /dev/null
+++ b/polyp/hashmap.c
@@ -0,0 +1,170 @@
+/* $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 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 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"
+
+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);
+};
+
+struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
+ struct pa_hashmap *h;
+ h = malloc(sizeof(struct pa_hashmap));
+ assert(h);
+ h->data = malloc(sizeof(struct hashmap_entry*)*(h->size = 1023));
+ assert(h->data);
+ memset(h->data, 0, sizeof(struct hashmap_entry*)*(h->size = 1023));
+ 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(struct 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
+ h->data[e->hash] = e->bucket_next;
+
+ free(e);
+ h->n_entries--;
+}
+
+void pa_hashmap_free(struct 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);
+ }
+
+ free(h->data);
+ free(h);
+}
+
+static struct hashmap_entry *get(struct pa_hashmap *h, unsigned hash, const void *key) {
+ struct hashmap_entry *e;
+
+ 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(struct pa_hashmap *h, const void *key, void *value) {
+ struct hashmap_entry *e;
+ unsigned hash;
+ assert(h && key);
+
+ hash = h->hash_func(key) % h->size;
+
+ if ((e = get(h, hash, key)))
+ return -1;
+
+ e = malloc(sizeof(struct hashmap_entry));
+ assert(e);
+
+ 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(struct 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;
+}
+
+int pa_hashmap_remove(struct pa_hashmap *h, const void *key) {
+ struct hashmap_entry *e;
+ unsigned hash;
+ assert(h && key);
+
+ hash = h->hash_func(key) % h->size;
+
+ if (!(e = get(h, hash, key)))
+ return 1;
+
+ remove(h, e);
+ return 0;
+}
+
+unsigned pa_hashmap_ncontents(struct pa_hashmap *h) {
+ return h->n_entries;
+}
diff --git a/polyp/hashmap.h b/polyp/hashmap.h
new file mode 100644
index 00000000..b24e74a5
--- /dev/null
+++ b/polyp/hashmap.h
@@ -0,0 +1,37 @@
+#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 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 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.
+***/
+
+struct pa_hashmap;
+
+struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
+void pa_hashmap_free(struct pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata);
+
+int pa_hashmap_put(struct pa_hashmap *h, const void *key, void *value);
+void* pa_hashmap_get(struct pa_hashmap *h, const void *key);
+
+int pa_hashmap_remove(struct pa_hashmap *h, const void *key);
+
+unsigned pa_hashmap_ncontents(struct pa_hashmap *h);
+
+#endif
diff --git a/polyp/idxset.c b/polyp/idxset.c
new file mode 100644
index 00000000..cecda6b7
--- /dev/null
+++ b/polyp/idxset.c
@@ -0,0 +1,397 @@
+/* $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 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 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"
+
+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;
+};
+
+struct pa_idxset {
+ unsigned (*hash_func) (const void *p);
+ int (*compare_func)(const void *a, const void *b);
+
+ unsigned hash_table_size, n_entries;
+ struct 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;
+}
+
+struct pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
+ struct pa_idxset *s;
+
+ s = malloc(sizeof(struct pa_idxset));
+ assert(s);
+ 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 = malloc(sizeof(struct idxset_entry*)*s->hash_table_size);
+ assert(s->hash_table);
+ memset(s->hash_table, 0, sizeof(struct 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(struct pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) {
+ assert(s);
+
+ if (free_func) {
+ while (s->iterate_list_head) {
+ struct 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);
+ free(e);
+ }
+ }
+
+ free(s->hash_table);
+ free(s->array);
+ free(s);
+}
+
+static struct idxset_entry* hash_scan(struct pa_idxset *s, struct idxset_entry* e, 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(struct pa_idxset *s, uint32_t index) {
+ uint32_t i, j, l;
+ struct idxset_entry** n;
+ assert(index >= s->start_index);
+
+ if (index < s->start_index + s->array_size)
+ return;
+
+ for (i = 0; i < s->array_size; i++)
+ if (s->array[i])
+ break;
+
+ l = index - s->start_index - i + 100;
+ n = malloc(sizeof(struct hash_table_entry*)*l);
+ assert(n);
+ memset(n, 0, sizeof(struct hash_table_entry*)*l);
+
+ for (j = 0; j < s->array_size-i; j++)
+ n[j] = s->array[i+j];
+
+ free(s->array);
+
+ s->array = n;
+ s->array_size = l;
+ s->start_index += i;
+}
+
+static struct idxset_entry** array_index(struct pa_idxset*s, uint32_t index) {
+ if (index >= s->start_index + s->array_size)
+ return NULL;
+
+ if (index < s->start_index)
+ return NULL;
+
+ return s->array + (index - s->start_index);
+}
+
+int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index) {
+ unsigned h;
+ struct 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 (index)
+ *index = e->index;
+
+ return -1;
+ }
+
+ e = malloc(sizeof(struct idxset_entry));
+ assert(e);
+
+ 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 (index)
+ *index = e->index;
+
+ return 0;
+}
+
+void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index) {
+ struct idxset_entry **a;
+ assert(s);
+
+ if (!(a = array_index(s, index)))
+ return NULL;
+
+ if (!*a)
+ return NULL;
+
+ return (*a)->data;
+}
+
+void* pa_idxset_get_by_data(struct pa_idxset*s, void *p, uint32_t *index) {
+ unsigned h;
+ struct 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 (index)
+ *index = e->index;
+
+ return e->data;
+}
+
+static void remove_entry(struct pa_idxset *s, struct idxset_entry *e) {
+ struct 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;
+
+ free(e);
+
+ assert(s->n_entries >= 1);
+ s->n_entries--;
+}
+
+void* pa_idxset_remove_by_index(struct pa_idxset*s, uint32_t index) {
+ struct idxset_entry **a;
+ void *data;
+
+ assert(s);
+
+ if (!(a = array_index(s, index)))
+ return NULL;
+
+ data = (*a)->data;
+ remove_entry(s, *a);
+
+ return data;
+}
+
+void* pa_idxset_remove_by_data(struct pa_idxset*s, void *data, uint32_t *index) {
+ struct idxset_entry *e;
+ unsigned h;
+
+ 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;
+
+ data = e->data;
+ if (index)
+ *index = e->index;
+
+ remove_entry(s, e);
+
+ return data;
+}
+
+void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index) {
+ struct idxset_entry **a, *e = NULL;
+ assert(s && index);
+
+ if ((a = array_index(s, *index)) && *a)
+ e = (*a)->iterate_next;
+
+ if (!e)
+ e = s->iterate_list_head;
+
+ if (!e)
+ return NULL;
+
+ *index = e->index;
+ return e->data;
+}
+
+void* pa_idxset_first(struct pa_idxset *s, uint32_t *index) {
+ assert(s);
+
+ if (!s->iterate_list_head)
+ return NULL;
+
+ if (index)
+ *index = s->iterate_list_head->index;
+ return s->iterate_list_head->data;
+}
+
+void *pa_idxset_next(struct pa_idxset *s, uint32_t *index) {
+ struct idxset_entry **a, *e = NULL;
+ assert(s && index);
+
+ if ((a = array_index(s, *index)) && *a)
+ e = (*a)->iterate_next;
+
+ if (e) {
+ *index = e->index;
+ return e->data;
+ } else {
+ *index = PA_IDXSET_INVALID;
+ return NULL;
+ }
+}
+
+
+int pa_idxset_foreach(struct pa_idxset*s, int (*func)(void *p, uint32_t index, int *del, void*userdata), void *userdata) {
+ struct idxset_entry *e;
+ assert(s && func);
+
+ e = s->iterate_list_head;
+ while (e) {
+ int del = 0, r;
+ struct 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_ncontents(struct pa_idxset*s) {
+ assert(s);
+ return s->n_entries;
+}
+
+int pa_idxset_isempty(struct pa_idxset *s) {
+ assert(s);
+ return s->n_entries == 0;
+}
+
diff --git a/polyp/idxset.h b/polyp/idxset.h
new file mode 100644
index 00000000..f26b03fb
--- /dev/null
+++ b/polyp/idxset.h
@@ -0,0 +1,63 @@
+#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 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 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>
+
+#define PA_IDXSET_INVALID ((uint32_t) -1)
+
+unsigned pa_idxset_trivial_hash_func(const void *p);
+int pa_idxset_trivial_compare_func(const void *a, const void *b);
+
+unsigned pa_idxset_string_hash_func(const void *p);
+int pa_idxset_string_compare_func(const void *a, const void *b);
+
+struct pa_idxset;
+
+struct pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
+void pa_idxset_free(struct pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata);
+
+int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index);
+
+void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index);
+void* pa_idxset_get_by_data(struct pa_idxset*s, void *p, uint32_t *index);
+
+void* pa_idxset_remove_by_index(struct pa_idxset*s, uint32_t index);
+void* pa_idxset_remove_by_data(struct pa_idxset*s, void *p, uint32_t *index);
+
+/* 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 *index =
+ PA_IDXSET_VALID first. */
+void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index);
+
+/* Return the oldest entry in the idxset */
+void* pa_idxset_first(struct pa_idxset *s, uint32_t *index);
+void *pa_idxset_next(struct pa_idxset *s, uint32_t *index);
+
+int pa_idxset_foreach(struct pa_idxset*s, int (*func)(void *p, uint32_t index, int *del, void*userdata), void *userdata);
+
+unsigned pa_idxset_ncontents(struct pa_idxset*s);
+int pa_idxset_isempty(struct pa_idxset *s);
+
+#endif
diff --git a/polyp/iochannel.c b/polyp/iochannel.c
new file mode 100644
index 00000000..69d381f4
--- /dev/null
+++ b/polyp/iochannel.c
@@ -0,0 +1,222 @@
+/* $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 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 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 "iochannel.h"
+#include "util.h"
+#include "socket-util.h"
+
+struct pa_iochannel {
+ int ifd, ofd;
+ struct pa_mainloop_api* mainloop;
+
+ void (*callback)(struct pa_iochannel*io, void *userdata);
+ void*userdata;
+
+ int readable;
+ int writable;
+ int hungup;
+
+ int no_close;
+
+ void* input_source, *output_source;
+};
+
+static void enable_mainloop_sources(struct pa_iochannel *io) {
+ assert(io);
+
+ if (io->input_source == io->output_source) {
+ enum pa_mainloop_api_io_events e = PA_MAINLOOP_API_IO_EVENT_NULL;
+ assert(io->input_source);
+
+ if (!io->readable)
+ e |= PA_MAINLOOP_API_IO_EVENT_INPUT;
+ if (!io->writable)
+ e |= PA_MAINLOOP_API_IO_EVENT_OUTPUT;
+
+ io->mainloop->enable_io(io->mainloop, io->input_source, e);
+ } else {
+ if (io->input_source)
+ io->mainloop->enable_io(io->mainloop, io->input_source, io->readable ? PA_MAINLOOP_API_IO_EVENT_NULL : PA_MAINLOOP_API_IO_EVENT_INPUT);
+ if (io->output_source)
+ io->mainloop->enable_io(io->mainloop, io->output_source, io->writable ? PA_MAINLOOP_API_IO_EVENT_NULL : PA_MAINLOOP_API_IO_EVENT_OUTPUT);
+ }
+}
+
+static void callback(struct pa_mainloop_api* m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
+ struct pa_iochannel *io = userdata;
+ int changed = 0;
+ assert(m && fd >= 0 && events && userdata);
+
+ if ((events & PA_MAINLOOP_API_IO_EVENT_HUP) && !io->hungup) {
+ io->hungup = 1;
+ changed = 1;
+ }
+
+ if ((events & PA_MAINLOOP_API_IO_EVENT_INPUT) && !io->readable) {
+ io->readable = 1;
+ changed = 1;
+ assert(id == io->input_source);
+ }
+
+ if ((events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) && !io->writable) {
+ io->writable = 1;
+ changed = 1;
+ assert(id == io->output_source);
+ }
+
+ if (changed) {
+ enable_mainloop_sources(io);
+
+ if (io->callback)
+ io->callback(io, io->userdata);
+ }
+}
+
+struct pa_iochannel* pa_iochannel_new(struct pa_mainloop_api*m, int ifd, int ofd) {
+ struct pa_iochannel *io;
+ assert(m && (ifd >= 0 || ofd >= 0));
+
+ io = malloc(sizeof(struct pa_iochannel));
+ 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;
+
+ if (ifd == ofd) {
+ assert(ifd >= 0);
+ pa_make_nonblock_fd(io->ifd);
+ io->input_source = io->output_source = m->source_io(m, ifd, PA_MAINLOOP_API_IO_EVENT_BOTH, callback, io);
+ } else {
+
+ if (ifd >= 0) {
+ pa_make_nonblock_fd(io->ifd);
+ io->input_source = m->source_io(m, ifd, PA_MAINLOOP_API_IO_EVENT_INPUT, callback, io);
+ } else
+ io->input_source = NULL;
+
+ if (ofd >= 0) {
+ pa_make_nonblock_fd(io->ofd);
+ io->output_source = m->source_io(m, ofd, PA_MAINLOOP_API_IO_EVENT_OUTPUT, callback, io);
+ } else
+ io->output_source = NULL;
+ }
+
+ return io;
+}
+
+void pa_iochannel_free(struct pa_iochannel*io) {
+ assert(io);
+
+ if (!io->no_close) {
+ if (io->ifd >= 0)
+ close(io->ifd);
+ if (io->ofd >= 0 && io->ofd != io->ifd)
+ close(io->ofd);
+ }
+
+ if (io->input_source)
+ io->mainloop->cancel_io(io->mainloop, io->input_source);
+ if (io->output_source && (io->output_source != io->input_source))
+ io->mainloop->cancel_io(io->mainloop, io->output_source);
+
+ free(io);
+}
+
+int pa_iochannel_is_readable(struct pa_iochannel*io) {
+ assert(io);
+ return io->readable;
+}
+
+int pa_iochannel_is_writable(struct pa_iochannel*io) {
+ assert(io);
+ return io->writable;
+}
+
+int pa_iochannel_is_hungup(struct pa_iochannel*io) {
+ assert(io);
+ return io->hungup;
+}
+
+ssize_t pa_iochannel_write(struct pa_iochannel*io, const void*data, size_t l) {
+ ssize_t r;
+ assert(io && data && l && io->ofd >= 0);
+
+ if ((r = write(io->ofd, data, l)) >= 0) {
+ io->writable = 0;
+ enable_mainloop_sources(io);
+ }
+
+ return r;
+}
+
+ssize_t pa_iochannel_read(struct pa_iochannel*io, void*data, size_t l) {
+ ssize_t r;
+
+ assert(io && data && io->ifd >= 0);
+
+ if ((r = read(io->ifd, data, l)) >= 0) {
+ io->readable = 0;
+ enable_mainloop_sources(io);
+ }
+
+ return r;
+}
+
+void pa_iochannel_set_callback(struct pa_iochannel*io, void (*callback)(struct pa_iochannel*io, void *userdata), void *userdata) {
+ assert(io);
+ io->callback = callback;
+ io->userdata = userdata;
+}
+
+void pa_iochannel_set_noclose(struct pa_iochannel*io, int b) {
+ assert(io);
+ io->no_close = b;
+}
+
+void pa_iochannel_socket_peer_to_string(struct pa_iochannel*io, char*s, size_t l) {
+ assert(io && s && l);
+ pa_socket_peer_to_string(io->ifd, s, l);
+}
+
+int pa_iochannel_socket_set_rcvbuf(struct pa_iochannel *io, size_t l) {
+ assert(io);
+ return pa_socket_set_rcvbuf(io->ifd, l);
+}
+
+int pa_iochannel_socket_set_sndbuf(struct pa_iochannel *io, size_t l) {
+ assert(io);
+ return pa_socket_set_sndbuf(io->ofd, l);
+}
diff --git a/polyp/iochannel.h b/polyp/iochannel.h
new file mode 100644
index 00000000..6f5f351c
--- /dev/null
+++ b/polyp/iochannel.h
@@ -0,0 +1,50 @@
+#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 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 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 "mainloop-api.h"
+
+/* It is safe to destroy the calling iochannel object from the callback */
+
+struct pa_iochannel;
+
+struct pa_iochannel* pa_iochannel_new(struct pa_mainloop_api*m, int ifd, int ofd);
+void pa_iochannel_free(struct pa_iochannel*io);
+
+ssize_t pa_iochannel_write(struct pa_iochannel*io, const void*data, size_t l);
+ssize_t pa_iochannel_read(struct pa_iochannel*io, void*data, size_t l);
+
+int pa_iochannel_is_readable(struct pa_iochannel*io);
+int pa_iochannel_is_writable(struct pa_iochannel*io);
+int pa_iochannel_is_hungup(struct pa_iochannel*io);
+
+void pa_iochannel_set_noclose(struct pa_iochannel*io, int b);
+
+void pa_iochannel_set_callback(struct pa_iochannel*io, void (*callback)(struct pa_iochannel*io, void *userdata), void *userdata);
+
+void pa_iochannel_socket_peer_to_string(struct pa_iochannel*io, char*s, size_t l);
+int pa_iochannel_socket_set_rcvbuf(struct pa_iochannel*io, size_t l);
+int pa_iochannel_socket_set_sndbuf(struct pa_iochannel*io, size_t l);
+
+#endif
diff --git a/polyp/ioline.c b/polyp/ioline.c
new file mode 100644
index 00000000..ff9a03c2
--- /dev/null
+++ b/polyp/ioline.c
@@ -0,0 +1,220 @@
+/* $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 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 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"
+
+#define BUFFER_LIMIT (64*1024)
+#define READ_SIZE (1024)
+
+struct pa_ioline {
+ struct pa_iochannel *io;
+ 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)(struct pa_ioline*io, const char *s, void *userdata);
+ void *userdata;
+};
+
+static void io_callback(struct pa_iochannel*io, void *userdata);
+static int do_write(struct pa_ioline *l);
+
+struct pa_ioline* pa_ioline_new(struct pa_iochannel *io) {
+ struct pa_ioline *l;
+ assert(io);
+
+ l = malloc(sizeof(struct pa_ioline));
+ assert(l);
+ 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;
+
+ pa_iochannel_set_callback(io, io_callback, l);
+
+ return l;
+}
+
+void pa_ioline_free(struct pa_ioline *l) {
+ assert(l);
+ pa_iochannel_free(l->io);
+ free(l->wbuf);
+ free(l->rbuf);
+ free(l);
+}
+
+void pa_ioline_puts(struct pa_ioline *l, const char *c) {
+ size_t len;
+ assert(l && c);
+
+ len = strlen(c);
+ if (len > BUFFER_LIMIT - l->wbuf_valid_length)
+ len = BUFFER_LIMIT - l->wbuf_valid_length;
+
+ if (!len)
+ return;
+
+ if (len > l->wbuf_length - l->wbuf_valid_length) {
+ size_t n = l->wbuf_valid_length+len;
+ char *new = malloc(n);
+ if (l->wbuf) {
+ memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
+ free(l->wbuf);
+ }
+ l->wbuf = new;
+ l->wbuf_length = n;
+ l->wbuf_index = 0;
+ } else if (len > l->wbuf_length - l->wbuf_valid_length - l->wbuf_index) {
+ memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
+ l->wbuf_index = 0;
+ }
+
+ memcpy(l->wbuf+l->wbuf_index+l->wbuf_valid_length, c, len);
+ l->wbuf_valid_length += len;
+
+ do_write(l);
+}
+
+void pa_ioline_set_callback(struct pa_ioline*l, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata) {
+ assert(l && callback);
+ l->callback = callback;
+ l->userdata = userdata;
+}
+
+static int do_read(struct pa_ioline *l) {
+ ssize_t r;
+ size_t m, len;
+ char *e;
+ assert(l);
+
+ if (!pa_iochannel_is_readable(l->io))
+ return 0;
+
+ len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
+
+ 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) {
+ if (l->rbuf_valid_length)
+ memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
+ } else {
+ char *new = malloc(n);
+ if (l->rbuf_valid_length)
+ memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
+ free(l->rbuf);
+ l->rbuf = new;
+ l->rbuf_length = n;
+ }
+
+ l->rbuf_index = 0;
+ }
+
+ len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
+
+ if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0)
+ return -1;
+
+ e = memchr(l->rbuf+l->rbuf_index+l->rbuf_valid_length, '\n', r);
+ l->rbuf_valid_length += r;
+
+ if (!e &&l->rbuf_valid_length >= BUFFER_LIMIT)
+ e = l->rbuf+BUFFER_LIMIT-1;
+
+ if (e) {
+ char *p;
+
+ *e = 0;
+
+ p = l->rbuf+l->rbuf_index;
+ m = strlen(p);
+
+ l->rbuf_index += m+1;
+ l->rbuf_valid_length -= m+1;
+
+ if (l->rbuf_valid_length == 0)
+ l->rbuf_index = 0;
+
+ if (l->callback)
+ l->callback(l, p, l->userdata);
+ }
+
+ return 0;
+}
+
+static int do_write(struct pa_ioline *l) {
+ ssize_t r;
+ assert(l);
+
+ if (!l->wbuf_valid_length || !pa_iochannel_is_writable(l->io))
+ return 0;
+
+ if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0)
+ return -1;
+
+ l->wbuf_valid_length -= r;
+ if (l->wbuf_valid_length == 0)
+ l->wbuf_index = 0;
+
+ return 0;
+}
+
+static void io_callback(struct pa_iochannel*io, void *userdata) {
+ struct pa_ioline *l = userdata;
+ assert(io && l);
+
+ if (!l->dead && do_write(l) < 0)
+ goto fail;
+
+ if (!l->dead && do_read(l) < 0)
+ goto fail;
+
+ return;
+
+fail:
+ l->dead = 1;
+ if (l->callback)
+ l->callback(l, NULL, l->userdata);
+}
diff --git a/polyp/ioline.h b/polyp/ioline.h
new file mode 100644
index 00000000..5f29a16b
--- /dev/null
+++ b/polyp/ioline.h
@@ -0,0 +1,35 @@
+#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 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 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"
+
+struct pa_ioline;
+
+struct pa_ioline* pa_ioline_new(struct pa_iochannel *io);
+void pa_ioline_free(struct pa_ioline *l);
+
+void pa_ioline_puts(struct pa_ioline *s, const char *c);
+void pa_ioline_set_callback(struct pa_ioline*io, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata);
+
+#endif
diff --git a/polyp/main.c b/polyp/main.c
new file mode 100644
index 00000000..d9967cef
--- /dev/null
+++ b/polyp/main.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 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 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 <memblock.h>
+
+#include "core.h"
+#include "mainloop.h"
+#include "module.h"
+#include "mainloop-signal.h"
+#include "cmdline.h"
+#include "cli-command.h"
+#include "util.h"
+#include "sioman.h"
+
+static struct pa_mainloop *mainloop;
+
+static void exit_signal_callback(void *id, int sig, void *userdata) {
+ struct pa_mainloop_api* m = pa_mainloop_get_api(mainloop);
+ m->quit(m, 1);
+ fprintf(stderr, __FILE__": got signal.\n");
+}
+
+static void aux_signal_callback(void *id, int sig, void *userdata) {
+ struct pa_core *c = userdata;
+ assert(c);
+ pa_module_load(c, sig == SIGUSR1 ? "module-cli" : "module-cli-protocol-unix", NULL);
+}
+
+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[]) {
+ struct pa_core *c;
+ struct pa_cmdline *cmdline = NULL;
+ struct pa_strbuf *buf = NULL;
+ char *s;
+ int r, retval = 1;
+ int daemon_pipe[2] = { -1, -1 };
+
+ if (!(cmdline = pa_cmdline_parse(argc, argv))) {
+ fprintf(stderr, __FILE__": failed to parse command line.\n");
+ goto finish;
+ }
+
+ if (cmdline->help) {
+ pa_cmdline_help(argv[0]);
+ retval = 0;
+ goto finish;
+ }
+
+ if (cmdline->daemonize) {
+ pid_t child;
+
+ if (pa_stdio_acquire() < 0) {
+ fprintf(stderr, __FILE__": failed to acquire stdio.\n");
+ goto finish;
+ }
+
+ if (pipe(daemon_pipe) < 0) {
+ fprintf(stderr, __FILE__": failed to create pipe.\n");
+ goto finish;
+ }
+
+ if ((child = fork()) < 0) {
+ fprintf(stderr, __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)) {
+ fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno));
+ retval = 1;
+ }
+
+ goto finish;
+ }
+
+ close(daemon_pipe[0]);
+ daemon_pipe[0] = -1;
+
+ setsid();
+ setpgrp();
+ }
+
+ r = lt_dlinit();
+ assert(r == 0);
+
+ mainloop = pa_mainloop_new();
+ assert(mainloop);
+
+ r = pa_signal_init(pa_mainloop_get_api(mainloop));
+ assert(r == 0);
+ pa_signal_register(SIGINT, exit_signal_callback, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ c = pa_core_new(pa_mainloop_get_api(mainloop));
+ assert(c);
+
+ pa_signal_register(SIGUSR1, aux_signal_callback, c);
+ pa_signal_register(SIGUSR2, aux_signal_callback, c);
+
+ buf = pa_strbuf_new();
+ assert(buf);
+ r = pa_cli_command_execute(c, cmdline->cli_commands, buf, &cmdline->fail, &cmdline->verbose);
+ fprintf(stderr, s = pa_strbuf_tostring_free(buf));
+ free(s);
+
+ if (r < 0 && cmdline->fail) {
+ fprintf(stderr, __FILE__": failed to initialize daemon.\n");
+ if (cmdline->daemonize)
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
+ } else if (!c->modules || pa_idxset_ncontents(c->modules) == 0) {
+ fprintf(stderr, __FILE__": daemon startup without any loaded modules, refusing to work.\n");
+ if (cmdline->daemonize)
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
+ } else {
+ retval = 0;
+ if (cmdline->daemonize)
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
+ fprintf(stderr, __FILE__": mainloop entry.\n");
+ if (pa_mainloop_run(mainloop, &retval) < 0)
+ retval = 1;
+ fprintf(stderr, __FILE__": mainloop exit.\n");
+ }
+
+ pa_core_free(c);
+
+ pa_signal_done();
+ pa_mainloop_free(mainloop);
+
+ lt_dlexit();
+
+finish:
+
+ if (cmdline)
+ pa_cmdline_free(cmdline);
+
+ close_pipe(daemon_pipe);
+
+ return retval;
+}
diff --git a/polyp/mainloop-api.c b/polyp/mainloop-api.c
new file mode 100644
index 00000000..cce49c06
--- /dev/null
+++ b/polyp/mainloop-api.c
@@ -0,0 +1,60 @@
+/* $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 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 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"
+
+struct once_info {
+ void (*callback)(void *userdata);
+ void *userdata;
+};
+
+static void once_callback(struct pa_mainloop_api *api, void *id, void *userdata) {
+ struct once_info *i = userdata;
+ assert(api && i && i->callback);
+ i->callback(i->userdata);
+ assert(api->cancel_fixed);
+ api->cancel_fixed(api, id);
+ free(i);
+}
+
+void pa_mainloop_api_once(struct pa_mainloop_api* api, void (*callback)(void *userdata), void *userdata) {
+ struct once_info *i;
+ void *id;
+ assert(api && callback);
+
+ i = malloc(sizeof(struct once_info));
+ assert(i);
+ i->callback = callback;
+ i->userdata = userdata;
+
+ assert(api->source_fixed);
+ id = api->source_fixed(api, once_callback, i);
+ assert(id);
+
+ /* Note: if the mainloop is destroyed before once_callback() was called, some memory is leaked. */
+}
+
diff --git a/polyp/mainloop-api.h b/polyp/mainloop-api.h
new file mode 100644
index 00000000..0228f580
--- /dev/null
+++ b/polyp/mainloop-api.h
@@ -0,0 +1,65 @@
+#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 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 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 <time.h>
+#include <sys/time.h>
+
+enum pa_mainloop_api_io_events {
+ PA_MAINLOOP_API_IO_EVENT_NULL = 0,
+ PA_MAINLOOP_API_IO_EVENT_INPUT = 1,
+ PA_MAINLOOP_API_IO_EVENT_OUTPUT = 2,
+ PA_MAINLOOP_API_IO_EVENT_BOTH = 3,
+ PA_MAINLOOP_API_IO_EVENT_HUP = 4
+};
+
+struct pa_mainloop_api {
+ void *userdata;
+
+ /* IO sources */
+ void* (*source_io)(struct pa_mainloop_api*a, int fd, enum pa_mainloop_api_io_events events, void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata);
+ void (*enable_io)(struct pa_mainloop_api*a, void* id, enum pa_mainloop_api_io_events events);
+ void (*cancel_io)(struct pa_mainloop_api*a, void* id);
+
+ /* Fixed sources */
+ void* (*source_fixed)(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata);
+ void (*enable_fixed)(struct pa_mainloop_api*a, void* id, int b);
+ void (*cancel_fixed)(struct pa_mainloop_api*a, void* id);
+
+ /* Idle sources */
+ void* (*source_idle)(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata);
+ void (*enable_idle)(struct pa_mainloop_api*a, void* id, int b);
+ void (*cancel_idle)(struct pa_mainloop_api*a, void* id);
+
+ /* Time sources */
+ void* (*source_time)(struct pa_mainloop_api*a, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*a, void *id, const struct timeval *tv, void *userdata), void *userdata);
+ void (*enable_time)(struct pa_mainloop_api*a, void *id, const struct timeval *tv);
+ void (*cancel_time)(struct pa_mainloop_api*a, void* id);
+
+ /* Exit mainloop */
+ void (*quit)(struct pa_mainloop_api*a, int retval);
+};
+
+void pa_mainloop_api_once(struct pa_mainloop_api*m, void (*callback)(void *userdata), void *userdata);
+
+#endif
diff --git a/polyp/mainloop-signal.c b/polyp/mainloop-signal.c
new file mode 100644
index 00000000..642ca5e0
--- /dev/null
+++ b/polyp/mainloop-signal.c
@@ -0,0 +1,163 @@
+/* $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 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 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 "mainloop-signal.h"
+#include "util.h"
+
+struct signal_info {
+ int sig;
+ struct sigaction saved_sigaction;
+ void (*callback) (void *id, int signal, void *userdata);
+ void *userdata;
+ struct signal_info *previous, *next;
+};
+
+static struct pa_mainloop_api *api = NULL;
+static int signal_pipe[2] = { -1, -1 };
+static void* mainloop_source = NULL;
+static struct signal_info *signals = NULL;
+
+static void signal_handler(int sig) {
+ write(signal_pipe[1], &sig, sizeof(sig));
+}
+
+static void callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
+ assert(a && id && events == PA_MAINLOOP_API_IO_EVENT_INPUT && id == mainloop_source && fd == signal_pipe[0]);
+
+ for (;;) {
+ ssize_t r;
+ int sig;
+ struct signal_info*s;
+
+ if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) {
+ if (errno == EAGAIN)
+ return;
+
+ fprintf(stderr, "signal.c: read(): %s\n", strerror(errno));
+ return;
+ }
+
+ if (r != sizeof(sig)) {
+ fprintf(stderr, "signal.c: short read()\n");
+ return;
+ }
+
+ for (s = signals; s; s = s->next)
+ if (s->sig == sig) {
+ assert(s->callback);
+ s->callback(s, sig, s->userdata);
+ break;
+ }
+ }
+}
+
+int pa_signal_init(struct pa_mainloop_api *a) {
+ assert(a);
+ if (pipe(signal_pipe) < 0) {
+ fprintf(stderr, "pipe() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ pa_make_nonblock_fd(signal_pipe[0]);
+ pa_make_nonblock_fd(signal_pipe[1]);
+
+ api = a;
+ mainloop_source = api->source_io(api, signal_pipe[0], PA_MAINLOOP_API_IO_EVENT_INPUT, callback, NULL);
+ assert(mainloop_source);
+ return 0;
+}
+
+void pa_signal_done(void) {
+ assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && mainloop_source);
+
+ api->cancel_io(api, mainloop_source);
+ mainloop_source = NULL;
+
+ close(signal_pipe[0]);
+ close(signal_pipe[1]);
+ signal_pipe[0] = signal_pipe[1] = -1;
+
+ while (signals)
+ pa_signal_unregister(signals);
+
+ api = NULL;
+}
+
+void* pa_signal_register(int sig, void (*callback) (void *id, int signal, void *userdata), void *userdata) {
+ struct signal_info *s = NULL;
+ struct sigaction sa;
+ assert(sig > 0 && callback);
+
+ for (s = signals; s; s = s->next)
+ if (s->sig == sig)
+ goto fail;
+
+ s = malloc(sizeof(struct signal_info));
+ assert(s);
+ s->sig = sig;
+ s->callback = callback;
+ s->userdata = userdata;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(sig, &sa, &s->saved_sigaction) < 0)
+ goto fail;
+
+ s->previous = NULL;
+ s->next = signals;
+ signals = s;
+
+ return s;
+fail:
+ if (s)
+ free(s);
+ return NULL;
+}
+
+void pa_signal_unregister(void *id) {
+ struct signal_info *s = id;
+ assert(s);
+
+ if (s->next)
+ s->next->previous = s->previous;
+ if (s->previous)
+ s->previous->next = s->next;
+ else
+ signals = s->next;
+
+ sigaction(s->sig, &s->saved_sigaction, NULL);
+ free(s);
+}
diff --git a/polyp/mainloop-signal.h b/polyp/mainloop-signal.h
new file mode 100644
index 00000000..8afe9c8d
--- /dev/null
+++ b/polyp/mainloop-signal.h
@@ -0,0 +1,33 @@
+#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 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 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 "mainloop-api.h"
+
+int pa_signal_init(struct pa_mainloop_api *api);
+void pa_signal_done(void);
+
+void* pa_signal_register(int signal, void (*callback) (void *id, int signal, void *userdata), void *userdata);
+void pa_signal_unregister(void *id);
+
+#endif
diff --git a/polyp/mainloop.c b/polyp/mainloop.c
new file mode 100644
index 00000000..b9eee86d
--- /dev/null
+++ b/polyp/mainloop.c
@@ -0,0 +1,553 @@
+/* $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 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 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 <sys/poll.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "mainloop.h"
+#include "util.h"
+#include "idxset.h"
+
+struct mainloop_source_header {
+ struct pa_mainloop *mainloop;
+ int dead;
+};
+
+struct mainloop_source_io {
+ struct mainloop_source_header header;
+
+ int fd;
+ enum pa_mainloop_api_io_events events;
+ void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata);
+ void *userdata;
+
+ struct pollfd *pollfd;
+};
+
+struct mainloop_source_fixed_or_idle {
+ struct mainloop_source_header header;
+ int enabled;
+
+ void (*callback)(struct pa_mainloop_api*a, void *id, void *userdata);
+ void *userdata;
+};
+
+struct mainloop_source_time {
+ struct mainloop_source_header header;
+ int enabled;
+
+ struct timeval timeval;
+ void (*callback)(struct pa_mainloop_api*a, void *id, const struct timeval*tv, void *userdata);
+ void *userdata;
+};
+
+struct pa_mainloop {
+ struct pa_idxset *io_sources, *fixed_sources, *idle_sources, *time_sources;
+ int io_sources_scan_dead, fixed_sources_scan_dead, idle_sources_scan_dead, time_sources_scan_dead;
+
+ struct pollfd *pollfds;
+ unsigned max_pollfds, n_pollfds;
+ int rebuild_pollfds;
+
+ int quit, running, retval;
+ struct pa_mainloop_api api;
+};
+
+static void setup_api(struct pa_mainloop *m);
+
+struct pa_mainloop *pa_mainloop_new(void) {
+ struct pa_mainloop *m;
+
+ m = malloc(sizeof(struct pa_mainloop));
+ assert(m);
+
+ m->io_sources = pa_idxset_new(NULL, NULL);
+ m->fixed_sources = pa_idxset_new(NULL, NULL);
+ m->idle_sources = pa_idxset_new(NULL, NULL);
+ m->time_sources = pa_idxset_new(NULL, NULL);
+
+ assert(m->io_sources && m->fixed_sources && m->idle_sources && m->time_sources);
+
+ m->io_sources_scan_dead = m->fixed_sources_scan_dead = m->idle_sources_scan_dead = m->time_sources_scan_dead = 0;
+
+ m->pollfds = NULL;
+ m->max_pollfds = m->n_pollfds = m->rebuild_pollfds = 0;
+
+ m->quit = m->running = m->retval = 0;
+
+ setup_api(m);
+
+ return m;
+}
+
+static int foreach(void *p, uint32_t index, int *del, void*userdata) {
+ struct mainloop_source_header *h = p;
+ int *all = userdata;
+ assert(p && del && all);
+
+ if (*all || h->dead) {
+ free(h);
+ *del = 1;
+ }
+
+ return 0;
+};
+
+void pa_mainloop_free(struct pa_mainloop* m) {
+ int all = 1;
+ assert(m);
+ pa_idxset_foreach(m->io_sources, foreach, &all);
+ pa_idxset_foreach(m->fixed_sources, foreach, &all);
+ pa_idxset_foreach(m->idle_sources, foreach, &all);
+ pa_idxset_foreach(m->time_sources, foreach, &all);
+
+ pa_idxset_free(m->io_sources, NULL, NULL);
+ pa_idxset_free(m->fixed_sources, NULL, NULL);
+ pa_idxset_free(m->idle_sources, NULL, NULL);
+ pa_idxset_free(m->time_sources, NULL, NULL);
+
+ free(m->pollfds);
+ free(m);
+}
+
+static void scan_dead(struct pa_mainloop *m) {
+ int all = 0;
+ assert(m);
+ if (m->io_sources_scan_dead)
+ pa_idxset_foreach(m->io_sources, foreach, &all);
+ if (m->fixed_sources_scan_dead)
+ pa_idxset_foreach(m->fixed_sources, foreach, &all);
+ if (m->idle_sources_scan_dead)
+ pa_idxset_foreach(m->idle_sources, foreach, &all);
+ if (m->time_sources_scan_dead)
+ pa_idxset_foreach(m->time_sources, foreach, &all);
+}
+
+static void rebuild_pollfds(struct pa_mainloop *m) {
+ struct mainloop_source_io*s;
+ struct pollfd *p;
+ uint32_t index = PA_IDXSET_INVALID;
+ unsigned l;
+
+ l = pa_idxset_ncontents(m->io_sources);
+ if (m->max_pollfds < l) {
+ m->pollfds = realloc(m->pollfds, sizeof(struct pollfd)*l);
+ m->max_pollfds = l;
+ }
+
+ m->n_pollfds = 0;
+ p = m->pollfds;
+ for (s = pa_idxset_first(m->io_sources, &index); s; s = pa_idxset_next(m->io_sources, &index)) {
+ if (s->header.dead) {
+ s->pollfd = NULL;
+ continue;
+ }
+
+ s->pollfd = p;
+ p->fd = s->fd;
+ p->events = ((s->events & PA_MAINLOOP_API_IO_EVENT_INPUT) ? POLLIN : 0) | ((s->events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) ? POLLOUT : 0);
+ p->revents = 0;
+
+ p++;
+ m->n_pollfds++;
+ }
+}
+
+static void dispatch_pollfds(struct pa_mainloop *m) {
+ uint32_t index = PA_IDXSET_INVALID;
+ struct mainloop_source_io *s;
+
+ for (s = pa_idxset_first(m->io_sources, &index); s; s = pa_idxset_next(m->io_sources, &index)) {
+ if (s->header.dead || !s->pollfd || !s->pollfd->revents)
+ continue;
+
+ assert(s->pollfd->fd == s->fd && s->callback);
+ s->callback(&m->api, s, s->fd,
+ ((s->pollfd->revents & POLLHUP) ? PA_MAINLOOP_API_IO_EVENT_HUP : 0) |
+ ((s->pollfd->revents & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) |
+ ((s->pollfd->revents & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), s->userdata);
+ s->pollfd->revents = 0;
+ }
+}
+
+static void run_fixed_or_idle(struct pa_mainloop *m, struct pa_idxset *i) {
+ uint32_t index = PA_IDXSET_INVALID;
+ struct mainloop_source_fixed_or_idle *s;
+
+ for (s = pa_idxset_first(i, &index); s; s = pa_idxset_next(i, &index)) {
+ if (s->header.dead || !s->enabled)
+ continue;
+
+ assert(s->callback);
+ s->callback(&m->api, s, s->userdata);
+ }
+}
+
+static int calc_next_timeout(struct pa_mainloop *m) {
+ uint32_t index = PA_IDXSET_INVALID;
+ struct mainloop_source_time *s;
+ struct timeval now;
+ int t = -1;
+
+ if (pa_idxset_isempty(m->time_sources))
+ return -1;
+
+ gettimeofday(&now, NULL);
+
+ for (s = pa_idxset_first(m->time_sources, &index); s; s = pa_idxset_next(m->time_sources, &index)) {
+ int tmp;
+
+ if (s->header.dead || !s->enabled)
+ continue;
+
+ if (s->timeval.tv_sec < now.tv_sec || (s->timeval.tv_sec == now.tv_sec && s->timeval.tv_usec <= now.tv_usec))
+ return 0;
+
+ tmp = (s->timeval.tv_sec - now.tv_sec)*1000;
+
+ if (s->timeval.tv_usec > now.tv_usec)
+ tmp += (s->timeval.tv_usec - now.tv_usec)/1000;
+ else
+ tmp -= (now.tv_usec - s->timeval.tv_usec)/1000;
+
+ if (tmp == 0)
+ return 0;
+ else if (t == -1 || tmp < t)
+ t = tmp;
+ }
+
+ return t;
+}
+
+static void dispatch_timeout(struct pa_mainloop *m) {
+ uint32_t index = PA_IDXSET_INVALID;
+ struct mainloop_source_time *s;
+ struct timeval now;
+ assert(m);
+
+ if (pa_idxset_isempty(m->time_sources))
+ return;
+
+ gettimeofday(&now, NULL);
+ for (s = pa_idxset_first(m->time_sources, &index); s; s = pa_idxset_next(m->time_sources, &index)) {
+
+ if (s->header.dead || !s->enabled)
+ continue;
+
+ if (s->timeval.tv_sec < now.tv_sec || (s->timeval.tv_sec == now.tv_sec && s->timeval.tv_usec <= now.tv_usec)) {
+ assert(s->callback);
+
+ s->enabled = 0;
+ s->callback(&m->api, s, &s->timeval, s->userdata);
+ }
+ }
+}
+
+static int any_idle_sources(struct pa_mainloop *m) {
+ struct mainloop_source_fixed_or_idle *s;
+ uint32_t index;
+ assert(m);
+
+ for (s = pa_idxset_first(m->idle_sources, &index); s; s = pa_idxset_next(m->idle_sources, &index))
+ if (!s->header.dead && s->enabled)
+ return 1;
+
+ return 0;
+}
+
+int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval) {
+ int r, idle;
+ assert(m && !m->running);
+
+ if(m->quit) {
+ if (retval)
+ *retval = m->retval;
+ return 1;
+ }
+
+ m->running = 1;
+
+ scan_dead(m);
+ run_fixed_or_idle(m, m->fixed_sources);
+
+ if (m->rebuild_pollfds) {
+ rebuild_pollfds(m);
+ m->rebuild_pollfds = 0;
+ }
+
+ idle = any_idle_sources(m);
+
+ do {
+ int t;
+
+ if (!block || idle)
+ t = 0;
+ else
+ t = calc_next_timeout(m);
+
+ r = poll(m->pollfds, m->n_pollfds, t);
+ } while (r < 0 && errno == EINTR);
+
+ dispatch_timeout(m);
+
+ if (r > 0)
+ dispatch_pollfds(m);
+ else if (r == 0 && idle)
+ run_fixed_or_idle(m, m->idle_sources);
+ else if (r < 0)
+ fprintf(stderr, "select(): %s\n", strerror(errno));
+
+ m->running = 0;
+ return r < 0 ? -1 : 0;
+}
+
+int pa_mainloop_run(struct pa_mainloop *m, int *retval) {
+ int r;
+ while ((r = pa_mainloop_iterate(m, 1, retval)) == 0);
+ return r;
+}
+
+void pa_mainloop_quit(struct pa_mainloop *m, int r) {
+ assert(m);
+ m->quit = r;
+}
+
+/* IO sources */
+static void* mainloop_source_io(struct pa_mainloop_api*a, int fd, enum pa_mainloop_api_io_events events, void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata) {
+ struct pa_mainloop *m;
+ struct mainloop_source_io *s;
+ assert(a && a->userdata && fd >= 0 && callback);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ s = malloc(sizeof(struct mainloop_source_io));
+ assert(s);
+ s->header.mainloop = m;
+ s->header.dead = 0;
+
+ s->fd = fd;
+ s->events = events;
+ s->callback = callback;
+ s->userdata = userdata;
+ s->pollfd = NULL;
+
+ pa_idxset_put(m->io_sources, s, NULL);
+ m->rebuild_pollfds = 1;
+ return s;
+}
+
+static void mainloop_enable_io(struct pa_mainloop_api*a, void* id, enum pa_mainloop_api_io_events events) {
+ struct pa_mainloop *m;
+ struct mainloop_source_io *s = id;
+ assert(a && a->userdata && s && !s->header.dead);
+ m = a->userdata;
+ assert(a == &m->api && s->header.mainloop == m);
+
+ s->events = events;
+ if (s->pollfd)
+ s->pollfd->events = ((s->events & PA_MAINLOOP_API_IO_EVENT_INPUT) ? POLLIN : 0) | ((s->events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) ? POLLOUT : 0);
+}
+
+static void mainloop_cancel_io(struct pa_mainloop_api*a, void* id) {
+ struct pa_mainloop *m;
+ struct mainloop_source_io *s = id;
+ assert(a && a->userdata && s && !s->header.dead);
+ m = a->userdata;
+ assert(a == &m->api && s->header.mainloop == m);
+
+ s->header.dead = 1;
+ m->io_sources_scan_dead = 1;
+ m->rebuild_pollfds = 1;
+}
+
+/* Fixed sources */
+static void* mainloop_source_fixed(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata) {
+ struct pa_mainloop *m;
+ struct mainloop_source_fixed_or_idle *s;
+ assert(a && a->userdata && callback);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ s = malloc(sizeof(struct mainloop_source_fixed_or_idle));
+ assert(s);
+ s->header.mainloop = m;
+ s->header.dead = 0;
+
+ s->enabled = 1;
+ s->callback = callback;
+ s->userdata = userdata;
+
+ pa_idxset_put(m->fixed_sources, s, NULL);
+ return s;
+}
+
+static void mainloop_enable_fixed(struct pa_mainloop_api*a, void* id, int b) {
+ struct pa_mainloop *m;
+ struct mainloop_source_fixed_or_idle *s = id;
+ assert(a && a->userdata && s && !s->header.dead);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ s->enabled = b;
+}
+
+static void mainloop_cancel_fixed(struct pa_mainloop_api*a, void* id) {
+ struct pa_mainloop *m;
+ struct mainloop_source_fixed_or_idle *s = id;
+ assert(a && a->userdata && s && !s->header.dead);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ s->header.dead = 1;
+ m->fixed_sources_scan_dead = 1;
+}
+
+/* Idle sources */
+static void* mainloop_source_idle(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata) {
+ struct pa_mainloop *m;
+ struct mainloop_source_fixed_or_idle *s;
+ assert(a && a->userdata && callback);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ s = malloc(sizeof(struct mainloop_source_fixed_or_idle));
+ assert(s);
+ s->header.mainloop = m;
+ s->header.dead = 0;
+
+ s->enabled = 1;
+ s->callback = callback;
+ s->userdata = userdata;
+
+ pa_idxset_put(m->idle_sources, s, NULL);
+ return s;
+}
+
+static void mainloop_cancel_idle(struct pa_mainloop_api*a, void* id) {
+ struct pa_mainloop *m;
+ struct mainloop_source_fixed_or_idle *s = id;
+ assert(a && a->userdata && s && !s->header.dead);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ s->header.dead = 1;
+ m->idle_sources_scan_dead = 1;
+}
+
+/* Time sources */
+static void* mainloop_source_time(struct pa_mainloop_api*a, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*a, void *id, const struct timeval *tv, void *userdata), void *userdata) {
+ struct pa_mainloop *m;
+ struct mainloop_source_time *s;
+ assert(a && a->userdata && callback);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ s = malloc(sizeof(struct mainloop_source_time));
+ assert(s);
+ s->header.mainloop = m;
+ s->header.dead = 0;
+
+ s->enabled = !!tv;
+ if (tv)
+ s->timeval = *tv;
+
+ s->callback = callback;
+ s->userdata = userdata;
+
+ pa_idxset_put(m->time_sources, s, NULL);
+ return s;
+}
+
+static void mainloop_enable_time(struct pa_mainloop_api*a, void *id, const struct timeval *tv) {
+ struct pa_mainloop *m;
+ struct mainloop_source_time *s = id;
+ assert(a && a->userdata && s && !s->header.dead);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ if (tv) {
+ s->enabled = 1;
+ s->timeval = *tv;
+ } else
+ s->enabled = 0;
+}
+
+static void mainloop_cancel_time(struct pa_mainloop_api*a, void* id) {
+ struct pa_mainloop *m;
+ struct mainloop_source_time *s = id;
+ assert(a && a->userdata && s && !s->header.dead);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ s->header.dead = 1;
+ m->time_sources_scan_dead = 1;
+
+}
+
+static void mainloop_quit(struct pa_mainloop_api*a, int retval) {
+ struct pa_mainloop *m;
+ assert(a && a->userdata);
+ m = a->userdata;
+ assert(a == &m->api);
+
+ m->quit = 1;
+ m->retval = retval;
+}
+
+static void setup_api(struct pa_mainloop *m) {
+ assert(m);
+
+ m->api.userdata = m;
+ m->api.source_io = mainloop_source_io;
+ m->api.enable_io = mainloop_enable_io;
+ m->api.cancel_io = mainloop_cancel_io;
+
+ m->api.source_fixed = mainloop_source_fixed;
+ m->api.enable_fixed = mainloop_enable_fixed;
+ m->api.cancel_fixed = mainloop_cancel_fixed;
+
+ m->api.source_idle = mainloop_source_idle;
+ m->api.enable_idle = mainloop_enable_fixed; /* (!) */
+ m->api.cancel_idle = mainloop_cancel_idle;
+
+ m->api.source_time = mainloop_source_time;
+ m->api.enable_time = mainloop_enable_time;
+ m->api.cancel_time = mainloop_cancel_time;
+
+ m->api.quit = mainloop_quit;
+}
+
+struct pa_mainloop_api* pa_mainloop_get_api(struct pa_mainloop*m) {
+ assert(m);
+ return &m->api;
+}
+
diff --git a/polyp/mainloop.h b/polyp/mainloop.h
new file mode 100644
index 00000000..58448c3e
--- /dev/null
+++ b/polyp/mainloop.h
@@ -0,0 +1,37 @@
+#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 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 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 "mainloop-api.h"
+
+struct pa_mainloop;
+
+struct pa_mainloop *pa_mainloop_new(void);
+void pa_mainloop_free(struct pa_mainloop* m);
+
+int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval);
+int pa_mainloop_run(struct pa_mainloop *m, int *retval);
+
+struct pa_mainloop_api* pa_mainloop_get_api(struct pa_mainloop*m);
+
+#endif
diff --git a/polyp/memblock.c b/polyp/memblock.c
new file mode 100644
index 00000000..8f24ff22
--- /dev/null
+++ b/polyp/memblock.c
@@ -0,0 +1,113 @@
+/* $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 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 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"
+
+static unsigned memblock_count = 0, memblock_total = 0;
+
+struct pa_memblock *pa_memblock_new(size_t length) {
+ struct pa_memblock *b = malloc(sizeof(struct pa_memblock)+length);
+ b->type = PA_MEMBLOCK_APPENDED;
+ b->ref = 1;
+ b->length = length;
+ b->data = b+1;
+ memblock_count++;
+ memblock_total += length;
+ return b;
+}
+
+struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length) {
+ struct pa_memblock *b = malloc(sizeof(struct pa_memblock));
+ b->type = PA_MEMBLOCK_FIXED;
+ b->ref = 1;
+ b->length = length;
+ b->data = d;
+ memblock_count++;
+ memblock_total += length;
+ return b;
+}
+
+struct pa_memblock *pa_memblock_new_dynamic(void *d, size_t length) {
+ struct pa_memblock *b = malloc(sizeof(struct pa_memblock));
+ b->type = PA_MEMBLOCK_DYNAMIC;
+ b->ref = 1;
+ b->length = length;
+ b->data = d;
+ memblock_count++;
+ memblock_total += length;
+ return b;
+}
+
+struct pa_memblock* pa_memblock_ref(struct pa_memblock*b) {
+ assert(b && b->ref >= 1);
+ b->ref++;
+ return b;
+}
+
+void pa_memblock_unref(struct pa_memblock*b) {
+ assert(b && b->ref >= 1);
+ b->ref--;
+
+ if (b->ref == 0) {
+ if (b->type == PA_MEMBLOCK_DYNAMIC)
+ free(b->data);
+
+ memblock_count--;
+ memblock_total -= b->length;
+
+ free(b);
+ }
+}
+
+void pa_memblock_unref_fixed(struct pa_memblock *b) {
+ void *d;
+
+ assert(b && b->ref >= 1);
+
+ if (b->ref == 1) {
+ pa_memblock_unref(b);
+ return;
+ } else {
+ d = malloc(b->length);
+ assert(d);
+ memcpy(d, b->data, b->length);
+ b->data = d;
+ b->type = PA_MEMBLOCK_DYNAMIC;
+ b->ref--;
+ }
+}
+
+unsigned pa_memblock_get_count(void) {
+ return memblock_count;
+}
+
+unsigned pa_memblock_get_total(void) {
+ return memblock_total;
+}
diff --git a/polyp/memblock.h b/polyp/memblock.h
new file mode 100644
index 00000000..4bb02977
--- /dev/null
+++ b/polyp/memblock.h
@@ -0,0 +1,49 @@
+#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 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 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>
+
+enum pa_memblock_type { PA_MEMBLOCK_FIXED, PA_MEMBLOCK_APPENDED, PA_MEMBLOCK_DYNAMIC };
+
+struct pa_memblock {
+ enum pa_memblock_type type;
+ unsigned ref;
+ size_t length;
+ void *data;
+};
+
+struct pa_memblock *pa_memblock_new(size_t length);
+struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length);
+struct pa_memblock *pa_memblock_new_dynamic(void *data, size_t length);
+
+void pa_memblock_unref(struct pa_memblock*b);
+struct pa_memblock* pa_memblock_ref(struct pa_memblock*b);
+
+void pa_memblock_unref_fixed(struct pa_memblock*b);
+
+unsigned pa_memblock_get_count(void);
+unsigned pa_memblock_get_total(void);
+
+#endif
diff --git a/polyp/memblockq.c b/polyp/memblockq.c
new file mode 100644
index 00000000..eff923b9
--- /dev/null
+++ b/polyp/memblockq.c
@@ -0,0 +1,326 @@
+/* $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 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 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 "memblockq.h"
+
+struct memblock_list {
+ struct memblock_list *next;
+ struct pa_memchunk chunk;
+ struct timeval stamp;
+};
+
+struct pa_memblockq {
+ struct memblock_list *blocks, *blocks_tail;
+ unsigned n_blocks;
+ size_t current_length, maxlength, tlength, base, prebuf, minreq;
+ int measure_delay;
+ uint32_t delay;
+ struct pa_mcalign *mcalign;
+};
+
+struct pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq) {
+ struct pa_memblockq* bq;
+ assert(maxlength && base && maxlength);
+
+ bq = malloc(sizeof(struct pa_memblockq));
+ assert(bq);
+ bq->blocks = bq->blocks_tail = 0;
+ bq->n_blocks = 0;
+
+ bq->current_length = 0;
+
+ fprintf(stderr, "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 == 0 || bq->tlength >= bq->maxlength)
+ bq->tlength = bq->maxlength;
+
+ 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;
+
+ bq->minreq = (minreq/base)*base;
+ if (bq->minreq == 0)
+ bq->minreq = 1;
+
+ fprintf(stderr, "memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq);
+
+ bq->measure_delay = 0;
+ bq->delay = 0;
+
+ bq->mcalign = NULL;
+
+ return bq;
+}
+
+void pa_memblockq_free(struct pa_memblockq* bq) {
+ struct memblock_list *l;
+ assert(bq);
+
+ if (bq->mcalign)
+ pa_mcalign_free(bq->mcalign);
+
+ while ((l = bq->blocks)) {
+ bq->blocks = l->next;
+ pa_memblock_unref(l->chunk.memblock);
+ free(l);
+ }
+
+ free(bq);
+}
+
+void pa_memblockq_push(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) {
+ struct memblock_list *q;
+ assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0);
+
+ 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;
+
+ /* fprintf(stderr, __FILE__": merge succeeded: %u\n", chunk->length);*/
+ return;
+ }
+ }
+
+ q = malloc(sizeof(struct memblock_list));
+ assert(q);
+
+ if (bq->measure_delay)
+ gettimeofday(&q->stamp, NULL);
+ else
+ timerclear(&q->stamp);
+
+ q->chunk = *chunk;
+ pa_memblock_ref(q->chunk.memblock);
+ assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length);
+ q->next = NULL;
+
+ if (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(struct pa_memblockq* bq, struct 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);
+
+/* if (chunk->memblock->ref != 2) */
+/* fprintf(stderr, "block %p with ref %u peeked.\n", chunk->memblock, chunk->memblock->ref); */
+
+ return 0;
+}
+
+/*
+int memblockq_pop(struct memblockq* bq, struct pa_memchunk *chunk) {
+ struct memblock_list *q;
+
+ assert(bq && chunk);
+
+ if (!bq->blocks || bq->current_length < bq->prebuf)
+ return -1;
+
+ bq->prebuf = 0;
+
+ q = bq->blocks;
+ bq->blocks = bq->blocks->next;
+
+ *chunk = q->chunk;
+
+ bq->n_blocks--;
+ bq->current_length -= chunk->length;
+
+ free(q);
+ return 0;
+}
+*/
+
+static uint32_t age(struct timeval *tv) {
+ assert(tv);
+ struct timeval now;
+ uint32_t r;
+
+ if (tv->tv_sec == 0)
+ return 0;
+
+ gettimeofday(&now, NULL);
+
+ r = (now.tv_sec-tv->tv_sec) * 1000000;
+
+ if (now.tv_usec >= tv->tv_usec)
+ r += now.tv_usec - tv->tv_usec;
+ else
+ r -= tv->tv_usec - now.tv_usec;
+
+ return r;
+}
+
+void pa_memblockq_drop(struct 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;
+
+ if (bq->measure_delay)
+ bq->delay = age(&bq->blocks->stamp);
+
+ bq->blocks->chunk.index += l;
+ bq->blocks->chunk.length -= l;
+ bq->current_length -= l;
+
+ if (bq->blocks->chunk.length == 0) {
+ struct memblock_list *q;
+
+ q = bq->blocks;
+ bq->blocks = bq->blocks->next;
+ if (bq->blocks == NULL)
+ bq->blocks_tail = NULL;
+ pa_memblock_unref(q->chunk.memblock);
+ free(q);
+
+ bq->n_blocks--;
+ }
+
+ length -= l;
+ }
+}
+
+void pa_memblockq_shorten(struct pa_memblockq *bq, size_t length) {
+ size_t l;
+ assert(bq);
+
+ if (bq->current_length <= length)
+ return;
+
+ fprintf(stderr, "Warning! pa_memblockq_shorten()\n");
+
+ l = bq->current_length - length;
+ l /= bq->base;
+ l *= bq->base;
+
+ pa_memblockq_drop(bq, l);
+}
+
+
+void pa_memblockq_empty(struct pa_memblockq *bq) {
+ assert(bq);
+ pa_memblockq_shorten(bq, 0);
+}
+
+int pa_memblockq_is_readable(struct pa_memblockq *bq) {
+ assert(bq);
+
+ return bq->current_length && (bq->current_length >= bq->prebuf);
+}
+
+int pa_memblockq_is_writable(struct pa_memblockq *bq, size_t length) {
+ assert(bq);
+
+ return bq->current_length + length <= bq->tlength;
+}
+
+uint32_t pa_memblockq_get_delay(struct pa_memblockq *bq) {
+ assert(bq);
+ return bq->delay;
+}
+
+uint32_t pa_memblockq_get_length(struct pa_memblockq *bq) {
+ assert(bq);
+ return bq->current_length;
+}
+
+uint32_t pa_memblockq_missing(struct 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(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) {
+ struct 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);
+ 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(struct pa_memblockq *bq) {
+ assert(bq);
+ return bq->minreq;
+}
diff --git a/polyp/memblockq.h b/polyp/memblockq.h
new file mode 100644
index 00000000..e6ad01db
--- /dev/null
+++ b/polyp/memblockq.h
@@ -0,0 +1,82 @@
+#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 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 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"
+
+struct 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
+*/
+struct pa_memblockq* pa_memblockq_new(size_t maxlength,
+ size_t tlength,
+ size_t base,
+ size_t prebuf,
+ size_t minreq);
+void pa_memblockq_free(struct pa_memblockq*bq);
+
+/* Push a new memory chunk into the queue. Optionally specify a value for future cancellation. This is currently not implemented, however! */
+void pa_memblockq_push(struct pa_memblockq* bq, const struct 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(struct pa_memblockq* bq, const struct 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(struct pa_memblockq* bq, struct pa_memchunk *chunk);
+
+/* Drop the specified bytes from the queue */
+void pa_memblockq_drop(struct 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(struct pa_memblockq *bq, size_t length);
+
+/* Empty the pa_memblockq */
+void pa_memblockq_empty(struct pa_memblockq *bq);
+
+/* Test if the pa_memblockq is currently readable, that is, more data than base */
+int pa_memblockq_is_readable(struct pa_memblockq *bq);
+
+/* Test if the pa_memblockq is currently writable for the specified amount of bytes */
+int pa_memblockq_is_writable(struct pa_memblockq *bq, size_t length);
+
+/* The time memory chunks stay in the queue until they are removed completely in usecs */
+uint32_t pa_memblockq_get_delay(struct pa_memblockq *bq);
+
+/* Return the length of the queue in bytes */
+uint32_t pa_memblockq_get_length(struct pa_memblockq *bq);
+
+/* Return how many bytes are missing in queue to the specified fill amount */
+uint32_t pa_memblockq_missing(struct pa_memblockq *bq);
+
+
+uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq);
+
+#endif
diff --git a/polyp/memchunk.c b/polyp/memchunk.c
new file mode 100644
index 00000000..d27ca61a
--- /dev/null
+++ b/polyp/memchunk.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 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 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"
+
+void pa_memchunk_make_writable(struct pa_memchunk *c) {
+ struct pa_memblock *n;
+ assert(c && c->memblock && c->memblock->ref >= 1);
+
+ if (c->memblock->ref == 1)
+ return;
+
+ n = pa_memblock_new(c->length);
+ assert(n);
+ memcpy(n->data, c->memblock->data+c->index, c->length);
+ pa_memblock_unref(c->memblock);
+ c->memblock = n;
+ c->index = 0;
+}
+
+
+struct pa_mcalign {
+ size_t base;
+ struct pa_memchunk chunk;
+ uint8_t *buffer;
+ size_t buffer_fill;
+};
+
+struct pa_mcalign *pa_mcalign_new(size_t base) {
+ struct pa_mcalign *m;
+ assert(base);
+
+ m = malloc(sizeof(struct pa_mcalign));
+ assert(m);
+ m->base = base;
+ m->chunk.memblock = NULL;
+ m->chunk.length = m->chunk.index = 0;
+ m->buffer = NULL;
+ m->buffer_fill = 0;
+ return m;
+}
+
+void pa_mcalign_free(struct pa_mcalign *m) {
+ assert(m);
+
+ free(m->buffer);
+
+ if (m->chunk.memblock)
+ pa_memblock_unref(m->chunk.memblock);
+
+ free(m);
+}
+
+void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) {
+ assert(m && c && !m->chunk.memblock && c->memblock && c->length);
+
+ m->chunk = *c;
+ pa_memblock_ref(m->chunk.memblock);
+}
+
+int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) {
+ assert(m && c && m->base > m->buffer_fill);
+ int ret;
+
+ if (!m->chunk.memblock)
+ return -1;
+
+ if (m->buffer_fill) {
+ size_t l = m->base - m->buffer_fill;
+ if (l > m->chunk.length)
+ l = m->chunk.length;
+ assert(m->buffer && l);
+
+ memcpy(m->buffer + m->buffer_fill, m->chunk.memblock->data + m->chunk.index, l);
+ m->buffer_fill += l;
+ m->chunk.index += l;
+ m->chunk.length -= l;
+
+ if (m->chunk.length == 0) {
+ m->chunk.length = m->chunk.index = 0;
+ pa_memblock_unref(m->chunk.memblock);
+ m->chunk.memblock = NULL;
+ }
+
+ assert(m->buffer_fill <= m->base);
+ if (m->buffer_fill == m->base) {
+ c->memblock = pa_memblock_new_dynamic(m->buffer, m->base);
+ assert(c->memblock);
+ c->index = 0;
+ c->length = m->base;
+ m->buffer = NULL;
+ m->buffer_fill = 0;
+
+ return 0;
+ }
+
+ return -1;
+ }
+
+ m->buffer_fill = m->chunk.length % m->base;
+
+ if (m->buffer_fill) {
+ assert(!m->buffer);
+ m->buffer = malloc(m->base);
+ assert(m->buffer);
+ m->chunk.length -= m->buffer_fill;
+ memcpy(m->buffer, m->chunk.memblock->data + m->chunk.index + m->chunk.length, m->buffer_fill);
+ }
+
+ if (m->chunk.length) {
+ *c = m->chunk;
+ pa_memblock_ref(c->memblock);
+ ret = 0;
+ } else
+ ret = -1;
+
+ m->chunk.length = m->chunk.index = 0;
+ pa_memblock_unref(m->chunk.memblock);
+ m->chunk.memblock = NULL;
+
+ return ret;
+}
diff --git a/polyp/memchunk.h b/polyp/memchunk.h
new file mode 100644
index 00000000..341c145c
--- /dev/null
+++ b/polyp/memchunk.h
@@ -0,0 +1,41 @@
+#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 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 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"
+
+struct pa_memchunk {
+ struct pa_memblock *memblock;
+ size_t index, length;
+};
+
+void pa_memchunk_make_writable(struct pa_memchunk *c);
+
+struct pa_mcalign;
+
+struct pa_mcalign *pa_mcalign_new(size_t base);
+void pa_mcalign_free(struct pa_mcalign *m);
+void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c);
+int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c);
+
+#endif
diff --git a/polyp/modargs.c b/polyp/modargs.c
new file mode 100644
index 00000000..280a546d
--- /dev/null
+++ b/polyp/modargs.c
@@ -0,0 +1,288 @@
+/* $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 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 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"
+
+struct pa_modargs;
+
+struct entry {
+ char *key, *value;
+};
+
+static int add_key_value(struct 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) {
+ free(key);
+ free(value);
+ return -1;
+ }
+ }
+
+ e = malloc(sizeof(struct entry));
+ assert(e);
+ e->key = key;
+ e->value = value;
+ pa_hashmap_put(map, key, e);
+ return 0;
+}
+
+struct pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
+ struct 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, value_len;
+
+ 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, strndup(key, key_len), strdup(""), 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, strndup(key, key_len), strndup(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, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0)
+ goto fail;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
+ case VALUE_TICKS:
+ if (*p == '\'') {
+ if (add_key_value(map, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0)
+ goto fail;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
+ }
+ }
+
+ if (state == VALUE_START) {
+ if (add_key_value(map, strndup(key, key_len), strdup(""), valid_keys) < 0)
+ goto fail;
+ } else if (state == VALUE_SIMPLE) {
+ if (add_key_value(map, strndup(key, key_len), strdup(value), valid_keys) < 0)
+ goto fail;
+ } else if (state != WHITESPACE)
+ goto fail;
+ }
+
+ return (struct pa_modargs*) map;
+
+fail:
+
+ if (map)
+ pa_modargs_free((struct pa_modargs*) map);
+
+ return NULL;
+}
+
+
+static void free_func(void *p, void*userdata) {
+ struct entry *e = p;
+ assert(e);
+ free(e->key);
+ free(e->value);
+ free(e);
+}
+
+void pa_modargs_free(struct pa_modargs*ma) {
+ struct pa_hashmap *map = (struct pa_hashmap*) ma;
+ pa_hashmap_free(map, free_func, NULL);
+}
+
+const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def) {
+ struct pa_hashmap *map = (struct pa_hashmap*) ma;
+ struct entry*e;
+
+ if (!(e = pa_hashmap_get(map, key)))
+ return def;
+
+ return e->value;
+}
+
+int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value) {
+ const char *v;
+ char *e;
+ unsigned long l;
+ assert(ma && key && value);
+
+ if (!(v = pa_modargs_get_value(ma, key, NULL)))
+ return 0;
+
+ if (!*v)
+ return -1;
+
+ l = strtoul(v, &e, 0);
+ if (*e)
+ return -1;
+
+ *value = (uint32_t) l;
+ return 0;
+}
+
+int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *rss) {
+ const char *format;
+ uint32_t channels;
+ struct pa_sample_spec ss;
+ assert(ma && rss);
+
+ 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 (strcmp(format, "s16le") == 0)
+ ss.format = PA_SAMPLE_S16LE;
+ else if (strcmp(format, "s16be") == 0)
+ ss.format = PA_SAMPLE_S16BE;
+ else if (strcmp(format, "s16ne") == 0 || strcmp(format, "s16") == 0 || strcmp(format, "16") == 0)
+ ss.format = PA_SAMPLE_S16NE;
+ else if (strcmp(format, "u8") == 0 || strcmp(format, "8") == 0)
+ ss.format = PA_SAMPLE_U8;
+ else if (strcmp(format, "float32") == 0 || strcmp(format, "float32ne") == 0)
+ ss.format = PA_SAMPLE_FLOAT32;
+ else if (strcmp(format, "float32le") == 0)
+ ss.format = PA_SAMPLE_FLOAT32LE;
+ else if (strcmp(format, "float32be") == 0)
+ ss.format = PA_SAMPLE_FLOAT32BE;
+ else if (strcmp(format, "ulaw") == 0)
+ ss.format = PA_SAMPLE_ULAW;
+ else if (strcmp(format, "alaw") == 0)
+ ss.format = PA_SAMPLE_ALAW;
+ else
+ return -1;
+ }
+
+ if (!pa_sample_spec_valid(&ss))
+ return -1;
+
+ *rss = ss;
+
+ return 0;
+}
+
+int pa_modargs_get_source_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index) {
+ const char *t;
+ assert(ma && index);
+
+ if (!(t = pa_modargs_get_value(ma, "source", NULL)))
+ *index = PA_IDXSET_INVALID;
+ else {
+ struct pa_source *source;
+ if (!(source = pa_namereg_get(c, t, PA_NAMEREG_SOURCE)))
+ return -1;
+
+ *index = source->index;
+ }
+
+ return 0;
+}
+
+int pa_modargs_get_sink_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index) {
+ const char *t;
+ assert(ma && index);
+
+ if (!(t = pa_modargs_get_value(ma, "sink", NULL)))
+ *index = PA_IDXSET_INVALID;
+ else {
+ struct pa_sink *sink;
+ if (!(sink = pa_namereg_get(c, t, PA_NAMEREG_SINK)))
+ return -1;
+
+ *index = sink->index;
+ }
+
+ return 0;
+}
diff --git a/polyp/modargs.h b/polyp/modargs.h
new file mode 100644
index 00000000..301dc297
--- /dev/null
+++ b/polyp/modargs.h
@@ -0,0 +1,42 @@
+#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 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 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 "sample.h"
+#include "core.h"
+
+struct pa_modargs;
+
+struct pa_modargs *pa_modargs_new(const char *args, const char* const* keys);
+void pa_modargs_free(struct pa_modargs*ma);
+
+const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def);
+int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value);
+
+int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *ss);
+
+int pa_modargs_get_source_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index);
+int pa_modargs_get_sink_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index);
+
+#endif
diff --git a/polyp/module-alsa-sink.c b/polyp/module-alsa-sink.c
new file mode 100644
index 00000000..8a3388af
--- /dev/null
+++ b/polyp/module-alsa-sink.c
@@ -0,0 +1,259 @@
+/* $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 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 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 <sys/poll.h>
+
+#include <asoundlib.h>
+
+#include "module.h"
+#include "core.h"
+#include "memchunk.h"
+#include "sink.h"
+#include "modargs.h"
+#include "util.h"
+#include "sample-util.h"
+#include "alsa-util.h"
+
+struct userdata {
+ snd_pcm_t *pcm_handle;
+ struct pa_sink *sink;
+ void **io_sources;
+ unsigned n_io_sources;
+
+ size_t frame_size, fragment_size;
+ struct pa_memchunk memchunk, silence;
+};
+
+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 "plughw:0,0"
+
+static void xrun_recovery(struct userdata *u) {
+ assert(u);
+
+ fprintf(stderr, "*** ALSA-XRUN (playback) ***\n");
+
+ if (snd_pcm_prepare(u->pcm_handle) < 0)
+ fprintf(stderr, "snd_pcm_prepare() failed\n");
+}
+
+static void do_write(struct userdata *u) {
+ assert(u);
+
+ for (;;) {
+ struct 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, memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) {
+ if (frames == -EAGAIN)
+ return;
+
+ if (frames == -EPIPE) {
+ xrun_recovery(u);
+ continue;
+ }
+
+ fprintf(stderr, "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(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
+ struct userdata *u = userdata;
+ assert(u && a && id);
+
+ if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
+ xrun_recovery(u);
+
+ do_write(u);
+}
+
+static uint32_t sink_get_latency_cb(struct pa_sink *s) {
+ struct userdata *u = s->userdata;
+ snd_pcm_sframes_t frames;
+ assert(s && u && u->sink);
+
+ if (snd_pcm_delay(u->pcm_handle, &frames) < 0) {
+ fprintf(stderr, __FILE__": failed to get delay\n");
+ s->get_latency = NULL;
+ return 0;
+ }
+
+ if (frames < 0)
+ frames = 0;
+
+ return pa_samples_usec(frames * u->frame_size, &s->sample_spec);
+}
+
+int pa_module_init(struct pa_core *c, struct pa_module*m) {
+ struct pa_modargs *ma = NULL;
+ int ret = -1;
+ struct userdata *u = NULL;
+ const char *dev;
+ struct pa_sample_spec ss;
+ unsigned periods, fragsize;
+ snd_pcm_uframes_t buffer_size;
+ size_t frame_size;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ fprintf(stderr, __FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ fprintf(stderr, __FILE__": failed to parse sample specification\n");
+ goto fail;
+ }
+ frame_size = pa_sample_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) {
+ fprintf(stderr, __FILE__": failed to parse buffer metrics\n");
+ goto fail;
+ }
+ buffer_size = fragsize/frame_size*periods;
+
+ u = malloc(sizeof(struct userdata));
+ assert(u);
+ memset(u, 0, sizeof(struct userdata));
+ m->userdata = u;
+
+ if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) {
+ fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev);
+ goto fail;
+ }
+
+ if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) {
+ fprintf(stderr, __FILE__": Failed to set hardware parameters\n");
+ goto fail;
+ }
+
+ u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss);
+ 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_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) {
+ fprintf(stderr, __FILE__": failed to obtain file descriptors\n");
+ goto fail;
+ }
+
+ u->frame_size = frame_size;
+ u->fragment_size = buffer_size*u->frame_size/periods;
+
+ fprintf(stderr, __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);
+ 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_module_done(c, m);
+
+ goto finish;
+}
+
+void pa_module_done(struct pa_core *c, struct pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if ((u = m->userdata)) {
+ if (u->sink)
+ pa_sink_free(u->sink);
+
+ if (u->io_sources)
+ pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources);
+
+ 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);
+
+ free(u);
+ }
+}
+
diff --git a/polyp/module-alsa-source.c b/polyp/module-alsa-source.c
new file mode 100644
index 00000000..287a0350
--- /dev/null
+++ b/polyp/module-alsa-source.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 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 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 <sys/poll.h>
+
+#include <asoundlib.h>
+
+#include "module.h"
+#include "core.h"
+#include "memchunk.h"
+#include "sink.h"
+#include "modargs.h"
+#include "util.h"
+#include "sample-util.h"
+#include "alsa-util.h"
+
+struct userdata {
+ snd_pcm_t *pcm_handle;
+ struct pa_source *source;
+ void **io_sources;
+ unsigned n_io_sources;
+
+ size_t frame_size, fragment_size;
+ struct pa_memchunk memchunk;
+};
+
+static const char* const valid_modargs[] = {
+ "device",
+ "source_name",
+ "format",
+ "channels",
+ "rate",
+ "fragments",
+ "fragment_size",
+ NULL
+};
+
+#define DEFAULT_SOURCE_NAME "alsa_input"
+#define DEFAULT_DEVICE "hw:0,0"
+
+static void xrun_recovery(struct userdata *u) {
+ assert(u);
+
+ fprintf(stderr, "*** ALSA-XRUN (capture) ***\n");
+
+ if (snd_pcm_prepare(u->pcm_handle) < 0)
+ fprintf(stderr, "snd_pcm_prepare() failed\n");
+}
+
+static void do_read(struct userdata *u) {
+ assert(u);
+
+ for (;;) {
+ struct 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->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, 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;
+ }
+
+ fprintf(stderr, "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(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
+ struct userdata *u = userdata;
+ assert(u && a && id);
+
+ if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
+ xrun_recovery(u);
+
+ do_read(u);
+}
+
+int pa_module_init(struct pa_core *c, struct pa_module*m) {
+ struct pa_modargs *ma = NULL;
+ int ret = -1;
+ struct userdata *u = NULL;
+ const char *dev;
+ struct pa_sample_spec ss;
+ unsigned periods, fragsize;
+ snd_pcm_uframes_t buffer_size;
+ size_t frame_size;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ fprintf(stderr, __FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ fprintf(stderr, __FILE__": failed to parse sample specification\n");
+ goto fail;
+ }
+ frame_size = pa_sample_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) {
+ fprintf(stderr, __FILE__": failed to parse buffer metrics\n");
+ goto fail;
+ }
+ buffer_size = fragsize/frame_size*periods;
+
+ u = malloc(sizeof(struct userdata));
+ assert(u);
+ memset(u, 0, sizeof(struct userdata));
+ m->userdata = u;
+
+ if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0) {
+ fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev);
+ goto fail;
+ }
+
+ if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) {
+ fprintf(stderr, __FILE__": Failed to set hardware parameters\n");
+ goto fail;
+ }
+
+ u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss);
+ assert(u->source);
+
+ u->source->userdata = u;
+ 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_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) {
+ fprintf(stderr, __FILE__": failed to obtain file descriptors\n");
+ goto fail;
+ }
+
+ u->frame_size = frame_size;
+ u->fragment_size = buffer_size*u->frame_size/periods;
+
+ fprintf(stderr, __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_module_done(c, m);
+
+ goto finish;
+}
+
+void pa_module_done(struct pa_core *c, struct pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ if ((u = m->userdata)) {
+ if (u->source)
+ pa_source_free(u->source);
+
+ if (u->io_sources)
+ pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources);
+
+ 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);
+
+ free(u);
+ }
+}
+
diff --git a/polyp/module-cli.c b/polyp/module-cli.c
new file mode 100644
index 00000000..8897c9c6
--- /dev/null
+++ b/polyp/module-cli.c
@@ -0,0 +1,73 @@
+/* $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 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 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 "module.h"
+#include "iochannel.h"
+#include "cli.h"
+#include "sioman.h"
+
+static void eof_cb(struct pa_cli*c, void *userdata) {
+ struct pa_module *m = userdata;
+ assert(c && m);
+
+ pa_module_unload_request(m->core, m);
+}
+
+int pa_module_init(struct pa_core *c, struct pa_module*m) {
+ struct pa_iochannel *io;
+ assert(c && m);
+
+ if (m->argument) {
+ fprintf(stderr, __FILE__": module doesn't accept arguments.\n");
+ return -1;
+ }
+
+ if (pa_stdio_acquire() < 0) {
+ fprintf(stderr, __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_module_done(struct pa_core *c, struct pa_module*m) {
+ assert(c && m);
+
+ pa_cli_free(m->userdata);
+ pa_stdio_release();
+}
diff --git a/polyp/module-oss-mmap.c b/polyp/module-oss-mmap.c
new file mode 100644
index 00000000..800eaf25
--- /dev/null
+++ b/polyp/module-oss-mmap.c
@@ -0,0 +1,404 @@
+/* $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 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 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 "iochannel.h"
+#include "sink.h"
+#include "source.h"
+#include "module.h"
+#include "oss-util.h"
+#include "sample-util.h"
+#include "util.h"
+#include "modargs.h"
+
+struct userdata {
+ struct pa_sink *sink;
+ struct pa_source *source;
+ struct pa_core *core;
+ struct 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;
+
+ void *mainloop_source;
+
+ struct pa_memblock **in_memblocks, **out_memblocks;
+ unsigned out_current, in_current;
+};
+
+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 out_fill_memblocks(struct userdata *u, unsigned n) {
+ assert(u && u->out_memblocks);
+
+ while (n > 0) {
+ struct 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(u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size);
+ 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);
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
+ fprintf(stderr, "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) {
+ struct pa_memchunk chunk;
+
+ if (!u->in_memblocks[u->in_current]) {
+ chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed(u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size);
+ 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);
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
+ fprintf(stderr, "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(struct pa_mainloop_api *m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
+ struct userdata *u = userdata;
+
+ assert (u && u->core->mainloop == m && u->mainloop_source == id);
+
+ if (events & PA_MAINLOOP_API_IO_EVENT_INPUT)
+ do_read(u);
+ if (events & PA_MAINLOOP_API_IO_EVENT_OUTPUT)
+ do_write(u);
+}
+
+static uint32_t sink_get_latency_cb(struct pa_sink *s) {
+ struct userdata *u = s->userdata;
+ assert(s && u);
+
+ do_write(u);
+ return pa_samples_usec(u->out_fill, &s->sample_spec);
+}
+
+int pa_module_init(struct pa_core *c, struct 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;
+ struct pa_modargs *ma = NULL;
+ assert(c && m);
+
+ m->userdata = u = malloc(sizeof(struct userdata));
+ assert(u);
+ memset(u, 0, sizeof(struct userdata));
+ u->fd = -1;
+ u->core = c;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ fprintf(stderr, __FILE__": failed to parse module arguments.\n");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_u32(ma, "record", &record) < 0 || pa_modargs_get_value_u32(ma, "playback", &playback) < 0) {
+ fprintf(stderr, __FILE__": record= and playback= expect numeric arguments.\n");
+ goto fail;
+ }
+
+ mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+ if (mode == 0) {
+ fprintf(stderr, __FILE__": neither playback nor record enabled for device.\n");
+ goto fail;
+ }
+
+ nfrags = 12;
+ frag_size = 1024;
+ if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) {
+ fprintf(stderr, __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) {
+ fprintf(stderr, __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)) {
+ fprintf(stderr, "OSS device not mmap capable.\n");
+ goto fail;
+ }
+
+ fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+
+ 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) {
+ fprintf(stderr, "SNDCTL_DSP_GETISPACE: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ fprintf(stderr, "module-oss-mmap: 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) {
+ fprintf(stderr, "module-oss-mmap: mmap failed for input. Changing to O_WRONLY mode.\n");
+ mode = O_WRONLY;
+ } else {
+ fprintf(stderr, "modeule-oss-mmap: mmap(): %s\n", strerror(errno));
+ goto fail;
+ }
+ } else {
+
+ u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &u->sample_spec);
+ 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 = malloc(sizeof(struct pa_memblock *)*u->in_fragments);
+ memset(u->in_memblocks, 0, sizeof(struct pa_memblock *)*u->in_fragments);
+
+ enable_bits |= PCM_ENABLE_INPUT;
+ }
+ }
+
+ if (mode != O_RDONLY) {
+ if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
+ fprintf(stderr, "SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ fprintf(stderr, "module-oss: 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) {
+ fprintf(stderr, "module-oss-mmap: mmap filed for output. Changing to O_RDONLY mode.\n");
+ mode = O_RDONLY;
+ } else {
+ fprintf(stderr, "module-oss-mmap: 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, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &u->sample_spec);
+ 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 = malloc(sizeof(struct memblock *)*u->out_fragments);
+ memset(u->out_memblocks, 0, sizeof(struct pa_memblock *)*u->out_fragments);
+
+ enable_bits |= PCM_ENABLE_OUTPUT;
+ }
+ }
+
+ zero = 0;
+ if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) {
+ fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) {
+ fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ assert(u->source || u->sink);
+
+ u->mainloop_source = c->mainloop->source_io(c->mainloop, u->fd, (u->source ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | (u->sink ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), io_callback, u);
+ assert(u->mainloop_source);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ pa_module_done(c, m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa_module_done(struct pa_core *c, struct pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ u = m->userdata;
+ assert(u);
+
+ 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]);
+ free(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]);
+ free(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_free(u->sink);
+
+ if (u->source)
+ pa_source_free(u->source);
+
+ if (u->mainloop_source)
+ u->core->mainloop->cancel_io(u->core->mainloop, u->mainloop_source);
+
+ if (u->fd >= 0)
+ close(u->fd);
+
+ free(u);
+}
diff --git a/polyp/module-oss.c b/polyp/module-oss.c
new file mode 100644
index 00000000..d727534a
--- /dev/null
+++ b/polyp/module-oss.c
@@ -0,0 +1,304 @@
+/* $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 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 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 "iochannel.h"
+#include "sink.h"
+#include "source.h"
+#include "module.h"
+#include "oss-util.h"
+#include "sample-util.h"
+#include "util.h"
+#include "modargs.h"
+
+struct userdata {
+ struct pa_sink *sink;
+ struct pa_source *source;
+ struct pa_iochannel *io;
+ struct pa_core *core;
+
+ struct pa_memchunk memchunk, silence;
+
+ uint32_t in_fragment_size, out_fragment_size, sample_size;
+
+ int fd;
+};
+
+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 do_write(struct userdata *u) {
+ struct pa_memchunk *memchunk;
+ ssize_t r;
+ assert(u);
+
+ if (!u->sink || !pa_iochannel_is_writable(u->io))
+ return;
+
+ if (!u->memchunk.length) {
+ if (pa_sink_render(u->sink, u->out_fragment_size, &u->memchunk) < 0)
+ memchunk = &u->silence;
+ else
+ memchunk = &u->memchunk;
+ }
+
+ assert(memchunk->memblock && memchunk->length);
+
+ if ((r = pa_iochannel_write(u->io, memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) {
+ fprintf(stderr, "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;
+ }
+ }
+}
+
+static void do_read(struct userdata *u) {
+ struct pa_memchunk memchunk;
+ ssize_t r;
+ assert(u);
+
+ if (!u->source || !pa_iochannel_is_readable(u->io))
+ return;
+
+ memchunk.memblock = pa_memblock_new(u->in_fragment_size);
+ 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)
+ fprintf(stderr, "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);
+};
+
+static void io_callback(struct pa_iochannel *io, void*userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_write(u);
+ do_read(u);
+}
+
+static uint32_t sink_get_latency_cb(struct pa_sink *s) {
+ int arg;
+ struct userdata *u = s->userdata;
+ assert(s && u && u->sink);
+
+ if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
+ fprintf(stderr, "module-oss: device doesn't support SNDCTL_DSP_GETODELAY.\n");
+ s->get_latency = NULL;
+ return 0;
+ }
+
+ return pa_samples_usec(arg, &s->sample_spec);
+}
+
+int pa_module_init(struct pa_core *c, struct 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;
+ uint32_t record = 1, playback = 1;
+ struct pa_sample_spec ss;
+ struct pa_modargs *ma = NULL;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ fprintf(stderr, __FILE__": failed to parse module arguments.\n");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_u32(ma, "record", &record) < 0 || pa_modargs_get_value_u32(ma, "playback", &playback) < 0) {
+ fprintf(stderr, __FILE__": record= and playback= expect numeric argument.\n");
+ goto fail;
+ }
+
+ mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+ if (mode == 0) {
+ fprintf(stderr, __FILE__": neither playback nor record enabled for device.\n");
+ goto fail;
+ }
+
+ nfrags = 12;
+ frag_size = 1024;
+ if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) {
+ fprintf(stderr, __FILE__": failed to parse fragments arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ fprintf(stderr, __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;
+
+ fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+
+ 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) {
+ fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno));
+ goto fail;
+ }
+ assert(frag_size);
+ in_frag_size = out_frag_size = frag_size;
+
+ if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
+ fprintf(stderr, "module-oss: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
+ in_frag_size = info.fragsize;
+ }
+
+ if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
+ fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
+ out_frag_size = info.fragsize;
+ }
+
+ u = malloc(sizeof(struct userdata));
+ assert(u);
+
+ u->core = c;
+
+ if (mode != O_WRONLY) {
+ u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss);
+ assert(u->source);
+ u->source->userdata = u;
+ 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, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss);
+ 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 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_sample_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);
+ assert(u->silence.memblock);
+ pa_silence_memblock(u->silence.memblock, &ss);
+ u->silence.index = 0;
+
+ m->userdata = u;
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa_module_done(struct pa_core *c, struct pa_module*m) {
+ struct userdata *u;
+ assert(c && m);
+
+ u = m->userdata;
+ assert(u);
+
+ 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_free(u->sink);
+ if (u->source)
+ pa_source_free(u->source);
+ pa_iochannel_free(u->io);
+ free(u);
+}
diff --git a/polyp/module-pipe-sink.c b/polyp/module-pipe-sink.c
new file mode 100644
index 00000000..df34f73a
--- /dev/null
+++ b/polyp/module-pipe-sink.c
@@ -0,0 +1,218 @@
+/* $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 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 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 "iochannel.h"
+#include "sink.h"
+#include "module.h"
+#include "util.h"
+#include "modargs.h"
+
+#define DEFAULT_FIFO_NAME "/tmp/musicfifo"
+#define DEFAULT_SINK_NAME "fifo_output"
+
+struct userdata {
+ struct pa_core *core;
+
+ char *filename;
+
+ struct pa_sink *sink;
+ struct pa_iochannel *io;
+ void *mainloop_source;
+
+ struct pa_memchunk memchunk;
+};
+
+static const char* const valid_modargs[] = {
+ "file",
+ "rate",
+ "channels",
+ "format",
+ "sink_name",
+ NULL
+};
+
+static void do_write(struct userdata *u) {
+ ssize_t r;
+ assert(u);
+
+ u->core->mainloop->enable_fixed(u->core->mainloop, u->mainloop_source, 0);
+
+ if (!pa_iochannel_is_writable(u->io))
+ return;
+
+ 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, u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
+ fprintf(stderr, "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(struct pa_sink*s) {
+ struct userdata *u = s->userdata;
+ assert(s && u);
+
+ if (pa_iochannel_is_writable(u->io))
+ u->core->mainloop->enable_fixed(u->core->mainloop, u->mainloop_source, 1);
+}
+
+static void fixed_callback(struct pa_mainloop_api *m, void *id, void *userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_write(u);
+}
+
+static void io_callback(struct pa_iochannel *io, void*userdata) {
+ struct userdata *u = userdata;
+ assert(u);
+ do_write(u);
+}
+
+int pa_module_init(struct pa_core *c, struct pa_module*m) {
+ struct userdata *u = NULL;
+ struct stat st;
+ const char *p;
+ int fd = -1;
+ struct pa_sample_spec ss;
+ struct pa_modargs *ma = NULL;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ fprintf(stderr, __FILE__": failed to parse module arguments\n");
+ goto fail;
+ }
+
+ ss = c->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ fprintf(stderr, __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) {
+ fprintf(stderr, __FILE__": open('%s'): %s\n", p, strerror(errno));
+ goto fail;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ fprintf(stderr, __FILE__": fstat('%s'): %s\n", p, strerror(errno));
+ goto fail;
+ }
+
+ if (!S_ISFIFO(st.st_mode)) {
+ fprintf(stderr, __FILE__": '%s' is not a FIFO.\n", p);
+ goto fail;
+ }
+
+ u = malloc(sizeof(struct userdata));
+ assert(u);
+ memset(u, 0, sizeof(struct userdata));
+
+ u->filename = strdup(p);
+ assert(u->filename);
+ u->core = c;
+
+ if (!(u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss))) {
+ fprintf(stderr, __FILE__": failed to create sink.\n");
+ goto fail;
+ }
+ u->sink->notify = notify_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->mainloop_source = c->mainloop->source_fixed(c->mainloop, fixed_callback, u);
+ assert(u->mainloop_source);
+ c->mainloop->enable_fixed(c->mainloop, u->mainloop_source, 0);
+
+ m->userdata = u;
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ if (fd >= 0)
+ close(fd);
+
+ pa_module_done(c, m);
+
+ return -1;
+}
+
+void pa_module_done(struct pa_core *c, struct 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_free(u->sink);
+ pa_iochannel_free(u->io);
+ u->core->mainloop->cancel_fixed(u->core->mainloop, u->mainloop_source);
+
+ assert(u->filename);
+ unlink(u->filename);
+ free(u->filename);
+
+ free(u);
+}
diff --git a/polyp/module-protocol-stub.c b/polyp/module-protocol-stub.c
new file mode 100644
index 00000000..3ce18c31
--- /dev/null
+++ b/polyp/module-protocol-stub.c
@@ -0,0 +1,165 @@
+/* $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 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 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 <arpa/inet.h>
+#include <unistd.h>
+
+#include "module.h"
+#include "socket-server.h"
+#include "socket-util.h"
+#include "util.h"
+#include "modargs.h"
+
+#ifdef USE_PROTOCOL_SIMPLE
+ #include "protocol-simple.h"
+ #define protocol_new pa_protocol_simple_new
+ #define protocol_free pa_protocol_simple_free
+ #define IPV4_PORT 4711
+ #define UNIX_SOCKET "/tmp/polypaudio/simple"
+ #define MODULE_ARGUMENTS "rate", "format", "channels", "sink", "source", "playback", "record",
+#else
+ #ifdef USE_PROTOCOL_CLI
+ #include "protocol-cli.h"
+ #define protocol_new pa_protocol_cli_new
+ #define protocol_free pa_protocol_cli_free
+ #define IPV4_PORT 4712
+ #define UNIX_SOCKET "/tmp/polypaudio/cli"
+ #define MODULE_ARGUMENTS
+ #else
+ #ifdef USE_PROTOCOL_NATIVE
+ #include "protocol-native.h"
+ #define protocol_new pa_protocol_native_new
+ #define protocol_free pa_protocol_native_free
+ #define IPV4_PORT 4713
+ #define UNIX_SOCKET "/tmp/polypaudio/native"
+ #define MODULE_ARGUMENTS "public", "cookie",
+ #else
+ #ifdef USE_PROTOCOL_ESOUND
+ #include "protocol-esound.h"
+ #include "esound.h"
+ #define protocol_new pa_protocol_esound_new
+ #define protocol_free pa_protocol_esound_free
+ #define IPV4_PORT ESD_DEFAULT_PORT
+ #define UNIX_SOCKET ESD_UNIX_SOCKET_NAME
+ #define MODULE_ARGUMENTS "sink", "source", "public", "cookie",
+ #else
+ #error "Broken build system"
+ #endif
+ #endif
+ #endif
+#endif
+
+static const char* const valid_modargs[] = {
+ MODULE_ARGUMENTS
+#ifdef USE_TCP_SOCKETS
+ "port",
+ "loopback",
+#else
+ "socket",
+#endif
+ NULL
+};
+
+static struct pa_socket_server *create_socket_server(struct pa_core *c, struct pa_modargs *ma) {
+ struct pa_socket_server *s;
+#ifdef USE_TCP_SOCKETS
+ uint32_t loopback = 1, port = IPV4_PORT;
+
+ if (pa_modargs_get_value_u32(ma, "loopback", &loopback) < 0) {
+ fprintf(stderr, "loopback= expects a numerical argument.\n");
+ return NULL;
+ }
+
+ if (pa_modargs_get_value_u32(ma, "port", &port) < 0) {
+ fprintf(stderr, "port= expects a numerical argument.\n");
+ return NULL;
+ }
+
+ if (!(s = pa_socket_server_new_ipv4(c->mainloop, loopback ? INADDR_LOOPBACK : INADDR_ANY, port)))
+ return NULL;
+#else
+ int r;
+ const char *p;
+
+ p = pa_modargs_get_value(ma, "socket", UNIX_SOCKET);
+ assert(p);
+
+ if (pa_unix_socket_make_secure_dir(p) < 0) {
+ fprintf(stderr, "Failed to create secure socket directory.\n");
+ return NULL;
+ }
+
+ if ((r = pa_unix_socket_remove_stale(p)) < 0) {
+ fprintf(stderr, "Failed to remove stale UNIX socket '%s': %s\n", p, strerror(errno));
+ return NULL;
+ }
+
+ if (r)
+ fprintf(stderr, "Removed stale UNIX socket '%s'.", p);
+
+ if (!(s = pa_socket_server_new_unix(c->mainloop, p)))
+ return NULL;
+
+#endif
+ return s;
+}
+
+int pa_module_init(struct pa_core *c, struct pa_module*m) {
+ struct pa_socket_server *s;
+ struct pa_modargs *ma = NULL;
+ int ret = -1;
+ assert(c && m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ fprintf(stderr, "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_free(s);
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+ if (ma)
+ pa_modargs_free(ma);
+
+ return ret;
+}
+
+void pa_module_done(struct pa_core *c, struct pa_module*m) {
+ assert(c && m);
+
+ protocol_free(m->userdata);
+}
diff --git a/polyp/module.c b/polyp/module.c
new file mode 100644
index 00000000..5c6f0fb6
--- /dev/null
+++ b/polyp/module.c
@@ -0,0 +1,161 @@
+/* $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 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 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"
+
+struct pa_module* pa_module_load(struct pa_core *c, const char *name, const char *argument) {
+ struct pa_module *m = NULL;
+ int r;
+
+ assert(c && name);
+
+ m = malloc(sizeof(struct pa_module));
+ assert(m);
+
+ m->name = strdup(name);
+ m->argument = argument ? strdup(argument) : NULL;
+
+ if (!(m->dl = lt_dlopenext(name)))
+ goto fail;
+
+ if (!(m->init = lt_dlsym(m->dl, "pa_module_init")))
+ goto fail;
+
+ if (!(m->done = lt_dlsym(m->dl, "pa_module_done")))
+ goto fail;
+
+ m->userdata = NULL;
+ m->core = c;
+
+ assert(m->init);
+ if (m->init(c, m) < 0)
+ goto fail;
+
+ if (!c->modules)
+ c->modules = pa_idxset_new(NULL, NULL);
+
+ assert(c->modules);
+ r = pa_idxset_put(c->modules, m, &m->index);
+ assert(r >= 0 && m->index != PA_IDXSET_INVALID);
+
+ fprintf(stderr, "module: loaded %u \"%s\" with argument \"%s\".\n", m->index, m->name, m->argument);
+
+ return m;
+
+fail:
+ if (m) {
+ free(m->argument);
+ free(m->name);
+
+ if (m->dl)
+ lt_dlclose(m->dl);
+
+ free(m);
+ }
+
+ return NULL;
+}
+
+static void pa_module_free(struct pa_module *m) {
+ assert(m && m->done && m->core);
+ m->done(m->core, m);
+
+ lt_dlclose(m->dl);
+
+ fprintf(stderr, "module: unloaded %u \"%s\".\n", m->index, m->name);
+
+ free(m->name);
+ free(m->argument);
+ free(m);
+}
+
+
+void pa_module_unload(struct pa_core *c, struct 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(struct pa_core *c, uint32_t index) {
+ struct pa_module *m;
+ assert(c && index != PA_IDXSET_INVALID);
+
+ assert(c->modules);
+ if (!(m = pa_idxset_remove_by_index(c->modules, index)))
+ return;
+
+ pa_module_free(m);
+}
+
+static void free_callback(void *p, void *userdata) {
+ struct pa_module *m = p;
+ assert(m);
+ pa_module_free(m);
+}
+
+void pa_module_unload_all(struct pa_core *c) {
+ assert(c);
+
+ if (!c->modules)
+ return;
+
+ pa_idxset_free(c->modules, free_callback, NULL);
+ c->modules = NULL;
+}
+
+struct once_info {
+ struct pa_core *core;
+ uint32_t index;
+};
+
+
+static void module_unload_once_callback(void *userdata) {
+ struct once_info *i = userdata;
+ assert(i);
+ pa_module_unload_by_index(i->core, i->index);
+ free(i);
+}
+
+void pa_module_unload_request(struct pa_core *c, struct pa_module *m) {
+ struct once_info *i;
+ assert(c && m);
+
+ i = malloc(sizeof(struct once_info));
+ assert(i);
+ i->core = c;
+ i->index = m->index;
+ pa_mainloop_api_once(c->mainloop, module_unload_once_callback, i);
+}
diff --git a/polyp/module.h b/polyp/module.h
new file mode 100644
index 00000000..af2d8552
--- /dev/null
+++ b/polyp/module.h
@@ -0,0 +1,55 @@
+#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 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 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"
+
+struct pa_module {
+ struct pa_core *core;
+ char *name, *argument;
+ uint32_t index;
+
+ lt_dlhandle dl;
+
+ int (*init)(struct pa_core *c, struct pa_module*m);
+ void (*done)(struct pa_core *c, struct pa_module*m);
+
+ void *userdata;
+};
+
+struct pa_module* pa_module_load(struct pa_core *c, const char *name, const char*argument);
+void pa_module_unload(struct pa_core *c, struct pa_module *m);
+void pa_module_unload_by_index(struct pa_core *c, uint32_t index);
+
+void pa_module_unload_all(struct pa_core *c);
+
+void pa_module_unload_request(struct pa_core *c, struct pa_module *m);
+
+/* These to following prototypes are for module entrypoints and not implemented by the core */
+int pa_module_init(struct pa_core *c, struct pa_module*m);
+void pa_module_done(struct pa_core *c, struct pa_module*m);
+
+#endif
diff --git a/polyp/namereg.c b/polyp/namereg.c
new file mode 100644
index 00000000..2349436f
--- /dev/null
+++ b/polyp/namereg.c
@@ -0,0 +1,136 @@
+/* $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 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 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 <malloc.h>
+#include <stdio.h>
+
+#include "namereg.h"
+
+struct namereg_entry {
+ enum pa_namereg_type type;
+ char *name;
+ void *data;
+};
+
+void pa_namereg_free(struct pa_core *c) {
+ assert(c);
+ if (!c->namereg)
+ return;
+ assert(pa_hashmap_ncontents(c->namereg) == 0);
+ pa_hashmap_free(c->namereg, NULL, NULL);
+}
+
+const char *pa_namereg_register(struct pa_core *c, const char *name, enum pa_namereg_type 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 = strdup(name);
+ else {
+ unsigned i;
+ size_t l = strlen(name);
+ n = malloc(l+3);
+ assert(n);
+
+ 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) {
+ free(n);
+ return NULL;
+ }
+ }
+
+ assert(n);
+ e = malloc(sizeof(struct namereg_entry));
+ assert(e);
+ 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(struct pa_core *c, const char *name) {
+ struct namereg_entry *e;
+ int r;
+ assert(c && name);
+
+ e = pa_hashmap_get(c->namereg, name);
+ assert(e);
+
+ r = pa_hashmap_remove(c->namereg, name);
+ assert(r >= 0);
+
+ free(e->name);
+ free(e);
+}
+
+void* pa_namereg_get(struct pa_core *c, const char *name, enum pa_namereg_type type) {
+ struct namereg_entry *e;
+ uint32_t index;
+ char *x = NULL;
+ void *d = NULL;
+ assert(c && name);
+
+ if ((e = pa_hashmap_get(c->namereg, name)))
+ if (e->type == e->type)
+ return e->data;
+
+ index = (uint32_t) strtol(name, &x, 0);
+
+ if (!x || *x != 0)
+ return NULL;
+
+ if (type == PA_NAMEREG_SINK)
+ d = pa_idxset_get_by_index(c->sinks, index);
+ else if (type == PA_NAMEREG_SOURCE)
+ d = pa_idxset_get_by_index(c->sources, index);
+
+ return d;
+}
diff --git a/polyp/namereg.h b/polyp/namereg.h
new file mode 100644
index 00000000..0af83cd8
--- /dev/null
+++ b/polyp/namereg.h
@@ -0,0 +1,38 @@
+#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 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 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"
+
+enum pa_namereg_type {
+ PA_NAMEREG_SINK,
+ PA_NAMEREG_SOURCE
+};
+
+void pa_namereg_free(struct pa_core *c);
+
+const char *pa_namereg_register(struct pa_core *c, const char *name, enum pa_namereg_type type, void *data, int fail);
+void pa_namereg_unregister(struct pa_core *c, const char *name);
+void* pa_namereg_get(struct pa_core *c, const char *name, enum pa_namereg_type type);
+
+#endif
diff --git a/polyp/native-common.h b/polyp/native-common.h
new file mode 100644
index 00000000..2acbae8e
--- /dev/null
+++ b/polyp/native-common.h
@@ -0,0 +1,68 @@
+#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 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 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.
+***/
+
+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_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_MAX
+};
+
+enum {
+ PA_ERROR_OK,
+ PA_ERROR_ACCESS,
+ PA_ERROR_COMMAND,
+ PA_ERROR_INVALID,
+ PA_ERROR_EXIST,
+ PA_ERROR_NOENTITY,
+ PA_ERROR_CONNECTIONREFUSED,
+ PA_ERROR_PROTOCOL,
+ PA_ERROR_TIMEOUT,
+ PA_ERROR_AUTHKEY,
+ PA_ERROR_INTERNAL,
+ PA_ERROR_CONNECTIONTERMINATED,
+ PA_ERROR_KILLED,
+ PA_ERROR_INVALIDSERVER,
+ PA_ERROR_MAX
+};
+
+#define PA_NATIVE_COOKIE_LENGTH 256
+#define PA_NATIVE_COOKIE_FILE ".polypaudio-cookie"
+
+#endif
diff --git a/polyp/oss-util.c b/polyp/oss-util.c
new file mode 100644
index 00000000..cf55a6ee
--- /dev/null
+++ b/polyp/oss-util.c
@@ -0,0 +1,163 @@
+/* $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 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 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"
+
+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) {
+ fprintf(stderr, __FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (*tcaps & DSP_CAP_DUPLEX)
+ return fd;
+
+ close(fd);
+ }
+
+ if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) {
+ if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) {
+ fprintf(stderr, __FILE__": open('%s'): %s\n", device, strerror(errno));
+ goto fail;
+ }
+ }
+ } else {
+ if ((fd = open(device, *mode|O_NDELAY)) < 0) {
+ fprintf(stderr, __FILE__": open('%s'): %s\n", device, strerror(errno));
+ goto fail;
+ }
+ }
+
+ if (pcaps) {
+ if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
+ fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
+ goto fail;
+ }
+ }
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+ return fd;
+}
+
+int pa_oss_auto_format(int fd, struct pa_sample_spec *ss) {
+ int format, channels, speed, reqformat;
+ static const int format_trans[] = {
+ [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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "SNDCTL_DSP_SPEED: %s\n", strerror(errno));
+ return -1;
+ }
+ assert(speed);
+ ss->rate = speed;
+
+ return 0;
+}
+
+static int 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) | log2(frag_size);
+
+ if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
+ fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/polyp/oss-util.h b/polyp/oss-util.h
new file mode 100644
index 00000000..cb2ce33c
--- /dev/null
+++ b/polyp/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 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 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 "sample.h"
+
+int pa_oss_open(const char *device, int *mode, int* pcaps);
+int pa_oss_auto_format(int fd, struct pa_sample_spec *ss);
+
+int pa_oss_set_fragments(int fd, int frags, int frag_size);
+
+#endif
diff --git a/polyp/pacat-simple.c b/polyp/pacat-simple.c
new file mode 100644
index 00000000..5018aa5f
--- /dev/null
+++ b/polyp/pacat-simple.c
@@ -0,0 +1,83 @@
+/* $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 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 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 "polyplib-simple.h"
+#include "polyplib-error.h"
+
+#define BUFSIZE 1024
+
+int main(int argc, char*argv[]) {
+ static const struct pa_sample_spec ss = {
+ .format = PA_SAMPLE_S16LE,
+ .rate = 44100,
+ .channels = 2
+ };
+ struct pa_simple *s = NULL;
+ int ret = 1;
+ int error;
+
+ 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 ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) {
+ if (r == 0) /* eof */
+ break;
+
+ fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno));
+ goto finish;
+ }
+
+ 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 way 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/polyp/pacat.c b/polyp/pacat.c
new file mode 100644
index 00000000..1d1cc3d5
--- /dev/null
+++ b/polyp/pacat.c
@@ -0,0 +1,336 @@
+/* $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 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 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 "polyplib.h"
+#include "polyplib-error.h"
+#include "mainloop.h"
+#include "mainloop-signal.h"
+
+static enum { RECORD, PLAYBACK } mode = PLAYBACK;
+
+static struct pa_context *context = NULL;
+static struct pa_stream *stream = NULL;
+static struct pa_mainloop_api *mainloop_api = NULL;
+
+static void *buffer = NULL;
+static size_t buffer_length = 0, buffer_index = 0;
+
+static void* stdio_source = NULL;
+
+static void quit(int ret) {
+ assert(mainloop_api);
+ mainloop_api->quit(mainloop_api, ret);
+}
+
+static void context_die_callback(struct pa_context *c, void *userdata) {
+ assert(c);
+ fprintf(stderr, "Connection to server shut down, exiting.\n");
+ quit(1);
+}
+
+static void stream_die_callback(struct pa_stream *s, void *userdata) {
+ assert(s);
+ fprintf(stderr, "Stream deleted, exiting.\n");
+ quit(1);
+}
+
+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, buffer+buffer_index, l);
+ buffer_length -= l;
+ buffer_index += l;
+
+ if (!buffer_length) {
+ free(buffer);
+ buffer = NULL;
+ buffer_index = buffer_length = 0;
+ }
+}
+
+static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) {
+ assert(s && length);
+
+ if (stdio_source)
+ mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_INPUT);
+
+ if (!buffer)
+ return;
+
+ do_stream_write(length);
+}
+
+static void stream_read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata) {
+ assert(s && data && length);
+
+ if (stdio_source)
+ mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_OUTPUT);
+
+ if (buffer) {
+ fprintf(stderr, "Buffer overrrun, dropping incoming data\n");
+ return;
+ }
+
+ buffer = malloc(buffer_length = length);
+ assert(buffer);
+ memcpy(buffer, data, length);
+ buffer_index = 0;
+}
+
+static void stream_complete_callback(struct pa_stream*s, int success, void *userdata) {
+ assert(s);
+
+ if (!success) {
+ fprintf(stderr, "Stream creation failed: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+ quit(1);
+ return;
+ }
+
+ fprintf(stderr, "Stream created.\n");
+}
+
+static void context_complete_callback(struct pa_context *c, int success, void *userdata) {
+ static const struct pa_sample_spec ss = {
+ .format = PA_SAMPLE_S16LE,
+ .rate = 44100,
+ .channels = 2
+ };
+
+ assert(c && !stream);
+
+ if (!success) {
+ fprintf(stderr, "Connection failed: %s\n", pa_strerror(pa_context_errno(c)));
+ goto fail;
+ }
+
+ fprintf(stderr, "Connection established.\n");
+
+ if (!(stream = pa_stream_new(c, mode == PLAYBACK ? PA_STREAM_PLAYBACK : PA_STREAM_RECORD, NULL, "pacat", &ss, NULL, stream_complete_callback, NULL))) {
+ fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c)));
+ goto fail;
+ }
+
+ pa_stream_set_die_callback(stream, stream_die_callback, NULL);
+ pa_stream_set_write_callback(stream, stream_write_callback, NULL);
+ pa_stream_set_read_callback(stream, stream_read_callback, NULL);
+
+ return;
+
+fail:
+ quit(1);
+}
+
+static void context_drain_complete(struct pa_context*c, void *userdata) {
+ quit(0);
+}
+
+static void stream_drain_complete(struct pa_stream*s, void *userdata) {
+ fprintf(stderr, "Playback stream drained.\n");
+
+ pa_stream_free(stream);
+ stream = NULL;
+
+ if (pa_context_drain(context, context_drain_complete, NULL) < 0)
+ quit(0);
+ else
+ fprintf(stderr, "Draining connection to server.\n");
+}
+
+static void stdin_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
+ size_t l, w = 0;
+ ssize_t r;
+ assert(a == mainloop_api && id && stdio_source == id);
+
+ if (buffer) {
+ mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_NULL);
+ return;
+ }
+
+ if (!stream || !pa_stream_is_ready(stream) || !(l = w = pa_stream_writable_size(stream)))
+ l = 4096;
+
+ buffer = malloc(l);
+ assert(buffer);
+ if ((r = read(fd, buffer, l)) <= 0) {
+ if (r == 0) {
+ fprintf(stderr, "Got EOF.\n");
+ pa_stream_drain(stream, stream_drain_complete, NULL);
+ } else {
+ fprintf(stderr, "read() failed: %s\n", strerror(errno));
+ quit(1);
+ }
+
+ mainloop_api->cancel_io(mainloop_api, stdio_source);
+ stdio_source = NULL;
+ return;
+ }
+
+ buffer_length = r;
+ buffer_index = 0;
+
+ if (w)
+ do_stream_write(w);
+}
+
+static void stdout_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
+ ssize_t r;
+ assert(a == mainloop_api && id && stdio_source == id);
+
+ if (!buffer) {
+ mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_NULL);
+ return;
+ }
+
+ assert(buffer_length);
+
+ if ((r = write(fd, buffer+buffer_index, buffer_length)) <= 0) {
+ fprintf(stderr, "write() failed: %s\n", strerror(errno));
+ quit(1);
+
+ mainloop_api->cancel_io(mainloop_api, stdio_source);
+ stdio_source = NULL;
+ return;
+ }
+
+ buffer_length -= r;
+ buffer_index += r;
+
+ if (!buffer_length) {
+ free(buffer);
+ buffer = NULL;
+ buffer_length = buffer_index = 0;
+ }
+}
+
+static void exit_signal_callback(void *id, int sig, void *userdata) {
+ fprintf(stderr, "Got SIGINT, exiting.\n");
+ quit(0);
+
+}
+
+static void stream_get_latency_callback(struct pa_stream *s, uint32_t latency, void *userdata) {
+ assert(s);
+
+ if (latency == (uint32_t) -1) {
+ fprintf(stderr, "Failed to get latency: %s\n", strerror(errno));
+ quit(1);
+ return;
+ }
+
+ fprintf(stderr, "Current latency is %u usecs.\n", latency);
+}
+
+static void sigusr1_signal_callback(void *id, int sig, void *userdata) {
+ if (mode == PLAYBACK) {
+ fprintf(stderr, "Got SIGUSR1, requesting latency.\n");
+ pa_stream_get_latency(stream, stream_get_latency_callback, NULL);
+ }
+}
+
+int main(int argc, char *argv[]) {
+ struct pa_mainloop* m = NULL;
+ int ret = 1, r;
+ char *bn;
+
+ if (!(bn = strrchr(argv[0], '/')))
+ bn = argv[0];
+
+ if (strstr(bn, "rec") || strstr(bn, "mon"))
+ mode = RECORD;
+ else if (strstr(bn, "cat") || strstr(bn, "play"))
+ mode = PLAYBACK;
+
+ fprintf(stderr, "Opening a %s stream.\n", mode == RECORD ? "recording" : "playback");
+
+ 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_register(SIGINT, exit_signal_callback, NULL);
+ pa_signal_register(SIGUSR1, sigusr1_signal_callback, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (!(stdio_source = mainloop_api->source_io(mainloop_api,
+ mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
+ mode == PLAYBACK ? PA_MAINLOOP_API_IO_EVENT_INPUT : PA_MAINLOOP_API_IO_EVENT_OUTPUT,
+ mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
+ fprintf(stderr, "source_io() failed.\n");
+ goto quit;
+ }
+
+ if (!(context = pa_context_new(mainloop_api, argv[0]))) {
+ fprintf(stderr, "pa_context_new() failed.\n");
+ goto quit;
+ }
+
+ if (pa_context_connect(context, NULL, context_complete_callback, NULL) < 0) {
+ fprintf(stderr, "pa_context_connext() failed.\n");
+ goto quit;
+ }
+
+ pa_context_set_die_callback(context, context_die_callback, NULL);
+
+ if (pa_mainloop_run(m, &ret) < 0) {
+ fprintf(stderr, "pa_mainloop_run() failed.\n");
+ goto quit;
+ }
+
+quit:
+ if (stream)
+ pa_stream_free(stream);
+ if (context)
+ pa_context_free(context);
+
+ if (m) {
+ pa_signal_done();
+ pa_mainloop_free(m);
+ }
+
+ if (buffer)
+ free(buffer);
+
+ return ret;
+}
diff --git a/polyp/packet.c b/polyp/packet.c
new file mode 100644
index 00000000..e94df057
--- /dev/null
+++ b/polyp/packet.c
@@ -0,0 +1,72 @@
+/* $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 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 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"
+
+struct pa_packet* pa_packet_new(size_t length) {
+ struct pa_packet *p;
+ assert(length);
+ p = malloc(sizeof(struct pa_packet)+length);
+ assert(p);
+
+ p->ref = 1;
+ p->length = length;
+ p->data = (uint8_t*) (p+1);
+ p->type = PA_PACKET_APPENDED;
+ return p;
+}
+
+struct pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length) {
+ struct pa_packet *p;
+ assert(data && length);
+ p = malloc(sizeof(struct pa_packet));
+ assert(p);
+
+ p->ref = 1;
+ p->length = length;
+ p->data = data;
+ p->type = PA_PACKET_DYNAMIC;
+ return p;
+}
+
+struct pa_packet* pa_packet_ref(struct pa_packet *p) {
+ assert(p && p->ref >= 1);
+ p->ref++;
+ return p;
+}
+
+void pa_packet_unref(struct pa_packet *p) {
+ assert(p && p->ref >= 1);
+ p->ref--;
+
+ if (p->ref == 0) {
+ if (p->type == PA_PACKET_DYNAMIC)
+ free(p->data);
+ free(p);
+ }
+}
diff --git a/polyp/packet.h b/polyp/packet.h
new file mode 100644
index 00000000..8bea07e1
--- /dev/null
+++ b/polyp/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 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 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 <stdint.h>
+
+struct pa_packet {
+ enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type;
+ unsigned ref;
+ size_t length;
+ uint8_t *data;
+};
+
+struct pa_packet* pa_packet_new(size_t length);
+struct pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length);
+
+struct pa_packet* pa_packet_ref(struct pa_packet *p);
+void pa_packet_unref(struct pa_packet *p);
+
+#endif
diff --git a/polyp/pactl.c b/polyp/pactl.c
new file mode 100644
index 00000000..2b1ed2c1
--- /dev/null
+++ b/polyp/pactl.c
@@ -0,0 +1,166 @@
+/* $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 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 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 "polyplib.h"
+#include "polyplib-error.h"
+#include "mainloop.h"
+#include "mainloop-signal.h"
+
+static struct pa_context *context = NULL;
+static struct pa_mainloop_api *mainloop_api = NULL;
+
+static enum {
+ NONE,
+ EXIT,
+ STAT
+} action = NONE;
+
+static void quit(int ret) {
+ assert(mainloop_api);
+ mainloop_api->quit(mainloop_api, ret);
+}
+
+static void context_die_callback(struct pa_context *c, void *userdata) {
+ assert(c);
+ fprintf(stderr, "Connection to server shut down, exiting.\n");
+ quit(1);
+}
+
+static void context_drain_complete(struct pa_context *c, void *userdata) {
+ assert(c);
+ fprintf(stderr, "Connection to server shut down, exiting.\n");
+ quit(0);
+}
+
+static void drain(void) {
+ if (pa_context_drain(context, context_drain_complete, NULL) < 0)
+ quit(0);
+}
+
+static void stat_callback(struct pa_context *c, uint32_t blocks, uint32_t total, void *userdata) {
+ if (blocks == (uint32_t) -1) {
+ fprintf(stderr, "Failed to get statistics: %s\n", pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ fprintf(stderr, "Currently in use: %u blocks containing %u bytes total.\n", blocks, total);
+ drain();
+}
+
+static void context_complete_callback(struct pa_context *c, int success, void *userdata) {
+ assert(c);
+
+ if (!success) {
+ fprintf(stderr, "Connection failed: %s\n", pa_strerror(pa_context_errno(c)));
+ goto fail;
+ }
+
+ fprintf(stderr, "Connection established.\n");
+
+ if (action == STAT)
+ pa_context_stat(c, stat_callback, NULL);
+ else {
+ assert(action == EXIT);
+ pa_context_exit(c);
+ drain();
+ }
+
+ return;
+
+fail:
+ quit(1);
+}
+
+static void exit_signal_callback(void *id, int sig, void *userdata) {
+ fprintf(stderr, "Got SIGINT, exiting.\n");
+ quit(0);
+
+}
+
+int main(int argc, char *argv[]) {
+ struct pa_mainloop* m = NULL;
+ int ret = 1, r;
+
+ if (argc >= 2) {
+ if (!strcmp(argv[1], "stat"))
+ action = STAT;
+ else if (!strcmp(argv[1], "exit"))
+ action = EXIT;
+ }
+
+ if (action == NONE) {
+ fprintf(stderr, "No valid action specified. Use one of: stat, exit\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_register(SIGINT, exit_signal_callback, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (!(context = pa_context_new(mainloop_api, argv[0]))) {
+ fprintf(stderr, "pa_context_new() failed.\n");
+ goto quit;
+ }
+
+ if (pa_context_connect(context, NULL, context_complete_callback, NULL) < 0) {
+ fprintf(stderr, "pa_context_connext() failed.\n");
+ goto quit;
+ }
+
+ pa_context_set_die_callback(context, context_die_callback, NULL);
+
+ if (pa_mainloop_run(m, &ret) < 0) {
+ fprintf(stderr, "pa_mainloop_run() failed.\n");
+ goto quit;
+ }
+
+quit:
+ if (context)
+ pa_context_free(context);
+
+ if (m) {
+ pa_signal_done();
+ pa_mainloop_free(m);
+ }
+
+ return ret;
+}
diff --git a/polyp/parec-simple.c b/polyp/parec-simple.c
new file mode 100644
index 00000000..c4196a14
--- /dev/null
+++ b/polyp/parec-simple.c
@@ -0,0 +1,94 @@
+/* $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 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 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 "polyplib-simple.h"
+#include "polyplib-error.h"
+
+#define BUFSIZE 1024
+
+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 += r;
+ size -= r;
+ }
+
+ return ret;
+}
+
+int main(int argc, char*argv[]) {
+ static const struct pa_sample_spec ss = {
+ .format = PA_SAMPLE_S16LE,
+ .rate = 44100,
+ .channels = 2
+ };
+ struct pa_simple *s = NULL;
+ int ret = 1;
+ int error;
+
+ 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;
+
+ if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
+ fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
+ goto finish;
+ }
+
+ 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/polyp/pdispatch.c b/polyp/pdispatch.c
new file mode 100644
index 00000000..a28458a4
--- /dev/null
+++ b/polyp/pdispatch.c
@@ -0,0 +1,247 @@
+/* $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 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 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"
+
+#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_NAME] = "SET_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",
+};
+
+#endif
+
+struct reply_info {
+ struct pa_pdispatch *pdispatch;
+ struct reply_info *next, *previous;
+ void (*callback)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+ void *userdata;
+ uint32_t tag;
+ void *mainloop_timeout;
+ int callback_is_running;
+};
+
+struct pa_pdispatch {
+ struct pa_mainloop_api *mainloop;
+ const struct pa_pdispatch_command *command_table;
+ unsigned n_commands;
+ struct reply_info *replies;
+ void (*drain_callback)(struct pa_pdispatch *pd, void *userdata);
+ void *drain_userdata;
+ int in_use, shall_free;
+};
+
+static void reply_info_free(struct reply_info *r) {
+ assert(r && r->pdispatch && r->pdispatch->mainloop);
+
+ if (r->pdispatch)
+ r->pdispatch->mainloop->cancel_time(r->pdispatch->mainloop, r->mainloop_timeout);
+
+ if (r->previous)
+ r->previous->next = r->next;
+ else
+ r->pdispatch->replies = r->next;
+
+ if (r->next)
+ r->next->previous = r->previous;
+
+ free(r);
+}
+
+struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *mainloop, const struct pa_pdispatch_command*table, unsigned entries) {
+ struct pa_pdispatch *pd;
+ assert(mainloop);
+
+ assert((entries && table) || (!entries && !table));
+
+ pd = malloc(sizeof(struct pa_pdispatch));
+ assert(pd);
+ pd->mainloop = mainloop;
+ pd->command_table = table;
+ pd->n_commands = entries;
+ pd->replies = NULL;
+ pd->drain_callback = NULL;
+ pd->drain_userdata = NULL;
+
+ pd->in_use = pd->shall_free = 0;
+ return pd;
+}
+
+void pa_pdispatch_free(struct pa_pdispatch *pd) {
+ assert(pd);
+
+ if (pd->in_use) {
+ pd->shall_free = 1;
+ return;
+ }
+
+ while (pd->replies)
+ reply_info_free(pd->replies);
+ free(pd);
+}
+
+int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*packet, void *userdata) {
+ uint32_t tag, command;
+ struct pa_tagstruct *ts = NULL;
+ int ret = -1;
+ assert(pd && packet && packet->data && !pd->in_use);
+
+ 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
+ fprintf(stderr, __FILE__": Recieved opcode <%s>\n", command_names[command]);
+#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) {
+ pd->in_use = r->callback_is_running = 1;
+ assert(r->callback);
+ r->callback(r->pdispatch, command, tag, ts, r->userdata);
+ pd->in_use = r->callback_is_running = 0;
+ reply_info_free(r);
+
+ if (pd->shall_free)
+ pa_pdispatch_free(pd);
+ else {
+ if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
+ pd->drain_callback(pd, pd->drain_userdata);
+ }
+ }
+
+ } else if (pd->command_table && command < pd->n_commands) {
+ const struct pa_pdispatch_command *c = pd->command_table+command;
+
+ if (c->proc)
+ c->proc(pd, command, tag, ts, userdata);
+ } else
+ goto finish;
+
+ ret = 0;
+
+finish:
+ if (ts)
+ pa_tagstruct_free(ts);
+
+ return ret;
+}
+
+static void timeout_callback(struct pa_mainloop_api*m, void *id, const struct timeval *tv, void *userdata) {
+ struct reply_info*r = userdata;
+ assert (r && r->mainloop_timeout == id && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
+
+ r->callback(r->pdispatch, PA_COMMAND_TIMEOUT, r->tag, NULL, r->userdata);
+ reply_info_free(r);
+
+ if (r->pdispatch->drain_callback && !pa_pdispatch_is_pending(r->pdispatch))
+ r->pdispatch->drain_callback(r->pdispatch, r->pdispatch->drain_userdata);
+}
+
+void pa_pdispatch_register_reply(struct pa_pdispatch *pd, uint32_t tag, int timeout, void (*cb)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void *userdata) {
+ struct reply_info *r;
+ struct timeval tv;
+ assert(pd && cb);
+
+ r = malloc(sizeof(struct reply_info));
+ assert(r);
+ r->pdispatch = pd;
+ r->callback = cb;
+ r->userdata = userdata;
+ r->tag = tag;
+ r->callback_is_running = 0;
+
+ gettimeofday(&tv, NULL);
+ tv.tv_sec += timeout;
+
+ r->mainloop_timeout = pd->mainloop->source_time(pd->mainloop, &tv, timeout_callback, r);
+ assert(r->mainloop_timeout);
+
+ r->previous = NULL;
+ r->next = pd->replies;
+ if (r->next)
+ r->next->previous = r;
+ pd->replies = r;
+}
+
+int pa_pdispatch_is_pending(struct pa_pdispatch *pd) {
+ assert(pd);
+
+ return !!pd->replies;
+}
+
+void pa_pdispatch_set_drain_callback(struct pa_pdispatch *pd, void (*cb)(struct 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(struct pa_pdispatch *pd, void *userdata) {
+ struct reply_info *r, *n;
+ assert(pd);
+
+ for (r = pd->replies; r; r = n) {
+ n = r->next;
+
+ if (!r->callback_is_running && r->userdata == userdata) /* when this item's callback is currently running it is destroyed anyway in the very near future */
+ reply_info_free(r);
+ }
+}
diff --git a/polyp/pdispatch.h b/polyp/pdispatch.h
new file mode 100644
index 00000000..796c1e03
--- /dev/null
+++ b/polyp/pdispatch.h
@@ -0,0 +1,52 @@
+#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 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 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 "mainloop-api.h"
+
+struct pa_pdispatch;
+
+/* It is safe to destroy the calling pdispatch object from all callbacks */
+
+struct pa_pdispatch_command {
+ void (*proc)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+};
+
+struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *m, const struct pa_pdispatch_command*table, unsigned entries);
+void pa_pdispatch_free(struct pa_pdispatch *pd);
+
+int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*p, void *userdata);
+
+void pa_pdispatch_register_reply(struct pa_pdispatch *pd, uint32_t tag, int timeout, void (*cb)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void *userdata);
+
+int pa_pdispatch_is_pending(struct pa_pdispatch *pd);
+
+void pa_pdispatch_set_drain_callback(struct pa_pdispatch *pd, void (*cb)(struct pa_pdispatch *pd, void *userdata), void *userdata);
+
+/* Remove all reply slots with the give userdata parameter */
+void pa_pdispatch_unregister_reply(struct pa_pdispatch *pd, void *userdata);
+
+#endif
diff --git a/polyp/polypaudio.pa b/polyp/polypaudio.pa
new file mode 100755
index 00000000..177707ba
--- /dev/null
+++ b/polyp/polypaudio.pa
@@ -0,0 +1,41 @@
+#!./polypaudio -F
+
+#
+# This file is part of polypaudio.
+#
+# polypaudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU 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 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
+load module-alsa-sink
+load module-alsa-source device=plughw:1,0
+#load module-oss device="/dev/dsp"
+#load module-oss-mmap device="/dev/dsp"
+
+# Load several protocols
+load module-esound-protocol-tcp
+load module-simple-protocol-tcp
+load module-native-protocol-unix
+load module-cli-protocol-unix
+
+# Load the CLI module
+load module-cli
+
+.nofail
+
+# Make some devices default
+sink_default alsa_output
+source_default alsa_input
+
diff --git a/polyp/polyplib-def.h b/polyp/polyplib-def.h
new file mode 100644
index 00000000..e43f4b35
--- /dev/null
+++ b/polyp/polyplib-def.h
@@ -0,0 +1,40 @@
+#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 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 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>
+
+enum pa_stream_direction {
+ PA_STREAM_PLAYBACK,
+ PA_STREAM_RECORD
+};
+
+struct pa_buffer_attr {
+ uint32_t maxlength;
+ uint32_t tlength;
+ uint32_t prebuf;
+ uint32_t minreq;
+ uint32_t fragsize;
+};
+
+#endif
diff --git a/polyp/polyplib-error.c b/polyp/polyplib-error.c
new file mode 100644
index 00000000..10b637c4
--- /dev/null
+++ b/polyp/polyplib-error.c
@@ -0,0 +1,53 @@
+/* $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 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 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 "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 corrupt",
+ [PA_ERROR_TIMEOUT] = "Timeout",
+ [PA_ERROR_AUTHKEY] = "Not authorization key",
+ [PA_ERROR_INTERNAL] = "Internal error",
+ [PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated",
+ [PA_ERROR_KILLED] = "Entity killed",
+};
+
+const char*pa_strerror(uint32_t error) {
+ if (error >= PA_ERROR_MAX)
+ return NULL;
+
+ return errortab[error];
+}
diff --git a/polyp/polyplib-error.h b/polyp/polyplib-error.h
new file mode 100644
index 00000000..cb86864a
--- /dev/null
+++ b/polyp/polyplib-error.h
@@ -0,0 +1,29 @@
+#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 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 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>
+
+const char* pa_strerror(uint32_t error);
+
+#endif
diff --git a/polyp/polyplib-simple.c b/polyp/polyplib-simple.c
new file mode 100644
index 00000000..024cb18a
--- /dev/null
+++ b/polyp/polyplib-simple.c
@@ -0,0 +1,259 @@
+/* $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 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 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 "native-common.h"
+/*#include "polyp-error.h"*/
+
+struct pa_simple {
+ struct pa_mainloop *mainloop;
+ struct pa_context *context;
+ struct pa_stream *stream;
+ enum pa_stream_direction direction;
+
+ int dead, drained;
+
+ void *read_data;
+ size_t read_index, read_length;
+};
+
+static void read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata);
+
+static int check_error(struct pa_simple *p, int *perror) {
+ assert(p);
+
+ if (pa_context_is_dead(p->context) || (p->stream && pa_stream_is_dead(p->stream))) {
+ if (perror)
+ *perror = pa_context_errno(p->context);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int iterate(struct pa_simple *p, int block, int *perror) {
+ assert(p && p->context && p->mainloop);
+
+ if (check_error(p, perror) < 0)
+ return -1;
+
+ if (!block && !pa_context_is_pending(p->context))
+ return 0;
+
+ do {
+ if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) {
+ if (perror)
+ *perror = PA_ERROR_INTERNAL;
+ return -1;
+ }
+
+ if (check_error(p, perror) < 0)
+ return -1;
+
+ } while (pa_context_is_pending(p->context));
+
+ return 0;
+}
+
+struct pa_simple* pa_simple_new(
+ const char *server,
+ const char *name,
+ enum pa_stream_direction dir,
+ const char *dev,
+ const char *stream_name,
+ const struct pa_sample_spec *ss,
+ const struct pa_buffer_attr *attr,
+ int *perror) {
+
+ struct pa_simple *p;
+ int error = PA_ERROR_INTERNAL;
+ assert(ss);
+
+ p = malloc(sizeof(struct pa_simple));
+ assert(p);
+ 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;
+
+ if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name)))
+ goto fail;
+
+ if (pa_context_connect(p->context, server, NULL, NULL) < 0) {
+ error = pa_context_errno(p->context);
+ goto fail;
+ }
+
+ /* Wait until the context is ready */
+ while (!pa_context_is_ready(p->context)) {
+ if (iterate(p, 1, &error) < 0)
+ goto fail;
+ }
+
+ if (!(p->stream = pa_stream_new(p->context, dir, dev, stream_name, ss, attr, NULL, NULL)))
+ goto fail;
+
+ /* Wait until the stream is ready */
+ while (!pa_stream_is_ready(p->stream)) {
+ if (iterate(p, 1, &error) < 0)
+ goto fail;
+ }
+
+ pa_stream_set_read_callback(p->stream, read_callback, p);
+
+ return p;
+
+fail:
+ if (perror)
+ *perror = error;
+ pa_simple_free(p);
+ return NULL;
+}
+
+void pa_simple_free(struct pa_simple *s) {
+ assert(s);
+
+ free(s->read_data);
+
+ if (s->stream)
+ pa_stream_free(s->stream);
+
+ if (s->context)
+ pa_context_free(s->context);
+
+ if (s->mainloop)
+ pa_mainloop_free(s->mainloop);
+
+ free(s);
+}
+
+int pa_simple_write(struct pa_simple *p, const void*data, size_t length, int *perror) {
+ assert(p && data && p->direction == PA_STREAM_PLAYBACK);
+
+ while (length > 0) {
+ size_t l;
+
+ while (!(l = pa_stream_writable_size(p->stream)))
+ if (iterate(p, 1, perror) < 0)
+ return -1;
+
+ if (l > length)
+ l = length;
+
+ pa_stream_write(p->stream, data, l);
+ data += l;
+ length -= l;
+ }
+
+ /* Make sure that no data is pending for write */
+ if (iterate(p, 0, perror) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata) {
+ struct pa_simple *p = userdata;
+ assert(s && data && length && p);
+
+ if (p->read_data) {
+ fprintf(stderr, __FILE__": Buffer overflow, dropping incoming memory blocks.\n");
+ free(p->read_data);
+ }
+
+ p->read_data = malloc(p->read_length = length);
+ assert(p->read_data);
+ memcpy(p->read_data, data, length);
+ p->read_index = 0;
+}
+
+int pa_simple_read(struct pa_simple *p, void*data, size_t length, int *perror) {
+ assert(p && data && p->direction == PA_STREAM_RECORD);
+
+ while (length > 0) {
+ if (p->read_data) {
+ size_t l = length;
+
+ if (p->read_length <= l)
+ l = p->read_length;
+
+ memcpy(data, p->read_data+p->read_index, l);
+
+ data += l;
+ length -= l;
+
+ p->read_index += l;
+ p->read_length -= l;
+
+ if (!p->read_length) {
+ free(p->read_data);
+ p->read_data = NULL;
+ p->read_index = 0;
+ }
+
+ if (!length)
+ return 0;
+
+ assert(!p->read_data);
+ }
+
+ if (iterate(p, 1, perror) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void drain_complete(struct pa_stream *s, void *userdata) {
+ struct pa_simple *p = userdata;
+ assert(s && p);
+ p->drained = 1;
+}
+
+int pa_simple_drain(struct pa_simple *p, int *perror) {
+ assert(p && p->direction == PA_STREAM_PLAYBACK);
+ p->drained = 0;
+ pa_stream_drain(p->stream, drain_complete, p);
+
+ while (!p->drained) {
+ if (iterate(p, 1, perror) < 0) {
+ pa_stream_drain(p->stream, NULL, NULL);
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/polyp/polyplib-simple.h b/polyp/polyplib-simple.h
new file mode 100644
index 00000000..0544410c
--- /dev/null
+++ b/polyp/polyplib-simple.h
@@ -0,0 +1,49 @@
+#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 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 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"
+
+struct pa_simple;
+
+struct pa_simple* pa_simple_new(
+ const char *server,
+ const char *name,
+ enum pa_stream_direction dir,
+ const char *dev,
+ const char *stream_name,
+ const struct pa_sample_spec *ss,
+ const struct pa_buffer_attr *attr,
+ int *error);
+
+void pa_simple_free(struct pa_simple *s);
+
+int pa_simple_write(struct pa_simple *s, const void*data, size_t length, int *error);
+int pa_simple_drain(struct pa_simple *s, int *error);
+
+int pa_simple_read(struct pa_simple *s, void*data, size_t length, int *error);
+
+#endif
diff --git a/polyp/polyplib.c b/polyp/polyplib.c
new file mode 100644
index 00000000..ea5003b4
--- /dev/null
+++ b/polyp/polyplib.c
@@ -0,0 +1,973 @@
+/* $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 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 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 <sys/socket.h>
+#include <netdb.h>
+
+#include "polyplib.h"
+#include "native-common.h"
+#include "pdispatch.h"
+#include "pstream.h"
+#include "dynarray.h"
+#include "socket-client.h"
+#include "pstream-util.h"
+#include "authkey.h"
+#include "util.h"
+
+#define DEFAULT_MAXLENGTH 204800
+#define DEFAULT_TLENGTH 10240
+#define DEFAULT_PREBUF 4096
+#define DEFAULT_MINREQ 1024
+#define DEFAULT_FRAGSIZE 1024
+
+#define DEFAULT_TIMEOUT (5*60)
+#define DEFAULT_SERVER "/tmp/polypaudio/native"
+#define DEFAULT_PORT "4713"
+
+struct pa_context {
+ char *name;
+ struct pa_mainloop_api* mainloop;
+ struct pa_socket_client *client;
+ struct pa_pstream *pstream;
+ struct pa_pdispatch *pdispatch;
+ struct pa_dynarray *record_streams, *playback_streams;
+ struct pa_stream *first_stream;
+ uint32_t ctag;
+ uint32_t error;
+ enum {
+ CONTEXT_UNCONNECTED,
+ CONTEXT_CONNECTING,
+ CONTEXT_AUTHORIZING,
+ CONTEXT_SETTING_NAME,
+ CONTEXT_READY,
+ CONTEXT_DEAD
+ } state;
+
+ void (*connect_complete_callback)(struct pa_context*c, int success, void *userdata);
+ void *connect_complete_userdata;
+
+ void (*drain_complete_callback)(struct pa_context*c, void *userdata);
+ void *drain_complete_userdata;
+
+ void (*die_callback)(struct pa_context*c, void *userdata);
+ void *die_userdata;
+
+ void (*stat_callback)(struct pa_context*c, uint32_t count, uint32_t total, void *userdata);
+ void *stat_userdata;
+
+ uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
+};
+
+struct pa_stream {
+ struct pa_context *context;
+ struct pa_stream *next, *previous;
+
+ char *name;
+ struct pa_buffer_attr buffer_attr;
+ struct pa_sample_spec sample_spec;
+ uint32_t device_index;
+ uint32_t channel;
+ int channel_valid;
+ enum pa_stream_direction direction;
+
+ enum { STREAM_LOOKING_UP, STREAM_CREATING, STREAM_READY, STREAM_DEAD} state;
+ uint32_t requested_bytes;
+
+ void (*read_callback)(struct pa_stream *p, const void*data, size_t length, void *userdata);
+ void *read_userdata;
+
+ void (*write_callback)(struct pa_stream *p, size_t length, void *userdata);
+ void *write_userdata;
+
+ void (*create_complete_callback)(struct pa_stream *s, int success, void *userdata);
+ void *create_complete_userdata;
+
+ void (*drain_complete_callback)(struct pa_stream *s, void *userdata);
+ void *drain_complete_userdata;
+
+ void (*die_callback)(struct pa_stream*c, void *userdata);
+ void *die_userdata;
+
+ void (*get_latency_callback)(struct pa_stream*c, uint32_t latency, void *userdata);
+ void *get_latency_userdata;
+};
+
+static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_playback_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+
+static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = {
+ [PA_COMMAND_ERROR] = { NULL },
+ [PA_COMMAND_REPLY] = { NULL },
+ [PA_COMMAND_CREATE_PLAYBACK_STREAM] = { NULL },
+ [PA_COMMAND_DELETE_PLAYBACK_STREAM] = { NULL },
+ [PA_COMMAND_CREATE_RECORD_STREAM] = { NULL },
+ [PA_COMMAND_DELETE_RECORD_STREAM] = { NULL },
+ [PA_COMMAND_EXIT] = { NULL },
+ [PA_COMMAND_REQUEST] = { command_request },
+ [PA_COMMAND_PLAYBACK_STREAM_KILLED] = { command_playback_stream_killed },
+ [PA_COMMAND_RECORD_STREAM_KILLED] = { command_playback_stream_killed },
+};
+
+struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name) {
+ struct pa_context *c;
+ assert(mainloop && name);
+
+ c = malloc(sizeof(struct pa_context));
+ assert(c);
+ c->name = strdup(name);
+ c->mainloop = mainloop;
+ c->client = NULL;
+ c->pstream = NULL;
+ c->pdispatch = NULL;
+ c->playback_streams = pa_dynarray_new();
+ assert(c->playback_streams);
+ c->record_streams = pa_dynarray_new();
+ assert(c->record_streams);
+ c->first_stream = NULL;
+ c->error = PA_ERROR_OK;
+ c->state = CONTEXT_UNCONNECTED;
+ c->ctag = 0;
+
+ c->connect_complete_callback = NULL;
+ c->connect_complete_userdata = NULL;
+
+ c->drain_complete_callback = NULL;
+ c->drain_complete_userdata = NULL;
+
+ c->die_callback = NULL;
+ c->die_userdata = NULL;
+
+ c->stat_callback = NULL;
+ c->stat_userdata = NULL;
+
+ pa_check_for_sigpipe();
+ return c;
+}
+
+void pa_context_free(struct pa_context *c) {
+ assert(c);
+
+ while (c->first_stream)
+ pa_stream_free(c->first_stream);
+
+ if (c->client)
+ pa_socket_client_free(c->client);
+ if (c->pdispatch)
+ pa_pdispatch_free(c->pdispatch);
+ if (c->pstream)
+ pa_pstream_free(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);
+
+ free(c->name);
+ free(c);
+}
+
+static void stream_dead(struct pa_stream *s) {
+ assert(s);
+
+ if (s->state == STREAM_DEAD)
+ return;
+
+ if (s->state == STREAM_READY) {
+ s->state = STREAM_DEAD;
+ if (s->die_callback)
+ s->die_callback(s, s->die_userdata);
+ } else
+ s->state = STREAM_DEAD;
+}
+
+static void context_dead(struct pa_context *c) {
+ struct pa_stream *s;
+ assert(c);
+
+ if (c->state == CONTEXT_DEAD)
+ return;
+
+ if (c->pdispatch)
+ pa_pdispatch_free(c->pdispatch);
+ c->pdispatch = NULL;
+
+ if (c->pstream)
+ pa_pstream_free(c->pstream);
+ c->pstream = NULL;
+
+ if (c->client)
+ pa_socket_client_free(c->client);
+ c->client = NULL;
+
+ for (s = c->first_stream; s; s = s->next)
+ stream_dead(s);
+
+ if (c->state == CONTEXT_READY) {
+ c->state = CONTEXT_DEAD;
+ if (c->die_callback)
+ c->die_callback(c, c->die_userdata);
+ } else
+ c->state = CONTEXT_DEAD;
+}
+
+static void pstream_die_callback(struct pa_pstream *p, void *userdata) {
+ struct pa_context *c = userdata;
+ assert(p && c);
+ c->error = PA_ERROR_CONNECTIONTERMINATED;
+ context_dead(c);
+}
+
+static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) {
+ struct pa_context *c = userdata;
+ assert(p && packet && c);
+
+ if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) {
+ fprintf(stderr, "polyp.c: invalid packet.\n");
+ c->error = PA_ERROR_PROTOCOL;
+ context_dead(c);
+ }
+}
+
+static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata) {
+ struct pa_context *c = userdata;
+ struct pa_stream *s;
+ assert(p && chunk && c && chunk->memblock && chunk->memblock->data);
+
+ if (!(s = pa_dynarray_get(c->record_streams, channel)))
+ return;
+
+ if (s->read_callback)
+ s->read_callback(s, chunk->memblock->data + chunk->index, chunk->length, s->read_userdata);
+}
+
+static int handle_error(struct pa_context *c, uint32_t command, struct pa_tagstruct *t) {
+ assert(c && t);
+
+ if (command == PA_COMMAND_ERROR) {
+ if (pa_tagstruct_getu32(t, &c->error) < 0) {
+ c->error = PA_ERROR_PROTOCOL;
+ return -1;
+ }
+
+ return 0;
+ }
+
+ c->error = (command == PA_COMMAND_TIMEOUT) ? PA_ERROR_TIMEOUT : PA_ERROR_INTERNAL;
+ return -1;
+}
+
+static void setup_complete_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct pa_context *c = userdata;
+ assert(pd && c && (c->state == CONTEXT_AUTHORIZING || c->state == CONTEXT_SETTING_NAME));
+
+ if (command != PA_COMMAND_REPLY) {
+ handle_error(c, command, t);
+ context_dead(c);
+
+ if (c->connect_complete_callback)
+ c->connect_complete_callback(c, 0, c->connect_complete_userdata);
+
+ return;
+ }
+
+ if (c->state == CONTEXT_AUTHORIZING) {
+ struct pa_tagstruct *t;
+ c->state = CONTEXT_SETTING_NAME;
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_SET_NAME);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_tagstruct_puts(t, c->name);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c);
+ } else {
+ assert(c->state == CONTEXT_SETTING_NAME);
+
+ c->state = CONTEXT_READY;
+
+ if (c->connect_complete_callback)
+ c->connect_complete_callback(c, 1, c->connect_complete_userdata);
+ }
+
+ return;
+}
+
+static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata) {
+ struct pa_context *c = userdata;
+ struct pa_tagstruct *t;
+ uint32_t tag;
+ assert(client && c && c->state == CONTEXT_CONNECTING);
+
+ pa_socket_client_free(client);
+ c->client = NULL;
+
+ if (!io) {
+ c->error = PA_ERROR_CONNECTIONREFUSED;
+ context_dead(c);
+
+ if (c->connect_complete_callback)
+ c->connect_complete_callback(c, 0, c->connect_complete_userdata);
+
+ return;
+ }
+
+ c->pstream = pa_pstream_new(c->mainloop, io);
+ 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);
+
+ c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
+ assert(c->pdispatch);
+
+ 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->auth_cookie, sizeof(c->auth_cookie));
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c);
+ c->state = CONTEXT_AUTHORIZING;
+}
+
+static struct sockaddr *resolve_server(const char *server, size_t *len) {
+ struct sockaddr *sa;
+ struct addrinfo hints, *result = NULL;
+ char *port;
+ assert(server && len);
+
+ if ((port = strrchr(server, ':')))
+ port++;
+ if (!port)
+ port = DEFAULT_PORT;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+
+ if (getaddrinfo(server, port, &hints, &result) != 0)
+ return NULL;
+ assert(result);
+
+ sa = malloc(*len = result->ai_addrlen);
+ assert(sa);
+ memcpy(sa, result->ai_addr, *len);
+
+ freeaddrinfo(result);
+
+ return sa;
+
+}
+
+int pa_context_connect(struct pa_context *c, const char *server, void (*complete) (struct pa_context*c, int success, void *userdata), void *userdata) {
+ assert(c && c->state == CONTEXT_UNCONNECTED);
+
+ if (pa_authkey_load_from_home(PA_NATIVE_COOKIE_FILE, c->auth_cookie, sizeof(c->auth_cookie)) < 0) {
+ c->error = PA_ERROR_AUTHKEY;
+ return -1;
+ }
+
+ if (!server)
+ if (!(server = getenv("POLYP_SERVER")))
+ server = DEFAULT_SERVER;
+
+ assert(!c->client);
+
+ if (*server == '/') {
+ if (!(c->client = pa_socket_client_new_unix(c->mainloop, server))) {
+ c->error = PA_ERROR_CONNECTIONREFUSED;
+ return -1;
+ }
+ } else {
+ struct sockaddr* sa;
+ size_t sa_len;
+
+ if (!(sa = resolve_server(server, &sa_len))) {
+ c->error = PA_ERROR_INVALIDSERVER;
+ return -1;
+ }
+
+ c->client = pa_socket_client_new_sockaddr(c->mainloop, sa, sa_len);
+ free(sa);
+
+ if (!c->client) {
+ c->error = PA_ERROR_CONNECTIONREFUSED;
+ return -1;
+ }
+ }
+
+ c->connect_complete_callback = complete;
+ c->connect_complete_userdata = userdata;
+
+ pa_socket_client_set_callback(c->client, on_connection, c);
+ c->state = CONTEXT_CONNECTING;
+
+ return 0;
+}
+
+int pa_context_is_dead(struct pa_context *c) {
+ assert(c);
+ return c->state == CONTEXT_DEAD;
+}
+
+int pa_context_is_ready(struct pa_context *c) {
+ assert(c);
+ return c->state == CONTEXT_READY;
+}
+
+int pa_context_errno(struct pa_context *c) {
+ assert(c);
+ return c->error;
+}
+
+void pa_context_set_die_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata) {
+ assert(c);
+ c->die_callback = cb;
+ c->die_userdata = userdata;
+}
+
+static void command_playback_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct pa_context *c = userdata;
+ struct pa_stream *s;
+ uint32_t channel;
+ assert(pd && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ c->error = PA_ERROR_PROTOCOL;
+ context_dead(c);
+ return;
+ }
+
+ if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
+ return;
+
+ c->error = PA_ERROR_KILLED;
+ stream_dead(s);
+}
+
+static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct pa_stream *s;
+ struct pa_context *c = userdata;
+ uint32_t bytes, channel;
+ assert(pd && command == PA_COMMAND_REQUEST && t && c);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_getu32(t, &bytes) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ c->error = PA_ERROR_PROTOCOL;
+ context_dead(c);
+ return;
+ }
+
+ if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+ return;
+
+ if (s->state != STREAM_READY)
+ return;
+
+ s->requested_bytes += bytes;
+
+ if (s->requested_bytes && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
+}
+
+static void create_stream_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct pa_stream *s = userdata;
+ assert(pd && s && s->state == STREAM_CREATING);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (handle_error(s->context, command, t) < 0) {
+ context_dead(s->context);
+ return;
+ }
+
+ stream_dead(s);
+ if (s->create_complete_callback)
+ s->create_complete_callback(s, 0, s->create_complete_userdata);
+
+ return;
+ }
+
+ if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
+ pa_tagstruct_getu32(t, &s->device_index) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ s->context->error = PA_ERROR_PROTOCOL;
+ context_dead(s->context);
+ return;
+ }
+
+ s->channel_valid = 1;
+ pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, s);
+
+ s->state = STREAM_READY;
+ if (s->create_complete_callback)
+ s->create_complete_callback(s, 1, s->create_complete_userdata);
+}
+
+static void create_stream(struct pa_stream *s, uint32_t tdev_index) {
+ struct pa_tagstruct *t;
+ uint32_t tag;
+ assert(s);
+
+ s->state = STREAM_CREATING;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+
+ pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_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, tdev_index);
+ pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+ if (s->direction == PA_STREAM_PLAYBACK) {
+ pa_tagstruct_putu32(t, s->buffer_attr.tlength);
+ pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
+ pa_tagstruct_putu32(t, s->buffer_attr.minreq);
+ } 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, create_stream_callback, s);
+}
+
+static void lookup_device_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct pa_stream *s = userdata;
+ uint32_t tdev;
+ assert(pd && s && s->state == STREAM_LOOKING_UP);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (handle_error(s->context, command, t) < 0) {
+ context_dead(s->context);
+ return;
+ }
+
+ stream_dead(s);
+ if (s->create_complete_callback)
+ s->create_complete_callback(s, 0, s->create_complete_userdata);
+ return;
+ }
+
+ if (pa_tagstruct_getu32(t, &tdev) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ s->context->error = PA_ERROR_PROTOCOL;
+ context_dead(s->context);
+ return;
+ }
+
+ create_stream(s, tdev);
+}
+
+static void lookup_device(struct pa_stream *s, const char *tdev) {
+ struct pa_tagstruct *t;
+ uint32_t tag;
+ assert(s);
+
+ s->state = STREAM_LOOKING_UP;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+
+ pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_LOOKUP_SINK : PA_COMMAND_LOOKUP_SOURCE);
+ pa_tagstruct_putu32(t, tag = s->context->ctag++);
+ pa_tagstruct_puts(t, tdev);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, lookup_device_callback, s);
+}
+
+struct pa_stream* pa_stream_new(
+ struct pa_context *c,
+ enum pa_stream_direction dir,
+ const char *dev,
+ const char *name,
+ const struct pa_sample_spec *ss,
+ const struct pa_buffer_attr *attr,
+ void (*complete) (struct pa_stream*s, int success, void *userdata),
+ void *userdata) {
+
+ struct pa_stream *s;
+
+ assert(c && name && ss && c->state == CONTEXT_READY);
+
+ s = malloc(sizeof(struct pa_stream));
+ assert(s);
+ s->context = c;
+
+ s->read_callback = NULL;
+ s->read_userdata = NULL;
+ s->write_callback = NULL;
+ s->write_userdata = NULL;
+ s->die_callback = NULL;
+ s->die_userdata = NULL;
+ s->create_complete_callback = complete;
+ s->create_complete_userdata = NULL;
+ s->get_latency_callback = NULL;
+ s->get_latency_userdata = NULL;
+
+ s->name = strdup(name);
+ s->state = STREAM_CREATING;
+ s->requested_bytes = 0;
+ s->channel = 0;
+ s->channel_valid = 0;
+ s->device_index = (uint32_t) -1;
+ s->direction = dir;
+ s->sample_spec = *ss;
+ if (attr)
+ s->buffer_attr = *attr;
+ else {
+ s->buffer_attr.maxlength = DEFAULT_MAXLENGTH;
+ s->buffer_attr.tlength = DEFAULT_TLENGTH;
+ s->buffer_attr.prebuf = DEFAULT_PREBUF;
+ s->buffer_attr.minreq = DEFAULT_MINREQ;
+ s->buffer_attr.fragsize = DEFAULT_FRAGSIZE;
+ }
+
+ s->next = c->first_stream;
+ if (s->next)
+ s->next->previous = s;
+ s->previous = NULL;
+ c->first_stream = s;
+
+ if (dev)
+ lookup_device(s, dev);
+ else
+ create_stream(s, (uint32_t) -1);
+
+ return s;
+}
+
+void pa_stream_free(struct pa_stream *s) {
+ assert(s && s->context);
+
+ if (s->context->pdispatch)
+ pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+
+ free(s->name);
+
+ if (s->channel_valid && s->context->state == CONTEXT_READY) {
+ struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+
+ pa_tagstruct_putu32(t, PA_COMMAND_DELETE_PLAYBACK_STREAM);
+ pa_tagstruct_putu32(t, s->context->ctag++);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ }
+
+ if (s->channel_valid)
+ pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+
+ if (s->next)
+ s->next->previous = s->previous;
+ if (s->previous)
+ s->previous->next = s->next;
+ else
+ s->context->first_stream = s->next;
+
+ free(s);
+}
+
+void pa_stream_set_write_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata) {
+ assert(s && cb);
+ s->write_callback = cb;
+ s->write_userdata = userdata;
+}
+
+void pa_stream_write(struct pa_stream *s, const void *data, size_t length) {
+ struct pa_memchunk chunk;
+ assert(s && s->context && data && length && s->state == STREAM_READY);
+
+ chunk.memblock = pa_memblock_new(length);
+ 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, 0, &chunk);
+ pa_memblock_unref(chunk.memblock);
+
+ /*fprintf(stderr, "Sent %u bytes\n", length);*/
+
+ if (length < s->requested_bytes)
+ s->requested_bytes -= length;
+ else
+ s->requested_bytes = 0;
+}
+
+size_t pa_stream_writable_size(struct pa_stream *s) {
+ assert(s && s->state == STREAM_READY);
+ return s->requested_bytes;
+}
+
+void pa_stream_set_read_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) {
+ assert(s && cb);
+ s->read_callback = cb;
+ s->read_userdata = userdata;
+}
+
+int pa_stream_is_dead(struct pa_stream *s) {
+ return s->state == STREAM_DEAD;
+}
+
+int pa_stream_is_ready(struct pa_stream*s) {
+ return s->state == STREAM_READY;
+}
+
+void pa_stream_set_die_callback(struct pa_stream *s, void (*cb)(struct pa_stream *s, void *userdata), void *userdata) {
+ assert(s);
+ s->die_callback = cb;
+ s->die_userdata = userdata;
+}
+
+int pa_context_is_pending(struct pa_context *c) {
+ assert(c);
+
+ if (c->state != CONTEXT_READY)
+ return 0;
+
+ return pa_pstream_is_pending(c->pstream) || pa_pdispatch_is_pending(c->pdispatch);
+}
+
+struct pa_context* pa_stream_get_context(struct pa_stream *p) {
+ assert(p);
+ return p->context;
+}
+
+static void set_dispatch_callbacks(struct pa_context *c);
+
+static void pdispatch_drain_callback(struct pa_pdispatch*pd, void *userdata) {
+ set_dispatch_callbacks(userdata);
+}
+
+static void pstream_drain_callback(struct pa_pstream *s, void *userdata) {
+ set_dispatch_callbacks(userdata);
+}
+
+static void set_dispatch_callbacks(struct pa_context *c) {
+ assert(c && c->state == CONTEXT_READY);
+
+ pa_pstream_set_drain_callback(c->pstream, NULL, NULL);
+ pa_pdispatch_set_drain_callback(c->pdispatch, NULL, NULL);
+
+ if (pa_pdispatch_is_pending(c->pdispatch)) {
+ pa_pdispatch_set_drain_callback(c->pdispatch, pdispatch_drain_callback, c);
+ return;
+ }
+
+ if (pa_pstream_is_pending(c->pstream)) {
+ pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c);
+ return;
+ }
+
+ assert(c->drain_complete_callback);
+ c->drain_complete_callback(c, c->drain_complete_userdata);
+}
+
+int pa_context_drain(
+ struct pa_context *c,
+ void (*complete) (struct pa_context*c, void *userdata),
+ void *userdata) {
+
+ assert(c && c->state == CONTEXT_READY);
+
+ if (complete == NULL) {
+ c->drain_complete_callback = NULL;
+ pa_pstream_set_drain_callback(c->pstream, NULL, NULL);
+ pa_pdispatch_set_drain_callback(c->pdispatch, NULL, NULL);
+ return 0;
+ }
+
+ if (!pa_context_is_pending(c))
+ return -1;
+
+ c->drain_complete_callback = complete;
+ c->drain_complete_userdata = userdata;
+
+ set_dispatch_callbacks(c);
+
+ return 0;
+}
+
+static void stream_drain_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct pa_stream *s = userdata;
+ assert(pd && s);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (handle_error(s->context, command, t) < 0) {
+ context_dead(s->context);
+ return;
+ }
+
+ stream_dead(s);
+ return;
+ }
+
+ if (s->state != STREAM_READY)
+ return;
+
+ if (!pa_tagstruct_eof(t)) {
+ s->context->error = PA_ERROR_PROTOCOL;
+ context_dead(s->context);
+ return;
+ }
+
+ if (s->drain_complete_callback) {
+ void (*temp) (struct pa_stream*s, void *userdata) = s->drain_complete_callback;
+ s->drain_complete_callback = NULL;
+ temp(s, s->drain_complete_userdata);
+ }
+}
+
+
+void pa_stream_drain(struct pa_stream *s, void (*complete) (struct pa_stream*s, void *userdata), void *userdata) {
+ struct pa_tagstruct *t;
+ uint32_t tag;
+ assert(s && s->state == STREAM_READY);
+
+ if (!complete) {
+ s->drain_complete_callback = NULL;
+ return;
+ }
+
+ s->drain_complete_callback = complete;
+ s->drain_complete_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, stream_drain_callback, s);
+}
+
+void pa_context_exit(struct pa_context *c) {
+ struct pa_tagstruct *t;
+ 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);
+}
+
+static void context_stat_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct pa_context *c = userdata;
+ uint32_t total, count;
+ assert(pd && c);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (handle_error(c, command, t) < 0) {
+ context_dead(c);
+ return;
+ }
+
+ if (c->stat_callback)
+ c->stat_callback(c, (uint32_t) -1, (uint32_t) -1, c->stat_userdata);
+ return;
+ }
+
+ if (pa_tagstruct_getu32(t, &count) < 0 ||
+ pa_tagstruct_getu32(t, &total) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ c->error = PA_ERROR_PROTOCOL;
+ context_dead(c);
+ return;
+ }
+
+ if (c->stat_callback)
+ c->stat_callback(c, count, total, c->stat_userdata);
+}
+
+void pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, uint32_t count, uint32_t total, void *userdata), void *userdata) {
+ uint32_t tag;
+ struct pa_tagstruct *t;
+
+ c->stat_callback = cb;
+ c->stat_userdata = userdata;
+
+ if (cb == NULL)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_STAT);
+ pa_tagstruct_putu32(t, tag = c->ctag++);
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_stat_callback, c);
+}
+
+static void stream_get_latency_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct pa_stream *s = userdata;
+ uint32_t latency;
+ assert(pd && s);
+
+ if (command != PA_COMMAND_REPLY) {
+ if (handle_error(s->context, command, t) < 0) {
+ context_dead(s->context);
+ return;
+ }
+
+ if (s->get_latency_callback)
+ s->get_latency_callback(s, (uint32_t) -1, s->get_latency_userdata);
+ return;
+ }
+
+ if (pa_tagstruct_getu32(t, &latency) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ s->context->error = PA_ERROR_PROTOCOL;
+ context_dead(s->context);
+ return;
+ }
+
+ if (s->get_latency_callback)
+ s->get_latency_callback(s, latency, s->get_latency_userdata);
+}
+
+void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p, uint32_t latency, void *userdata), void *userdata) {
+ uint32_t tag;
+ struct pa_tagstruct *t;
+
+ p->get_latency_callback = cb;
+ p->get_latency_userdata = userdata;
+
+ if (cb == NULL)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
+ pa_tagstruct_putu32(t, tag = p->context->ctag++);
+ pa_tagstruct_putu32(t, p->channel);
+ pa_pstream_send_tagstruct(p->context->pstream, t);
+ pa_pdispatch_register_reply(p->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, p);
+}
diff --git a/polyp/polyplib.h b/polyp/polyplib.h
new file mode 100644
index 00000000..440b9658
--- /dev/null
+++ b/polyp/polyplib.h
@@ -0,0 +1,94 @@
+#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 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 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 "mainloop-api.h"
+
+struct pa_context;
+
+struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name);
+
+int pa_context_connect(
+ struct pa_context *c,
+ const char *server,
+ void (*complete) (struct pa_context*c, int success, void *userdata),
+ void *userdata);
+
+int pa_context_drain(
+ struct pa_context *c,
+ void (*complete) (struct pa_context*c, void *userdata),
+ void *userdata);
+
+void pa_context_free(struct pa_context *c);
+
+void pa_context_set_die_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata);
+
+int pa_context_is_dead(struct pa_context *c);
+int pa_context_is_ready(struct pa_context *c);
+int pa_context_errno(struct pa_context *c);
+
+int pa_context_is_pending(struct pa_context *c);
+
+void pa_context_exit(struct pa_context *c);
+void pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, uint32_t count, uint32_t total, void *userdata), void *userdata);
+
+struct pa_stream;
+
+struct pa_stream* pa_stream_new(
+ struct pa_context *c,
+ enum pa_stream_direction dir,
+ const char *dev,
+ const char *name,
+ const struct pa_sample_spec *ss,
+ const struct pa_buffer_attr *attr,
+ void (*complete) (struct pa_stream*s, int success, void *userdata),
+ void *userdata);
+
+void pa_stream_free(struct pa_stream *p);
+
+void pa_stream_drain(
+ struct pa_stream *s,
+ void (*complete) (struct pa_stream*s, void *userdata),
+ void *userdata);
+
+
+void pa_stream_set_die_callback(struct pa_stream *s, void (*cb)(struct pa_stream *s, void *userdata), void *userdata);
+
+void pa_stream_set_write_callback(struct pa_stream *p, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata);
+void pa_stream_write(struct pa_stream *p, const void *data, size_t length);
+size_t pa_stream_writable_size(struct pa_stream *p);
+
+void pa_stream_set_read_callback(struct pa_stream *p, void (*cb)(struct pa_stream *p, const void*data, size_t length, void *userdata), void *userdata);
+
+int pa_stream_is_dead(struct pa_stream *p);
+int pa_stream_is_ready(struct pa_stream*p);
+
+void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p, uint32_t latency, void *userdata), void *userdata);
+
+struct pa_context* pa_stream_get_context(struct pa_stream *p);
+
+#endif
diff --git a/polyp/protocol-cli.c b/polyp/protocol-cli.c
new file mode 100644
index 00000000..d6e69b54
--- /dev/null
+++ b/polyp/protocol-cli.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 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 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"
+
+struct pa_protocol_cli {
+ struct pa_module *module;
+ struct pa_core *core;
+ struct pa_socket_server*server;
+ struct pa_idxset *connections;
+};
+
+static void cli_eof_cb(struct pa_cli*c, void*userdata) {
+ struct pa_protocol_cli *p = userdata;
+ assert(p);
+ pa_idxset_remove_by_data(p->connections, c, NULL);
+ pa_cli_free(c);
+}
+
+static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
+ struct pa_protocol_cli *p = userdata;
+ struct pa_cli *c;
+ assert(s && io && p);
+
+ 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);
+}
+
+struct pa_protocol_cli* pa_protocol_cli_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
+ struct pa_protocol_cli* p;
+ assert(core && server);
+
+ p = malloc(sizeof(struct pa_protocol_cli));
+ assert(p);
+ 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, void *userdata) {
+ assert(p);
+ pa_cli_free(p);
+}
+
+void pa_protocol_cli_free(struct pa_protocol_cli *p) {
+ assert(p);
+
+ pa_idxset_free(p->connections, free_connection, NULL);
+ pa_socket_server_free(p->server);
+ free(p);
+}
diff --git a/polyp/protocol-cli.h b/polyp/protocol-cli.h
new file mode 100644
index 00000000..7ad2db75
--- /dev/null
+++ b/polyp/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 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 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"
+
+struct pa_protocol_cli;
+
+struct pa_protocol_cli* pa_protocol_cli_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma);
+void pa_protocol_cli_free(struct pa_protocol_cli *n);
+
+#endif
diff --git a/polyp/protocol-esound.c b/polyp/protocol-esound.c
new file mode 100644
index 00000000..8a7c4bcb
--- /dev/null
+++ b/polyp/protocol-esound.c
@@ -0,0 +1,847 @@
+/* $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 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 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 "sample.h"
+
+#include "authkey.h"
+
+#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)
+
+/* This is heavily based on esound's code */
+
+struct connection {
+ uint32_t index;
+ struct pa_protocol_esound *protocol;
+ struct pa_iochannel *io;
+ struct 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;
+ struct pa_sink_input *sink_input;
+ struct pa_source_output *source_output;
+ struct pa_memblockq *input_memblockq, *output_memblockq;
+ void *fixed_source;
+ struct {
+ struct pa_memblock *current_memblock;
+ size_t memblock_index, fragment_size;
+ } playback;
+};
+
+struct pa_protocol_esound {
+ int public;
+ struct pa_module *module;
+ struct pa_core *core;
+ struct pa_socket_server *server;
+ struct pa_idxset *connections;
+ uint32_t sink_index, source_index;
+ 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(struct pa_sink_input *i, size_t length);
+static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk);
+static void sink_input_kill_cb(struct pa_sink_input *i);
+static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i);
+
+static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk);
+static void source_output_kill_cb(struct 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);
+
+/* 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), NULL, "sample cache" },
+ { sizeof(int), NULL, "sample free" },
+ { sizeof(int), NULL, "sample play" },
+ { sizeof(int), NULL, "sample loop" },
+ { sizeof(int), NULL, "sample stop" },
+ { -1, NULL, "TODO: sample kill" },
+
+ { ESD_KEY_LEN + sizeof(int), NULL, "standby" },
+ { ESD_KEY_LEN + sizeof(int), NULL, "resume" },
+
+ { ESD_NAME_MAX, NULL, "sample getid" },
+ { 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_free(c->sink_input);
+ if (c->source_output)
+ pa_source_output_free(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);
+
+ free(c->read_data);
+ free(c->write_data);
+
+ pa_iochannel_free(c->io);
+
+ if (c->fixed_source)
+ c->protocol->core->mainloop->cancel_fixed(c->protocol->core->mainloop, c->fixed_source);
+
+ free(c);
+}
+
+static struct pa_sink* get_output_sink(struct pa_protocol_esound *p) {
+ struct pa_sink *s;
+ assert(p);
+
+ if (!(s = pa_idxset_get_by_index(p->core->sinks, p->sink_index)))
+ s = pa_sink_get_default(p->core);
+
+ p->sink_index = s ? s->index : PA_IDXSET_INVALID;
+ return s;
+}
+
+static struct pa_source* get_input_source(struct pa_protocol_esound *p) {
+ struct pa_source *s;
+ assert(p);
+
+ if (!(s = pa_idxset_get_by_index(p->core->sources, p->sink_index)))
+ s = pa_source_get_default(p->core);
+
+ p->source_index = s ? s->index : PA_IDXSET_INVALID;
+ return s;
+}
+
+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->enable_fixed);
+ c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1);
+
+ t = c->write_data_length+length;
+
+ if (c->write_data_alloc < t)
+ c->write_data = realloc(c->write_data, c->write_data_alloc = t);
+
+ assert(c->write_data);
+
+ i = c->write_data_length;
+ c->write_data_length += length;
+
+ return c->write_data+i;
+}
+
+/*** esound commands ***/
+
+static int esd_proto_connect(struct connection *c, 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) {
+ fprintf(stderr, __FILE__": kicked client with invalid authorization key.\n");
+ return -1;
+ }
+
+ c->authorized = 1;
+ }
+
+ ekey = *(uint32_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 {
+ fprintf(stderr, __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, esd_proto_t request, const void *data, size_t length) {
+ char name[ESD_NAME_MAX];
+ int format, rate;
+ struct pa_sink *sink;
+ struct pa_sample_spec ss;
+ size_t l;
+ assert(c && length == (sizeof(int)*2+ESD_NAME_MAX));
+
+ format = maybe_swap_endian_32(c->swap_byte_order, *(int*)data);
+ rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1));
+
+ ss.rate = rate;
+ ss.channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
+ ss.format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
+
+ if (!pa_sample_spec_valid(&ss))
+ return -1;
+
+ if (!(sink = get_output_sink(c->protocol)))
+ return -1;
+
+ strncpy(name, data + sizeof(int)*2, sizeof(name));
+ name[sizeof(name)-1] = 0;
+
+ pa_client_rename(c->client, name);
+
+ assert(!c->input_memblockq);
+
+ l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
+ c->input_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS);
+ assert(c->input_memblockq);
+ pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
+ c->playback.fragment_size = l/10;
+
+ assert(!c->sink_input);
+ c->sink_input = pa_sink_input_new(sink, name, &ss);
+ assert(c->sink_input);
+
+ 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;
+ struct pa_source *source;
+ struct pa_sample_spec ss;
+ size_t l;
+ assert(c && length == (sizeof(int)*2+ESD_NAME_MAX));
+
+ format = maybe_swap_endian_32(c->swap_byte_order, *(int*)data);
+ rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1));
+
+ ss.rate = rate;
+ ss.channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
+ ss.format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
+
+ if (!pa_sample_spec_valid(&ss))
+ return -1;
+
+ if (request == ESD_PROTO_STREAM_MON) {
+ struct pa_sink* sink;
+
+ if (!(sink = get_output_sink(c->protocol)))
+ return -1;
+
+ if (!(source = sink->monitor_source))
+ return -1;
+ } else {
+ assert(request == ESD_PROTO_STREAM_REC);
+
+ if (!(source = get_input_source(c->protocol)))
+ return -1;
+ }
+
+ strncpy(name, data + sizeof(int)*2, sizeof(name));
+ name[sizeof(name)-1] = 0;
+
+ pa_client_rename(c->client, name);
+
+ assert(!c->output_memblockq);
+
+ l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
+ c->output_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&ss), 0, 0);
+ assert(c->output_memblockq);
+ pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
+
+ assert(!c->source_output);
+ c->source_output = pa_source_output_new(source, name, &ss);
+ assert(c->source_output);
+
+ 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->userdata = c;
+
+ c->state = ESD_STREAMING_DATA;
+
+ c->protocol->n_player++;
+
+ return 0;
+}
+
+static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+ struct pa_sink *sink;
+ int latency, *lag;
+ assert(c && !data && length == 0);
+
+ if (!(sink = get_output_sink(c->protocol)))
+ latency = 0;
+ else {
+ float usec = pa_sink_get_latency(sink);
+ usec += PLAYBACK_BUFFER_SECONDS*1000000; /* A better estimation would be a good idea! */
+ latency = (int) ((usec*44100)/1000000);
+ }
+
+ lag = connection_write(c, sizeof(int));
+ assert(lag);
+ *lag = c->swap_byte_order ? swap_endian_32(latency) : latency;
+ return 0;
+}
+
+static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+ int rate = 44100, format = ESD_STEREO|ESD_BITS16;
+ int *response;
+ struct pa_sink *sink;
+ assert(c && data && length == sizeof(int));
+
+ if ((sink = get_output_sink(c->protocol))) {
+ rate = sink->sample_spec.rate;
+ format = (sink->sample_spec.format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
+ format |= (sink->sample_spec.channels >= 2) ? ESD_STEREO : ESD_MONO;
+ }
+
+ response = connection_write(c, sizeof(int)*3);
+ assert(response);
+ *(response++) = 0;
+ *(response++) = maybe_swap_endian_32(c->swap_byte_order, rate);
+ *(response++) = maybe_swap_endian_32(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) {
+ void *response;
+ size_t t, k, s;
+ struct connection *conn;
+ size_t index = PA_IDXSET_INVALID;
+ 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;
+ response = connection_write(c, (t = s+k*(c->protocol->n_player+1)));
+ assert(k);
+
+ for (conn = pa_idxset_first(c->protocol->connections, &index); conn; conn = pa_idxset_next(c->protocol->connections, &index)) {
+ int format = ESD_BITS16 | ESD_STEREO, rate = 44100, volume = 0xFF;
+
+ if (conn->state != ESD_STREAMING_DATA)
+ continue;
+
+ assert(t >= s+k+k);
+
+ if (conn->sink_input) {
+ rate = conn->sink_input->sample_spec.rate;
+ volume = (conn->sink_input->volume*0xFF)/0x100;
+ format = (conn->sink_input->sample_spec.format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
+ format |= (conn->sink_input->sample_spec.channels >= 2) ? ESD_STEREO : ESD_MONO;
+ }
+
+ /* id */
+ *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, (int) conn->index);
+ response += sizeof(int);
+
+ /* name */
+ assert(conn->client);
+ strncpy(response, conn->client->name, ESD_NAME_MAX);
+ response += ESD_NAME_MAX;
+
+ /* rate */
+ *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, rate);
+ response += sizeof(int);
+
+ /* left */
+ *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, volume);
+ response += sizeof(int);
+
+ /*right*/
+ *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, volume);
+ response += sizeof(int);
+
+ /*format*/
+ *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, format);
+ response += sizeof(int);
+
+ t-= k;
+ }
+
+ assert(t == s+k);
+ memset(response, 0, t);
+ return 0;
+}
+
+static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+ int *ok;
+ uint32_t index, volume;
+ struct connection *conn;
+ assert(c && data && length == sizeof(int)*3);
+
+ index = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *(int*)data);
+ volume = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1));
+ volume = (volume*0x100)/0xFF;
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+
+ if ((conn = pa_idxset_get_by_index(c->protocol->connections, index))) {
+ assert(conn->sink_input);
+ conn->sink_input->volume = volume;
+ *ok = 1;
+ } else
+ *ok = 0;
+
+ return 0;
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(struct 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);
+
+ if (c->state == ESD_NEXT_REQUEST) {
+ ssize_t r;
+ assert(c->read_data_length < sizeof(c->request));
+
+ if ((r = pa_iochannel_read(c->io, ((void*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
+ fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
+ return -1;
+ }
+
+ if ((c->read_data_length+= r) >= sizeof(c->request)) {
+ struct proto_handler *handler;
+
+ if (c->swap_byte_order)
+ c->request = swap_endian_32(c->request);
+
+ if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
+ fprintf(stderr, "protocol-esound.c: recieved invalid request.\n");
+ return -1;
+ }
+
+ handler = proto_map+c->request;
+
+ if (!handler->proc) {
+ fprintf(stderr, "protocol-sound.c: recieved unimplemented request.\n");
+ 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 = realloc(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, c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
+ fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : 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_STREAMING_DATA && c->sink_input) {
+ struct pa_memchunk chunk;
+ ssize_t r;
+ size_t l;
+
+ assert(c->input_memblockq);
+
+ 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);
+ assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
+ c->playback.memblock_index = 0;
+ }
+
+ if ((r = pa_iochannel_read(c->io, c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
+ fprintf(stderr, __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) {
+ assert(c && c->io);
+
+ if (c->write_data_length) {
+ ssize_t r;
+
+ assert(c->write_data_index < c->write_data_length);
+ if ((r = pa_iochannel_write(c->io, c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
+ fprintf(stderr, __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) {
+ struct 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, chunk.memblock->data+chunk.index, chunk.length)) < 0) {
+ pa_memblock_unref(chunk.memblock);
+ fprintf(stderr, __FILE__": write(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ pa_memblockq_drop(c->output_memblockq, 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->enable_fixed);
+ c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0);
+
+ if (pa_iochannel_is_hungup(c->io))
+ goto fail;
+
+ 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;
+
+ return;
+
+fail:
+ connection_free(c);
+}
+
+static void io_callback(struct pa_iochannel*io, void *userdata) {
+ struct connection *c = userdata;
+ assert(io && c && c->io == io);
+
+ do_work(c);
+}
+
+/*** fixed callback ***/
+
+static void fixed_callback(struct pa_mainloop_api*a, void *id, void *userdata) {
+ struct connection *c = userdata;
+ assert(a && c && c->fixed_source == id);
+
+ do_work(c);
+}
+
+/*** sink_input callbacks ***/
+
+static int sink_input_peek_cb(struct pa_sink_input *i, struct 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(struct pa_sink_input *i, size_t length) {
+ struct connection*c = i->userdata;
+ assert(i && c && length);
+
+ pa_memblockq_drop(c->input_memblockq, length);
+
+ /* do something */
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed);
+ c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1);
+}
+
+static void sink_input_kill_cb(struct pa_sink_input *i) {
+ assert(i && i->userdata);
+ connection_free((struct connection *) i->userdata);
+}
+
+
+static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) {
+ struct connection*c = i->userdata;
+ assert(i && c);
+ return pa_samples_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+}
+
+/*** source_output callbacks ***/
+
+static void source_output_push_cb(struct pa_source_output *o, const struct 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->enable_fixed);
+ c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1);
+}
+
+static void source_output_kill_cb(struct pa_source_output *o) {
+ assert(o && o->userdata);
+ connection_free((struct connection *) o->userdata);
+}
+
+/*** socket server callback ***/
+
+static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
+ struct connection *c;
+ char cname[256];
+ assert(s && io && userdata);
+
+ c = malloc(sizeof(struct connection));
+ assert(c);
+ c->protocol = userdata;
+ c->io = io;
+ pa_iochannel_set_callback(c->io, io_callback, c);
+
+ pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
+ assert(c->protocol->core);
+ c->client = pa_client_new(c->protocol->core, "ESOUND", cname);
+ assert(c->client);
+ c->client->owner = c->protocol->module;
+ c->client->kill = client_kill_cb;
+ c->client->userdata = c;
+
+ c->authorized = c->protocol->public;
+ c->swap_byte_order = 0;
+
+ c->read_data_length = 0;
+ c->read_data = malloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
+ assert(c->read_data);
+
+ 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->fixed_source = c->protocol->core->mainloop->source_fixed(c->protocol->core->mainloop, fixed_callback, c);
+ assert(c->fixed_source);
+ c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0);
+
+ pa_idxset_put(c->protocol->connections, c, &c->index);
+}
+
+/*** entry points ***/
+
+struct pa_protocol_esound* pa_protocol_esound_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
+ uint32_t source_index, sink_index;
+ struct pa_protocol_esound *p;
+ assert(core && server && ma);
+
+ if (pa_modargs_get_source_index(ma, core, &source_index) < 0) {
+ fprintf(stderr, __FILE__": source does not exist.\n");
+ return NULL;
+ }
+
+ if (pa_modargs_get_sink_index(ma, core, &sink_index) < 0) {
+ fprintf(stderr, __FILE__": sink does not exist.\n");
+ return NULL;
+ }
+ p = malloc(sizeof(struct pa_protocol_esound));
+ assert(p);
+
+ if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0) {
+ free(p);
+ return NULL;
+ }
+
+ p->module = m;
+ p->public = 0;
+ 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_index = sink_index;
+ p->source_index = source_index;
+ p->n_player = 0;
+
+ return p;
+}
+
+void pa_protocol_esound_free(struct 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_free(p->server);
+ free(p);
+}
diff --git a/polyp/protocol-esound.h b/polyp/protocol-esound.h
new file mode 100644
index 00000000..b2bdd31b
--- /dev/null
+++ b/polyp/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 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 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"
+
+struct pa_protocol_esound;
+
+struct pa_protocol_esound* pa_protocol_esound_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma);
+void pa_protocol_esound_free(struct pa_protocol_esound *p);
+
+#endif
diff --git a/polyp/protocol-native.c b/polyp/protocol-native.c
new file mode 100644
index 00000000..83c910d1
--- /dev/null
+++ b/polyp/protocol-native.c
@@ -0,0 +1,863 @@
+/* $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 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 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"
+
+struct connection;
+struct pa_protocol_native;
+
+struct record_stream {
+ struct connection *connection;
+ uint32_t index;
+ struct pa_source_output *source_output;
+ struct pa_memblockq *memblockq;
+ size_t fragment_size;
+};
+
+struct playback_stream {
+ struct connection *connection;
+ uint32_t index;
+ struct pa_sink_input *sink_input;
+ struct pa_memblockq *memblockq;
+ size_t requested_bytes;
+ int drain_request;
+ uint32_t drain_tag;
+};
+
+struct connection {
+ int authorized;
+ struct pa_protocol_native *protocol;
+ struct pa_client *client;
+ struct pa_pstream *pstream;
+ struct pa_pdispatch *pdispatch;
+ struct pa_idxset *record_streams, *playback_streams;
+ uint32_t rrobin_index;
+};
+
+struct pa_protocol_native {
+ struct pa_module *module;
+ int public;
+ struct pa_core *core;
+ struct pa_socket_server *server;
+ struct pa_idxset *connections;
+ uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
+};
+
+static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk);
+static void sink_input_drop_cb(struct pa_sink_input *i, size_t length);
+static void sink_input_kill_cb(struct pa_sink_input *i);
+static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i);
+
+static void request_bytes(struct playback_stream*s);
+
+static void source_output_kill_cb(struct pa_source_output *o);
+static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk);
+
+static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+
+static const struct pa_pdispatch_command 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_playback_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_record_stream },
+ [PA_COMMAND_AUTH] = { command_auth },
+ [PA_COMMAND_REQUEST] = { NULL },
+ [PA_COMMAND_EXIT] = { command_exit },
+ [PA_COMMAND_SET_NAME] = { command_set_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 },
+};
+
+/* structure management */
+
+static struct record_stream* record_stream_new(struct connection *c, struct pa_source *source, struct pa_sample_spec *ss, const char *name, size_t maxlength, size_t fragment_size) {
+ struct record_stream *s;
+ struct pa_source_output *source_output;
+ size_t base;
+ assert(c && source && ss && name && maxlength);
+
+ if (!(source_output = pa_source_output_new(source, name, ss)))
+ return NULL;
+
+ s = malloc(sizeof(struct record_stream));
+ assert(s);
+ 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->userdata = s;
+ s->source_output->owner = c->protocol->module;
+ s->source_output->client = c->client;
+
+ s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_sample_size(ss), 0, 0);
+ 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_free(r->source_output);
+ pa_memblockq_free(r->memblockq);
+ free(r);
+}
+
+static struct playback_stream* playback_stream_new(struct connection *c, struct pa_sink *sink, struct pa_sample_spec *ss, const char *name,
+ size_t maxlength,
+ size_t tlength,
+ size_t prebuf,
+ size_t minreq) {
+ struct playback_stream *s;
+ struct pa_sink_input *sink_input;
+ assert(c && sink && ss && name && maxlength);
+
+ if (!(sink_input = pa_sink_input_new(sink, name, ss)))
+ return NULL;
+
+ s = malloc(sizeof(struct playback_stream));
+ assert (s);
+ 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_sample_size(ss), prebuf, minreq);
+ assert(s->memblockq);
+
+ s->requested_bytes = 0;
+ s->drain_request = 0;
+
+ pa_idxset_put(c->playback_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->playback_streams, p, NULL);
+ pa_sink_input_free(p->sink_input);
+ pa_memblockq_free(p->memblockq);
+ free(p);
+}
+
+static void connection_free(struct connection *c) {
+ struct record_stream *r;
+ struct playback_stream *p;
+ 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 ((p = pa_idxset_first(c->playback_streams, NULL)))
+ playback_stream_free(p);
+ pa_idxset_free(c->playback_streams, NULL, NULL);
+
+ pa_pdispatch_free(c->pdispatch);
+ pa_pstream_free(c->pstream);
+ pa_client_free(c->client);
+ free(c);
+}
+
+static void request_bytes(struct playback_stream *s) {
+ struct 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);
+
+ /*fprintf(stderr, "Requesting %u bytes\n", l);*/
+}
+
+static void send_memblock(struct connection *c) {
+ uint32_t start;
+ struct record_stream *r;
+
+ start = PA_IDXSET_INVALID;
+ for (;;) {
+ struct 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) {
+ if (chunk.length > r->fragment_size)
+ chunk.length = r->fragment_size;
+
+ pa_pstream_send_memblock(c->pstream, r->index, 0, &chunk);
+ pa_memblockq_drop(r->memblockq, chunk.length);
+ pa_memblock_unref(chunk.memblock);
+
+ return;
+ }
+ }
+}
+
+static void send_playback_stream_killed(struct playback_stream *p) {
+ struct 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) {
+ struct 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(struct pa_sink_input *i, struct 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(struct pa_sink_input *i, size_t length) {
+ struct playback_stream *s;
+ assert(i && i->userdata && length);
+ s = i->userdata;
+
+ pa_memblockq_drop(s->memblockq, 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;
+ }
+}
+
+static void sink_input_kill_cb(struct 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 uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) {
+ struct playback_stream *s;
+ assert(i && i->userdata);
+ s = i->userdata;
+
+ return pa_samples_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
+}
+
+/*** source_output callbacks ***/
+
+static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk) {
+ struct record_stream *s;
+ assert(o && o->userdata && chunk);
+ s = o->userdata;
+
+ pa_memblockq_push(s->memblockq, chunk, 0);
+ if (!pa_pstream_is_pending(s->connection->pstream))
+ send_memblock(s->connection);
+}
+
+static void source_output_kill_cb(struct 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);
+}
+
+/*** pdispatch callbacks ***/
+
+static void protocol_error(struct connection *c) {
+ fprintf(stderr, __FILE__": protocol error, kicking client\n");
+ connection_free(c);
+}
+
+static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct 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;
+ struct pa_sample_spec ss;
+ struct pa_tagstruct *reply;
+ struct pa_sink *sink;
+ assert(c && t && c->protocol && c->protocol->core);
+
+ if (pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_getu32(t, &sink_index) < 0 ||
+ pa_tagstruct_getu32(t, &maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &tlength) < 0 ||
+ pa_tagstruct_getu32(t, &prebuf) < 0 ||
+ pa_tagstruct_getu32(t, &minreq) < 0 ||
+ !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_sink_get_default(c->protocol->core);
+ else
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
+
+ if (!sink) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ if (!(s = playback_stream_new(c, sink, &ss, name, maxlength, tlength, prebuf, minreq))) {
+ 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);
+ assert(s->sink_input);
+ pa_tagstruct_putu32(reply, s->sink_input->index);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+ request_bytes(s);
+}
+
+static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t channel;
+ struct playback_stream *s;
+ 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->playback_streams, channel))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
+ return;
+ }
+
+ playback_stream_free(s);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct 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;
+ struct pa_sample_spec ss;
+ struct pa_tagstruct *reply;
+ struct pa_source *source;
+ assert(c && t && c->protocol && c->protocol->core);
+
+ if (pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+ pa_tagstruct_getu32(t, &source_index) < 0 ||
+ pa_tagstruct_getu32(t, &maxlength) < 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_source_get_default(c->protocol->core);
+ else
+ source = pa_idxset_get_by_index(c->protocol->core->sources, source_index);
+
+ if (!source) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ if (!(s = record_stream_new(c, source, &ss, name, maxlength, fragment_size))) {
+ 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);
+ assert(s->source_output);
+ pa_tagstruct_putu32(reply, s->source_output->index);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t channel;
+ struct record_stream *s;
+ 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->record_streams, channel))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
+ return;
+ }
+
+ record_stream_free(s);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct 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(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct 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 (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) {
+ fprintf(stderr, "protocol-native.c: Denied access to client with invalid authorization key.\n");
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
+ return;
+ }
+
+ c->authorized = 1;
+ pa_pstream_send_simple_ack(c->pstream, tag);
+ return;
+}
+
+static void command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ const char *name;
+ assert(c && t);
+
+ if (pa_tagstruct_gets(t, &name) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ pa_client_rename(c->client, name);
+ pa_pstream_send_simple_ack(c->pstream, tag);
+ return;
+}
+
+static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ const char *name;
+ uint32_t index = PA_IDXSET_INVALID;
+ assert(c && t);
+
+ if (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_LOOKUP_SINK) {
+ struct pa_sink *sink;
+ if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK)))
+ index = sink->index;
+ } else {
+ struct pa_source *source;
+ assert(command == PA_COMMAND_LOOKUP_SOURCE);
+ if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE)))
+ index = source->index;
+ }
+
+ if (index == PA_IDXSET_INVALID)
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ else {
+ struct 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, index);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+ }
+}
+
+static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ uint32_t index;
+ struct playback_stream *s;
+ assert(c && t);
+
+ if (pa_tagstruct_getu32(t, &index) < 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->playback_streams, index))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ s->drain_request = 0;
+
+ if (!pa_memblockq_is_readable(s->memblockq))
+ pa_pstream_send_simple_ack(c->pstream, tag);
+ else {
+ s->drain_request = 1;
+ s->drain_tag = tag;
+ }
+}
+
+static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ assert(c && t);
+ struct pa_tagstruct *reply;
+
+ 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, pa_memblock_get_count());
+ pa_tagstruct_putu32(reply, pa_memblock_get_total());
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct connection *c = userdata;
+ assert(c && t);
+ struct pa_tagstruct *reply;
+ struct playback_stream *s;
+ uint32_t index, latency;
+
+ if (pa_tagstruct_getu32(t, &index) < 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->playback_streams, index))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
+ return;
+ }
+
+ latency = pa_sink_input_get_latency(s->sink_input);
+ reply = pa_tagstruct_new(NULL, 0);
+ assert(reply);
+ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+ pa_tagstruct_putu32(reply, tag);
+ pa_tagstruct_putu32(reply, latency);
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+/*** pstream callbacks ***/
+
+static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) {
+ struct connection *c = userdata;
+ assert(p && packet && packet->data && c);
+
+ if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) {
+ fprintf(stderr, "protocol-native: invalid packet.\n");
+ connection_free(c);
+ }
+}
+
+static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata) {
+ struct connection *c = userdata;
+ struct playback_stream *stream;
+ assert(p && chunk && userdata);
+
+ if (!(stream = pa_idxset_get_by_index(c->playback_streams, channel))) {
+ fprintf(stderr, "protocol-native: client sent block for invalid stream.\n");
+ connection_free(c);
+ return;
+ }
+
+ if (chunk->length >= stream->requested_bytes)
+ stream->requested_bytes = 0;
+ else
+ stream->requested_bytes -= chunk->length;
+
+ pa_memblockq_push_align(stream->memblockq, chunk, delta);
+ assert(stream->sink_input);
+ pa_sink_notify(stream->sink_input->sink);
+
+ /*fprintf(stderr, "Recieved %u bytes.\n", chunk->length);*/
+}
+
+static void pstream_die_callback(struct pa_pstream *p, void *userdata) {
+ struct connection *c = userdata;
+ assert(p && c);
+ connection_free(c);
+
+ fprintf(stderr, "protocol-native: connection died.\n");
+}
+
+
+static void pstream_drain_callback(struct pa_pstream *p, void *userdata) {
+ struct connection *c = userdata;
+ assert(p && c);
+
+ send_memblock(c);
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(struct pa_client *c) {
+ assert(c && c->userdata);
+ connection_free(c->userdata);
+}
+
+/*** socket server callbacks ***/
+
+static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
+ struct pa_protocol_native *p = userdata;
+ struct connection *c;
+ assert(s && io && p);
+
+ c = malloc(sizeof(struct connection));
+ assert(c);
+ c->authorized = p->public;
+ c->protocol = p;
+ assert(p->core);
+ c->client = pa_client_new(p->core, "NATIVE", "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);
+ 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->playback_streams = pa_idxset_new(NULL, NULL);
+ assert(c->record_streams && c->playback_streams);
+
+ c->rrobin_index = PA_IDXSET_INVALID;
+
+ pa_idxset_put(p->connections, c, NULL);
+}
+
+/*** module entry points ***/
+
+struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
+ struct pa_protocol_native *p;
+ uint32_t public;
+ assert(core && server && ma);
+
+ if (pa_modargs_get_value_u32(ma, "public", &public) < 0) {
+ fprintf(stderr, __FILE__": public= expects numeric argument.\n");
+ return NULL;
+ }
+
+ p = malloc(sizeof(struct pa_protocol_native));
+ assert(p);
+
+ if (pa_authkey_load_from_home(pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), p->auth_cookie, sizeof(p->auth_cookie)) < 0) {
+ free(p);
+ return NULL;
+ }
+
+ p->module = m;
+ p->public = public;
+ p->server = server;
+ p->core = core;
+ p->connections = pa_idxset_new(NULL, NULL);
+ assert(p->connections);
+
+ pa_socket_server_set_callback(p->server, on_connection, p);
+
+ return p;
+}
+
+void pa_protocol_native_free(struct 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);
+ pa_socket_server_free(p->server);
+ free(p);
+}
diff --git a/polyp/protocol-native.h b/polyp/protocol-native.h
new file mode 100644
index 00000000..3d9fdde1
--- /dev/null
+++ b/polyp/protocol-native.h
@@ -0,0 +1,35 @@
+#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 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 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"
+
+struct pa_protocol_native;
+
+struct pa_protocol_native* pa_protocol_native_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma);
+void pa_protocol_native_free(struct pa_protocol_native *n);
+
+#endif
diff --git a/polyp/protocol-simple.c b/polyp/protocol-simple.c
new file mode 100644
index 00000000..3a52e311
--- /dev/null
+++ b/polyp/protocol-simple.c
@@ -0,0 +1,444 @@
+/* $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 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 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"
+
+struct connection {
+ struct pa_protocol_simple *protocol;
+ struct pa_iochannel *io;
+ struct pa_sink_input *sink_input;
+ struct pa_source_output *source_output;
+ struct pa_client *client;
+ struct pa_memblockq *input_memblockq, *output_memblockq;
+ void *fixed_source;
+
+ struct {
+ struct pa_memblock *current_memblock;
+ size_t memblock_index, fragment_size;
+ } playback;
+};
+
+struct pa_protocol_simple {
+ struct pa_module *module;
+ struct pa_core *core;
+ struct pa_socket_server*server;
+ struct pa_idxset *connections;
+ enum {
+ RECORD = 1,
+ PLAYBACK = 2,
+ DUPLEX = 3
+ } mode;
+ struct pa_sample_spec sample_spec;
+ uint32_t sink_index, source_index;
+};
+
+#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_free(c->sink_input);
+ if (c->source_output)
+ pa_source_output_free(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->fixed_source)
+ c->protocol->core->mainloop->cancel_fixed(c->protocol->core->mainloop, c->fixed_source);
+ free(c);
+}
+
+static int do_read(struct connection *c) {
+ struct 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);
+ assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
+ c->playback.memblock_index = 0;
+ }
+
+ if ((r = pa_iochannel_read(c->io, c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
+ fprintf(stderr, __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) {
+ struct 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, chunk.memblock->data+chunk.index, chunk.length)) < 0) {
+ pa_memblock_unref(chunk.memblock);
+ fprintf(stderr, "write(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ pa_memblockq_drop(c->output_memblockq, 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->enable_fixed);
+ c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0);
+
+ if (pa_iochannel_is_hungup(c->io))
+ goto fail;
+
+ 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;
+
+ return;
+
+fail:
+ connection_free(c);
+}
+
+/*** sink_input callbacks ***/
+
+static int sink_input_peek_cb(struct pa_sink_input *i, struct 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(struct pa_sink_input *i, size_t length) {
+ struct connection*c = i->userdata;
+ assert(i && c && length);
+
+ pa_memblockq_drop(c->input_memblockq, length);
+
+ /* do something */
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed);
+ c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1);
+}
+
+static void sink_input_kill_cb(struct pa_sink_input *i) {
+ assert(i && i->userdata);
+ connection_free((struct connection *) i->userdata);
+}
+
+
+static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) {
+ struct connection*c = i->userdata;
+ assert(i && c);
+ return pa_samples_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+}
+
+/*** source_output callbacks ***/
+
+static void source_output_push_cb(struct pa_source_output *o, const struct 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->enable_fixed);
+ c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1);
+}
+
+static void source_output_kill_cb(struct pa_source_output *o) {
+ assert(o && o->userdata);
+ connection_free((struct connection *) o->userdata);
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(struct pa_client *c) {
+ assert(c && c->userdata);
+ connection_free((struct connection *) c->userdata);
+}
+
+/*** pa_iochannel callbacks ***/
+
+static void io_callback(struct pa_iochannel*io, void *userdata) {
+ struct connection *c = userdata;
+ assert(io && c && c->io == io);
+
+ do_work(c);
+}
+
+/*** fixed callback ***/
+
+static void fixed_callback(struct pa_mainloop_api*a, void *id, void *userdata) {
+ struct connection *c = userdata;
+ assert(a && c && c->fixed_source == id);
+
+ do_work(c);
+}
+
+/*** socket_server callbacks ***/
+
+static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
+ struct pa_protocol_simple *p = userdata;
+ struct connection *c = NULL;
+ char cname[256];
+ assert(s && io && p);
+
+ c = malloc(sizeof(struct connection));
+ assert(c);
+ c->io = io;
+ c->sink_input = NULL;
+ c->source_output = NULL;
+ c->fixed_source = 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, "SIMPLE", cname);
+ assert(c->client);
+ c->client->owner = p->module;
+ c->client->kill = client_kill_cb;
+ c->client->userdata = c;
+
+ if (p->mode & PLAYBACK) {
+ struct pa_sink *sink;
+ size_t l;
+
+ if (!(sink = pa_idxset_get_by_index(p->core->sinks, p->sink_index)))
+ if (!(sink = pa_sink_get_default(p->core))) {
+ fprintf(stderr, "Failed to get sink.\n");
+ goto fail;
+ }
+
+ c->sink_input = pa_sink_input_new(sink, c->client->name, &p->sample_spec);
+ if (!c->sink_input) {
+ fprintf(stderr, "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_sample_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS);
+ 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) {
+ struct pa_source *source;
+ size_t l;
+
+ if (!(source = pa_idxset_get_by_index(p->core->sources, p->source_index)))
+ if (!(source = pa_source_get_default(p->core))) {
+ fprintf(stderr, "Failed to get source.\n");
+ goto fail;
+ }
+
+ c->source_output = pa_source_output_new(source, c->client->name, &p->sample_spec);
+ if (!c->source_output) {
+ fprintf(stderr, "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->userdata = c;
+
+ l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
+ c->output_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&p->sample_spec), 0, 0);
+ 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->fixed_source = p->core->mainloop->source_fixed(p->core->mainloop, fixed_callback, c);
+ assert(c->fixed_source);
+ p->core->mainloop->enable_fixed(p->core->mainloop, c->fixed_source, 0);
+
+ return;
+
+fail:
+ if (c)
+ connection_free(c);
+}
+
+struct pa_protocol_simple* pa_protocol_simple_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
+ struct pa_protocol_simple* p = NULL;
+ uint32_t enable;
+ assert(core && server && ma);
+
+ p = malloc(sizeof(struct pa_protocol_simple));
+ assert(p);
+ memset(p, 0, sizeof(struct 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) {
+ fprintf(stderr, "Failed to parse sample type specification.\n");
+ goto fail;
+ }
+
+ if (pa_modargs_get_source_index(ma, core, &p->source_index) < 0) {
+ fprintf(stderr, __FILE__": source does not exist.\n");
+ goto fail;
+ }
+
+ if (pa_modargs_get_sink_index(ma, core, &p->sink_index) < 0) {
+ fprintf(stderr, __FILE__": sink does not exist.\n");
+ goto fail;
+ }
+
+ enable = 0;
+ if (pa_modargs_get_value_u32(ma, "record", &enable) < 0) {
+ fprintf(stderr, __FILE__": record= expects a numeric argument.\n");
+ goto fail;
+ }
+ p->mode = enable ? RECORD : 0;
+
+ enable = 1;
+ if (pa_modargs_get_value_u32(ma, "playback", &enable) < 0) {
+ fprintf(stderr, __FILE__": playback= expects a numeric argument.\n");
+ goto fail;
+ }
+ p->mode |= enable ? PLAYBACK : 0;
+
+ if ((p->mode & (RECORD|PLAYBACK)) == 0) {
+ fprintf(stderr, __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(struct 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_free(p->server);
+ free(p);
+}
+
diff --git a/polyp/protocol-simple.h b/polyp/protocol-simple.h
new file mode 100644
index 00000000..0fc1e19d
--- /dev/null
+++ b/polyp/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 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 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"
+
+struct pa_protocol_simple;
+
+struct pa_protocol_simple* pa_protocol_simple_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma);
+void pa_protocol_simple_free(struct pa_protocol_simple *n);
+
+#endif
diff --git a/polyp/pstream-util.c b/polyp/pstream-util.c
new file mode 100644
index 00000000..3957e643
--- /dev/null
+++ b/polyp/pstream-util.c
@@ -0,0 +1,60 @@
+/* $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 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 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(struct pa_pstream *p, struct pa_tagstruct *t) {
+ size_t length;
+ uint8_t *data;
+ struct pa_packet *packet;
+ assert(p && 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(struct pa_pstream *p, uint32_t tag, uint32_t error) {
+ struct 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(struct pa_pstream *p, uint32_t tag) {
+ struct 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/polyp/pstream-util.h b/polyp/pstream-util.h
new file mode 100644
index 00000000..b3c89eb0
--- /dev/null
+++ b/polyp/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 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 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(struct pa_pstream *p, struct pa_tagstruct *t);
+
+void pa_pstream_send_error(struct pa_pstream *p, uint32_t tag, uint32_t error);
+void pa_pstream_send_simple_ack(struct pa_pstream *p, uint32_t tag);
+
+#endif
diff --git a/polyp/pstream.c b/polyp/pstream.c
new file mode 100644
index 00000000..3076b776
--- /dev/null
+++ b/polyp/pstream.c
@@ -0,0 +1,457 @@
+/* $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 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 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 <netinet/in.h>
+
+#include "pstream.h"
+#include "queue.h"
+
+enum pa_pstream_descriptor_index {
+ PA_PSTREAM_DESCRIPTOR_LENGTH,
+ PA_PSTREAM_DESCRIPTOR_CHANNEL,
+ PA_PSTREAM_DESCRIPTOR_DELTA,
+ PA_PSTREAM_DESCRIPTOR_MAX
+};
+
+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*64)
+
+struct item_info {
+ enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type;
+
+ /* memblock info */
+ struct pa_memchunk chunk;
+ uint32_t channel;
+ int32_t delta;
+
+ /* packet info */
+ struct pa_packet *packet;
+};
+
+struct pa_pstream {
+ struct pa_mainloop_api *mainloop;
+ struct mainloop_source *mainloop_source;
+ struct pa_iochannel *io;
+ struct pa_queue *send_queue;
+
+ int in_use, shall_free;
+
+ int dead;
+ void (*die_callback) (struct 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 {
+ struct pa_memblock *memblock;
+ struct pa_packet *packet;
+ pa_pstream_descriptor descriptor;
+ void *data;
+ size_t index;
+ } read;
+
+ void (*recieve_packet_callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata);
+ void *recieve_packet_callback_userdata;
+
+ void (*recieve_memblock_callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata);
+ void *recieve_memblock_callback_userdata;
+
+ void (*drain_callback)(struct pa_pstream *p, void *userdata);
+ void *drain_userdata;
+};
+
+static void do_write(struct pa_pstream *p);
+static void do_read(struct pa_pstream *p);
+
+static void do_something(struct pa_pstream *p) {
+ assert(p && !p->shall_free);
+ p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 0);
+
+ if (p->dead)
+ return;
+
+ if (pa_iochannel_is_hungup(p->io)) {
+ p->dead = 1;
+ if (p->die_callback)
+ p->die_callback(p, p->die_callback_userdata);
+
+ return;
+ }
+
+ if (pa_iochannel_is_writable(p->io)) {
+ p->in_use = 1;
+ do_write(p);
+ p->in_use = 0;
+
+ if (p->shall_free) {
+ pa_pstream_free(p);
+ return;
+ }
+ }
+
+ if (pa_iochannel_is_readable(p->io)) {
+ p->in_use = 1;
+ do_read(p);
+ p->in_use = 0;
+ if (p->shall_free) {
+ pa_pstream_free(p);
+ return;
+ }
+ }
+}
+
+static void io_callback(struct pa_iochannel*io, void *userdata) {
+ struct pa_pstream *p = userdata;
+ assert(p && p->io == io);
+ do_something(p);
+}
+
+static void fixed_callback(struct pa_mainloop_api *m, void *id, void*userdata) {
+ struct pa_pstream *p = userdata;
+ assert(p && p->mainloop_source == id && p->mainloop == m);
+ do_something(p);
+}
+
+struct pa_pstream *pa_pstream_new(struct pa_mainloop_api *m, struct pa_iochannel *io) {
+ struct pa_pstream *p;
+ assert(io);
+
+ p = malloc(sizeof(struct pa_pstream));
+ assert(p);
+
+ 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->mainloop_source = m->source_fixed(m, fixed_callback, p);
+ m->enable_fixed(m, p->mainloop_source, 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->in_use = p->shall_free = 0;
+
+ return p;
+}
+
+static void item_free(void *item, 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);
+ }
+
+ free(i);
+}
+
+void pa_pstream_free(struct pa_pstream *p) {
+ assert(p);
+
+ if (p->in_use) {
+ /* If this pstream object is used by someone else on the call stack, we have to postpone the freeing */
+ p->dead = p->shall_free = 1;
+ return;
+ }
+
+ pa_iochannel_free(p->io);
+ 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);
+
+ p->mainloop->cancel_fixed(p->mainloop, p->mainloop_source);
+ free(p);
+}
+
+void pa_pstream_send_packet(struct pa_pstream*p, struct pa_packet *packet) {
+ struct item_info *i;
+ assert(p && packet);
+
+ i = malloc(sizeof(struct item_info));
+ assert(i);
+ i->type = PA_PSTREAM_ITEM_PACKET;
+ i->packet = pa_packet_ref(packet);
+
+ pa_queue_push(p->send_queue, i);
+ p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 1);
+}
+
+void pa_pstream_send_memblock(struct pa_pstream*p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk) {
+ struct item_info *i;
+ assert(p && channel != (uint32_t) -1 && chunk);
+
+ i = malloc(sizeof(struct item_info));
+ assert(i);
+ 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->enable_fixed(p->mainloop, p->mainloop_source, 1);
+}
+
+void pa_pstream_set_recieve_packet_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, struct 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(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct 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(struct 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) {
+ 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 = 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(struct 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 = (void*) p->write.descriptor + p->write.index;
+ l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;
+ } else {
+ d = (void*) 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(struct pa_pstream *p) {
+ void *d;
+ size_t l;
+ ssize_t r;
+ assert(p);
+
+ if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
+ d = (void*) p->read.descriptor + p->read.index;
+ l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index;
+ } else {
+ assert(p->read.data);
+ d = (void*) 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)
+ 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]));
+ 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 */
+ size_t l;
+
+ l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r;
+
+ if (l > 0) {
+ struct 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]),
+ (int32_t) 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(struct pa_pstream *p, void (*callback)(struct pa_pstream *p, void *userdata), void *userdata) {
+ assert(p && callback);
+ p->die_callback = callback;
+ p->die_callback_userdata = userdata;
+}
+
+int pa_pstream_is_pending(struct 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(struct pa_pstream *p, void (*cb)(struct pa_pstream *p, void *userdata), void *userdata) {
+ assert(p);
+
+ p->drain_callback = cb;
+ p->drain_userdata = userdata;
+}
+
diff --git a/polyp/pstream.h b/polyp/pstream.h
new file mode 100644
index 00000000..6b91aeb0
--- /dev/null
+++ b/polyp/pstream.h
@@ -0,0 +1,51 @@
+#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 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 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 "mainloop-api.h"
+#include "memchunk.h"
+
+/* It is safe to destroy the calling pstream object from all callbacks */
+
+struct pa_pstream;
+
+struct pa_pstream* pa_pstream_new(struct pa_mainloop_api *m, struct pa_iochannel *io);
+void pa_pstream_free(struct pa_pstream*p);
+
+void pa_pstream_send_packet(struct pa_pstream*p, struct pa_packet *packet);
+void pa_pstream_send_memblock(struct pa_pstream*p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk);
+
+void pa_pstream_set_recieve_packet_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata), void *userdata);
+void pa_pstream_set_recieve_memblock_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata), void *userdata);
+void pa_pstream_set_drain_callback(struct pa_pstream *p, void (*cb)(struct pa_pstream *p, void *userdata), void *userdata);
+
+void pa_pstream_set_die_callback(struct pa_pstream *p, void (*callback)(struct pa_pstream *p, void *userdata), void *userdata);
+
+int pa_pstream_is_pending(struct pa_pstream *p);
+
+#endif
diff --git a/polyp/queue.c b/polyp/queue.c
new file mode 100644
index 00000000..9befd475
--- /dev/null
+++ b/polyp/queue.c
@@ -0,0 +1,109 @@
+/* $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 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 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"
+
+struct queue_entry {
+ struct queue_entry *next;
+ void *data;
+};
+
+struct pa_queue {
+ struct queue_entry *front, *back;
+ unsigned length;
+};
+
+struct pa_queue* pa_queue_new(void) {
+ struct pa_queue *q = malloc(sizeof(struct pa_queue));
+ assert(q);
+ q->front = q->back = NULL;
+ q->length = 0;
+ return q;
+}
+
+void pa_queue_free(struct 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);
+
+ free(e);
+ e = n;
+ }
+
+ free(q);
+}
+
+void pa_queue_push(struct pa_queue *q, void *p) {
+ struct queue_entry *e;
+
+ e = malloc(sizeof(struct queue_entry));
+
+ 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(struct 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;
+ free(e);
+
+ q->length--;
+
+ return p;
+}
+
+int pa_queue_is_empty(struct pa_queue *q) {
+ assert(q);
+ return q->length == 0;
+}
diff --git a/polyp/queue.h b/polyp/queue.h
new file mode 100644
index 00000000..3ec13734
--- /dev/null
+++ b/polyp/queue.h
@@ -0,0 +1,34 @@
+#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 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 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.
+***/
+
+struct pa_queue;
+
+struct pa_queue* pa_queue_new(void);
+void pa_queue_free(struct pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata);
+void pa_queue_push(struct pa_queue *q, void *p);
+void* pa_queue_pop(struct pa_queue *q);
+
+int pa_queue_is_empty(struct pa_queue *q);
+
+#endif
diff --git a/polyp/resampler.c b/polyp/resampler.c
new file mode 100644
index 00000000..4f5f6be3
--- /dev/null
+++ b/polyp/resampler.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 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 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 <samplerate.h>
+
+#include "resampler.h"
+#include "sconv.h"
+
+struct pa_resampler {
+ struct pa_sample_spec i_ss, o_ss;
+ float* i_buf, *o_buf;
+ unsigned i_alloc, o_alloc;
+ size_t i_sz, o_sz;
+
+ int channels;
+
+ pa_convert_to_float32_func_t to_float32_func;
+ pa_convert_from_float32_func_t from_float32_func;
+ SRC_STATE *src_state;
+};
+
+struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b) {
+ struct pa_resampler *r;
+ int err;
+ assert(a && b && pa_sample_spec_valid(a) && pa_sample_spec_valid(b));
+
+ if (a->channels != b->channels && a->channels != 1 && b->channels != 1)
+ goto fail;
+
+ if (a->format == PA_SAMPLE_ALAW || a->format == PA_SAMPLE_ULAW || b->format == PA_SAMPLE_ALAW || b->format == PA_SAMPLE_ULAW)
+ goto fail;
+
+ r = malloc(sizeof(struct pa_resampler));
+ assert(r);
+
+ r->channels = a->channels;
+ if (b->channels < r->channels)
+ r->channels = b->channels;
+
+ r->i_buf = r->o_buf = NULL;
+ r->i_alloc = r->o_alloc = 0;
+
+ if (a->rate != b->rate) {
+ r->src_state = src_new(SRC_SINC_FASTEST, r->channels, &err);
+ if (err != 0 || !r->src_state)
+ goto fail;
+ } else
+ r->src_state = NULL;
+
+ r->i_ss = *a;
+ r->o_ss = *b;
+
+ r->i_sz = pa_sample_size(a);
+ r->o_sz = pa_sample_size(b);
+
+ r->to_float32_func = pa_get_convert_to_float32_function(a->format);
+ r->from_float32_func = pa_get_convert_from_float32_function(b->format);
+
+ assert(r->to_float32_func && r->from_float32_func);
+
+ return r;
+
+fail:
+ if (r)
+ free(r);
+
+ return NULL;
+}
+
+void pa_resampler_free(struct pa_resampler *r) {
+ assert(r);
+ if (r->src_state)
+ src_delete(r->src_state);
+ free(r->i_buf);
+ free(r->o_buf);
+ free(r);
+}
+
+size_t pa_resampler_request(struct pa_resampler *r, size_t out_length) {
+ assert(r && (out_length % r->o_sz) == 0);
+
+ return (((out_length / r->o_sz)*r->i_ss.rate)/r->o_ss.rate) * r->i_sz;
+}
+
+
+void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out) {
+ unsigned i_nchannels, o_nchannels, ins, ons, eff_ins, eff_ons;
+ float *cbuf;
+ assert(r && in && out && in->length && in->memblock && (in->length % r->i_sz) == 0);
+
+ /* How many input samples? */
+ ins = in->length/r->i_sz;
+
+ /* How much space for output samples? */
+ if (r->src_state)
+ ons = (ins*r->o_ss.rate/r->i_ss.rate)+1024;
+ else
+ ons = ins;
+
+ /* How many channels? */
+ if (r->i_ss.channels == r->o_ss.channels) {
+ i_nchannels = o_nchannels = 1;
+ eff_ins = ins*r->i_ss.channels; /* effective samples */
+ eff_ons = ons*r->o_ss.channels;
+ } else {
+ i_nchannels = r->i_ss.channels;
+ o_nchannels = r->o_ss.channels;
+ eff_ins = ins;
+ eff_ons = ons;
+ }
+
+ out->memblock = pa_memblock_new(out->length = (ons*r->o_sz));
+ out->index = 0;
+ assert(out->memblock);
+
+ if (r->i_alloc < eff_ins)
+ r->i_buf = realloc(r->i_buf, sizeof(float) * (r->i_alloc = eff_ins));
+ assert(r->i_buf);
+
+ r->to_float32_func(eff_ins, in->memblock->data+in->index, i_nchannels, r->i_buf);
+
+ if (r->src_state) {
+ int ret;
+ SRC_DATA data;
+
+ if (r->o_alloc < eff_ons)
+ r->o_buf = realloc(r->o_buf, sizeof(float) * (r->o_alloc = eff_ons));
+ assert(r->o_buf);
+
+ data.data_in = r->i_buf;
+ data.input_frames = ins;
+
+ data.data_out = r->o_buf;
+ data.output_frames = ons;
+
+ data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
+ data.end_of_input = 0;
+
+ ret = src_process(r->src_state, &data);
+ assert(ret == 0);
+ assert((unsigned) data.input_frames_used == ins);
+
+ cbuf = r->o_buf;
+ ons = data.output_frames_gen;
+
+ if (r->i_ss.channels == r->o_ss.channels)
+ eff_ons = ons*r->o_ss.channels;
+ else
+ eff_ons = ons;
+ } else
+ cbuf = r->i_buf;
+
+ r->from_float32_func(eff_ons, cbuf, out->memblock->data+out->index, o_nchannels);
+ out->length = ons*r->o_sz;
+}
diff --git a/polyp/resampler.h b/polyp/resampler.h
new file mode 100644
index 00000000..8e979478
--- /dev/null
+++ b/polyp/resampler.h
@@ -0,0 +1,37 @@
+#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 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 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 "sample.h"
+#include "memblock.h"
+#include "memchunk.h"
+
+struct pa_resampler;
+
+struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b);
+void pa_resampler_free(struct pa_resampler *r);
+
+size_t pa_resampler_request(struct pa_resampler *r, size_t out_length);
+void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out);
+
+#endif
diff --git a/polyp/sample-util.c b/polyp/sample-util.c
new file mode 100644
index 00000000..d608ce1b
--- /dev/null
+++ b/polyp/sample-util.c
@@ -0,0 +1,144 @@
+/* $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 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 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 "sample-util.h"
+
+struct pa_memblock *pa_silence_memblock(struct pa_memblock* b, const struct pa_sample_spec *spec) {
+ assert(b && b->data && spec);
+ pa_silence_memory(b->data, b->length, spec);
+ return b;
+}
+
+void pa_silence_memchunk(struct pa_memchunk *c, const struct pa_sample_spec *spec) {
+ assert(c && c->memblock && c->memblock->data && spec && c->length);
+ pa_silence_memory(c->memblock->data+c->index, c->length, spec);
+}
+
+void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec) {
+ char c = 0;
+ assert(p && length && spec);
+
+ switch (spec->format) {
+ case PA_SAMPLE_U8:
+ c = 127;
+ 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(struct pa_mix_info channels[], unsigned nchannels, void *data, size_t length, const struct pa_sample_spec *spec, uint32_t volume) {
+ unsigned c, d;
+ assert(channels && data && length && spec);
+ assert(spec->format == PA_SAMPLE_S16NE);
+
+ for (d = 0;; d += sizeof(int16_t)) {
+ int32_t sum = 0;
+
+ if (d >= length)
+ return d;
+
+ for (c = 0; c < nchannels; c++) {
+ int32_t v;
+ uint32_t volume = channels[c].volume;
+
+ if (d >= channels[c].chunk.length)
+ return d;
+
+ if (volume == PA_VOLUME_MUTE)
+ v = 0;
+ else {
+ v = *((int16_t*) (channels[c].chunk.memblock->data + channels[c].chunk.index + d));
+
+ if (volume != PA_VOLUME_NORM)
+ v = (int32_t) ((float)v*volume/PA_VOLUME_NORM);
+ }
+
+ sum += v;
+ }
+
+ if (volume == PA_VOLUME_MUTE)
+ sum = 0;
+ else if (volume != PA_VOLUME_NORM)
+ sum = (int32_t) ((float) sum*volume/PA_VOLUME_NORM);
+
+ if (sum < -0x8000) sum = -0x8000;
+ if (sum > 0x7FFF) sum = 0x7FFF;
+
+ *((int16_t*) data) = sum;
+ data += sizeof(int16_t);
+ }
+}
+
+
+void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, uint32_t volume) {
+ int16_t *d;
+ size_t n;
+ assert(c && spec && (c->length % pa_sample_size(spec) == 0));
+ assert(spec->format == PA_SAMPLE_S16NE);
+
+ if (volume == PA_VOLUME_NORM)
+ return;
+
+ if (volume == PA_VOLUME_MUTE) {
+ pa_silence_memchunk(c, spec);
+ return;
+ }
+
+ for (d = (c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+ int32_t t = (int32_t)(*d);
+
+ t *= volume;
+ t /= PA_VOLUME_NORM;
+
+ if (t < -0x8000) t = -0x8000;
+ if (t > 0x7FFF) t = 0x7FFF;
+
+ *d = (int16_t) t;
+ }
+}
+
+uint32_t pa_volume_multiply(uint32_t a, uint32_t b) {
+ uint64_t p = a;
+ p *= b;
+ p /= PA_VOLUME_NORM;
+
+ return (uint32_t) p;
+}
diff --git a/polyp/sample-util.h b/polyp/sample-util.h
new file mode 100644
index 00000000..73101ab4
--- /dev/null
+++ b/polyp/sample-util.h
@@ -0,0 +1,48 @@
+#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 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 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 "sample.h"
+#include "memblock.h"
+#include "memchunk.h"
+
+#define PA_VOLUME_NORM (0x100)
+#define PA_VOLUME_MUTE (0)
+
+struct pa_memblock *pa_silence_memblock(struct pa_memblock* b, const struct pa_sample_spec *spec);
+void pa_silence_memchunk(struct pa_memchunk *c, const struct pa_sample_spec *spec);
+void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec);
+
+struct pa_mix_info {
+ struct pa_memchunk chunk;
+ uint32_t volume;
+ void *userdata;
+};
+
+size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, size_t length, const struct pa_sample_spec *spec, uint32_t volume);
+
+void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, uint32_t volume);
+
+uint32_t pa_volume_multiply(uint32_t a, uint32_t b);
+
+#endif
diff --git a/polyp/sample.c b/polyp/sample.c
new file mode 100644
index 00000000..8179475d
--- /dev/null
+++ b/polyp/sample.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 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 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 "sample.h"
+
+size_t pa_sample_size(const struct pa_sample_spec *spec) {
+ assert(spec);
+ size_t b = 1;
+
+ switch (spec->format) {
+ case PA_SAMPLE_U8:
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
+ b = 1;
+ break;
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ b = 2;
+ break;
+ case PA_SAMPLE_FLOAT32LE:
+ case PA_SAMPLE_FLOAT32BE:
+ b = 4;
+ break;
+ default:
+ assert(0);
+ }
+
+ return b * spec->channels;
+}
+
+size_t pa_bytes_per_second(const struct pa_sample_spec *spec) {
+ assert(spec);
+ return spec->rate*pa_sample_size(spec);
+}
+
+
+uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec) {
+ assert(spec);
+
+ return (uint32_t) (((double) length /pa_sample_size(spec))/spec->rate*1000000);
+}
+
+int pa_sample_spec_valid(const struct pa_sample_spec *spec) {
+ assert(spec);
+
+ if (!spec->rate || !spec->channels)
+ return 0;
+
+ if (spec->format >= PA_SAMPLE_MAX)
+ return 0;
+
+ return 1;
+}
+
+int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b) {
+ assert(a && b);
+
+ return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels);
+}
+
+void pa_sample_snprint(char *s, size_t l, const struct pa_sample_spec *spec) {
+ 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",
+ };
+
+ assert(pa_sample_spec_valid(spec));
+ snprintf(s, l, "%s %uch %uHz", table[spec->format], spec->channels, spec->rate);
+}
diff --git a/polyp/sample.h b/polyp/sample.h
new file mode 100644
index 00000000..825441f2
--- /dev/null
+++ b/polyp/sample.h
@@ -0,0 +1,64 @@
+#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 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 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>
+
+enum pa_sample_format {
+ PA_SAMPLE_U8,
+ PA_SAMPLE_ALAW,
+ PA_SAMPLE_ULAW,
+ PA_SAMPLE_S16LE,
+ PA_SAMPLE_S16BE,
+ PA_SAMPLE_FLOAT32LE,
+ PA_SAMPLE_FLOAT32BE,
+ PA_SAMPLE_MAX
+};
+
+#ifdef WORDS_BIGENDIAN
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
+#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
+#else
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
+#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
+#endif
+#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE
+
+struct pa_sample_spec {
+ enum pa_sample_format format;
+ uint32_t rate;
+ uint8_t channels;
+};
+
+size_t pa_bytes_per_second(const struct pa_sample_spec *spec);
+size_t pa_sample_size(const struct pa_sample_spec *spec);
+uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec);
+int pa_sample_spec_valid(const struct pa_sample_spec *spec);
+int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b);
+
+
+#define PA_SAMPLE_SNPRINT_MAX_LENGTH 32
+void pa_sample_snprint(char *s, size_t l, const struct pa_sample_spec *spec);
+
+#endif
diff --git a/polyp/sconv-s16be.c b/polyp/sconv-s16be.c
new file mode 100644
index 00000000..a4c25cde
--- /dev/null
+++ b/polyp/sconv-s16be.c
@@ -0,0 +1,34 @@
+/* $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 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 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 "sconv-s16be.h"
+
+#define INT16_FROM INT16_FROM_BE
+#define INT16_TO INT16_TO_BE
+
+#define pa_sconv_s16le_to_float32 pa_sconv_s16be_to_float32
+#define pa_sconv_s16le_from_float32 pa_sconv_s16be_from_float32
+
+#include "sconv-s16le.c"
diff --git a/polyp/sconv-s16be.h b/polyp/sconv-s16be.h
new file mode 100644
index 00000000..d112d9f2
--- /dev/null
+++ b/polyp/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 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 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_float32(unsigned n, const void *a, unsigned an, float *b);
+void pa_sconv_s16be_from_float32(unsigned n, const float *a, void *b, unsigned bn);
+
+#endif
diff --git a/polyp/sconv-s16le.c b/polyp/sconv-s16le.c
new file mode 100644
index 00000000..45b28bdb
--- /dev/null
+++ b/polyp/sconv-s16le.c
@@ -0,0 +1,82 @@
+/* $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 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 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 "endianmacros.h"
+#include "sconv.h"
+
+#ifndef INT16_FROM
+#define INT16_FROM INT16_FROM_LE
+#endif
+
+#ifndef INT16_TO
+#define INT16_TO INT16_TO_LE
+#endif
+
+void pa_sconv_s16le_to_float32(unsigned n, const void *a, unsigned an, float *b) {
+ const int16_t *ca = a;
+ assert(n && a && an && b);
+
+ for (; n > 0; n--) {
+ unsigned i;
+ float sum = 0;
+
+ for (i = 0; i < an; i++) {
+ int16_t s = *(ca++);
+ sum += ((float) INT16_FROM(s))/0x7FFF;
+ }
+
+ if (sum > 1)
+ sum = 1;
+ if (sum < -1)
+ sum = -1;
+
+ *(b++) = sum;
+ }
+}
+
+void pa_sconv_s16le_from_float32(unsigned n, const float *a, void *b, unsigned bn) {
+ int16_t *cb = b;
+ assert(n && a && b && bn);
+
+ for (; n > 0; n--) {
+ unsigned i;
+ int16_t s;
+ float v = *(a++);
+
+ if (v > 1)
+ v = 1;
+ if (v < -1)
+ v = -1;
+
+ s = (int16_t) (v * 0x7FFF);
+ s = INT16_TO(s);
+
+ for (i = 0; i < bn; i++)
+ *(cb++) = s;
+ }
+}
diff --git a/polyp/sconv-s16le.h b/polyp/sconv-s16le.h
new file mode 100644
index 00000000..0f206ec3
--- /dev/null
+++ b/polyp/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 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 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_float32(unsigned n, const void *a, unsigned an, float *b);
+void pa_sconv_s16le_from_float32(unsigned n, const float *a, void *b, unsigned bn);
+
+#endif
diff --git a/polyp/sconv.c b/polyp/sconv.c
new file mode 100644
index 00000000..dd9dd241
--- /dev/null
+++ b/polyp/sconv.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 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 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 "endianmacros.h"
+#include "sconv.h"
+
+#include "sconv-s16le.h"
+#include "sconv-s16be.h"
+
+static void u8_to_float32(unsigned n, const void *a, unsigned an, float *b) {
+ unsigned i;
+ const uint8_t *ca = a;
+ assert(n && a && an && b);
+
+ for (; n > 0; n--) {
+ float sum = 0;
+
+ for (i = 0; i < an; i++) {
+ uint8_t v = *(ca++);
+ sum += (((float) v)-127)/127;
+ }
+
+ if (sum > 1)
+ sum = 1;
+ if (sum < -1)
+ sum = -1;
+
+ *(b++) = sum;
+ }
+}
+
+static void u8_from_float32(unsigned n, const float *a, void *b, unsigned bn) {
+ unsigned i;
+ uint8_t *cb = b;
+
+ assert(n && a && b && bn);
+ for (; n > 0; n--) {
+ float v = *(a++);
+ uint8_t u;
+
+ if (v > 1)
+ v = 1;
+
+ if (v < -1)
+ v = -1;
+
+ u = (uint8_t) (v*127+127);
+
+ for (i = 0; i < bn; i++)
+ *(cb++) = u;
+ }
+}
+
+static void float32_to_float32(unsigned n, const void *a, unsigned an, float *b) {
+ unsigned i;
+ const float *ca = a;
+ assert(n && a && an && b);
+ for (; n > 0; n--) {
+ float sum = 0;
+
+ for (i = 0; i < an; i++)
+ sum += *(ca++);
+
+ if (sum > 1)
+ sum = 1;
+ if (sum < -1)
+ sum = -1;
+
+ *(b++) = sum;
+ }
+}
+
+static void float32_from_float32(unsigned n, const float *a, void *b, unsigned bn) {
+ unsigned i;
+ float *cb = b;
+ assert(n && a && b && bn);
+ for (; n > 0; n--) {
+ float v = *(a++);
+ for (i = 0; i < bn; i++)
+ *(cb++) = v;
+ }
+}
+
+pa_convert_to_float32_func_t pa_get_convert_to_float32_function(enum pa_sample_format f) {
+ switch(f) {
+ case PA_SAMPLE_U8:
+ return u8_to_float32;
+ case PA_SAMPLE_S16LE:
+ return pa_sconv_s16le_to_float32;
+ case PA_SAMPLE_S16BE:
+ return pa_sconv_s16be_to_float32;
+ case PA_SAMPLE_FLOAT32:
+ return float32_to_float32;
+ default:
+ return NULL;
+ }
+}
+
+pa_convert_from_float32_func_t pa_get_convert_from_float32_function(enum pa_sample_format f) {
+ switch(f) {
+ case PA_SAMPLE_U8:
+ return u8_from_float32;
+ case PA_SAMPLE_S16LE:
+ return pa_sconv_s16le_from_float32;
+ case PA_SAMPLE_S16BE:
+ return pa_sconv_s16be_from_float32;
+ case PA_SAMPLE_FLOAT32:
+ return float32_from_float32;
+ default:
+ return NULL;
+ }
+}
diff --git a/polyp/sconv.h b/polyp/sconv.h
new file mode 100644
index 00000000..1a62ed20
--- /dev/null
+++ b/polyp/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 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 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 "sample.h"
+
+typedef void (*pa_convert_to_float32_func_t)(unsigned n, const void *a, unsigned an, float *b);
+typedef void (*pa_convert_from_float32_func_t)(unsigned n, const float *a, void *b, unsigned bn);
+
+pa_convert_to_float32_func_t pa_get_convert_to_float32_function(enum pa_sample_format f);
+pa_convert_from_float32_func_t pa_get_convert_from_float32_function(enum pa_sample_format f);
+
+#endif
diff --git a/polyp/sink-input.c b/polyp/sink-input.c
new file mode 100644
index 00000000..5c2d3a13
--- /dev/null
+++ b/polyp/sink-input.c
@@ -0,0 +1,163 @@
+/* $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 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 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"
+
+#define CONVERT_BUFFER_LENGTH 4096
+
+struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, const struct pa_sample_spec *spec) {
+ struct pa_sink_input *i;
+ struct pa_resampler *resampler = NULL;
+ int r;
+ char st[256];
+ assert(s && spec);
+
+ if (!pa_sample_spec_equal(spec, &s->sample_spec))
+ if (!(resampler = pa_resampler_new(spec, &s->sample_spec)))
+ return NULL;
+
+ i = malloc(sizeof(struct pa_sink_input));
+ assert(i);
+ i->name = name ? strdup(name) : NULL;
+ i->client = NULL;
+ i->owner = NULL;
+ i->sink = s;
+ i->sample_spec = *spec;
+
+ i->peek = NULL;
+ i->drop = NULL;
+ i->kill = NULL;
+ i->get_latency = NULL;
+ i->userdata = NULL;
+
+ i->volume = PA_VOLUME_NORM;
+
+ i->resampled_chunk.memblock = NULL;
+ i->resampled_chunk.index = i->resampled_chunk.length = 0;
+ 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_snprint(st, sizeof(st), spec);
+ fprintf(stderr, "sink-input: created %u \"%s\" on %u with sample spec \"%s\"\n", i->index, i->name, s->index, st);
+
+ return i;
+}
+
+void pa_sink_input_free(struct pa_sink_input* i) {
+ assert(i);
+
+ assert(i->sink && 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);
+
+ if (i->resampled_chunk.memblock)
+ pa_memblock_unref(i->resampled_chunk.memblock);
+ if (i->resampler)
+ pa_resampler_free(i->resampler);
+
+ free(i->name);
+ free(i);
+}
+
+void pa_sink_input_kill(struct pa_sink_input*i) {
+ assert(i);
+
+ if (i->kill)
+ i->kill(i);
+}
+
+uint32_t pa_sink_input_get_latency(struct pa_sink_input *i) {
+ uint32_t l = 0;
+
+ assert(i);
+ if (i->get_latency)
+ l += i->get_latency(i);
+
+ assert(i->sink);
+ l += pa_sink_get_latency(i->sink);
+
+ return l;
+}
+
+int pa_sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) {
+ assert(i && chunk && i->peek && i->drop);
+
+ if (!i->resampler)
+ return i->peek(i, chunk);
+
+ if (!i->resampled_chunk.memblock) {
+ struct pa_memchunk tchunk;
+ size_t l;
+ int ret;
+
+ if ((ret = i->peek(i, &tchunk)) < 0)
+ return ret;
+
+ l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH);
+ if (tchunk.length > l)
+ tchunk.length = l;
+
+ i->drop(i, tchunk.length);
+
+ pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk);
+ pa_memblock_unref(tchunk.memblock);
+ }
+
+ assert(i->resampled_chunk.memblock && i->resampled_chunk.length);
+ *chunk = i->resampled_chunk;
+ pa_memblock_ref(i->resampled_chunk.memblock);
+ return 0;
+}
+
+void pa_sink_input_drop(struct pa_sink_input *i, size_t length) {
+ assert(i && length);
+
+ if (!i->resampler) {
+ i->drop(i, length);
+ return;
+ }
+
+ assert(i->resampled_chunk.memblock && i->resampled_chunk.length >= length);
+
+ i->resampled_chunk.index += length;
+ i->resampled_chunk.length -= length;
+
+ if (!i->resampled_chunk.length) {
+ pa_memblock_unref(i->resampled_chunk.memblock);
+ i->resampled_chunk.memblock = NULL;
+ i->resampled_chunk.index = i->resampled_chunk.length = 0;
+ }
+}
diff --git a/polyp/sink-input.h b/polyp/sink-input.h
new file mode 100644
index 00000000..63dce71d
--- /dev/null
+++ b/polyp/sink-input.h
@@ -0,0 +1,67 @@
+#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 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 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 "sink.h"
+#include "sample.h"
+#include "memblockq.h"
+#include "resampler.h"
+#include "module.h"
+#include "client.h"
+
+struct pa_sink_input {
+ uint32_t index;
+
+ char *name;
+ struct pa_module *owner;
+ struct pa_client *client;
+ struct pa_sink *sink;
+ struct pa_sample_spec sample_spec;
+ uint32_t volume;
+
+ int (*peek) (struct pa_sink_input *i, struct pa_memchunk *chunk);
+ void (*drop) (struct pa_sink_input *i, size_t length);
+ void (*kill) (struct pa_sink_input *i);
+ uint32_t (*get_latency) (struct pa_sink_input *i);
+
+ void *userdata;
+
+ struct pa_memchunk resampled_chunk;
+ struct pa_resampler *resampler;
+};
+
+struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, const struct pa_sample_spec *spec);
+void pa_sink_input_free(struct pa_sink_input* i);
+
+/* Code that didn't create the input stream should call this function to
+ * request destruction of it */
+void pa_sink_input_kill(struct pa_sink_input *i);
+
+uint32_t pa_sink_input_get_latency(struct pa_sink_input *i);
+
+int pa_sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk);
+void pa_sink_input_drop(struct pa_sink_input *i, size_t length);
+
+#endif
diff --git a/polyp/sink.c b/polyp/sink.c
new file mode 100644
index 00000000..20fa76a6
--- /dev/null
+++ b/polyp/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 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 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"
+
+#define MAX_MIX_CHANNELS 32
+
+struct pa_sink* pa_sink_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec) {
+ struct pa_sink *s;
+ char *n = NULL;
+ char st[256];
+ int r;
+ assert(core && name && spec);
+
+ s = malloc(sizeof(struct pa_sink));
+ assert(s);
+
+ if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) {
+ free(s);
+ return NULL;
+ }
+
+ s->name = strdup(name);
+ s->description = NULL;
+
+ s->owner = NULL;
+ s->core = core;
+ s->sample_spec = *spec;
+ s->inputs = pa_idxset_new(NULL, NULL);
+
+ n = pa_sprintf_malloc("%s_monitor", name);
+ s->monitor_source = pa_source_new(core, n, 0, spec);
+ assert(s->monitor_source);
+ free(n);
+ s->monitor_source->monitor_of = s;
+
+ s->volume = PA_VOLUME_NORM;
+
+ s->notify = NULL;
+ s->get_latency = NULL;
+ s->userdata = NULL;
+
+ r = pa_idxset_put(core->sinks, s, &s->index);
+ assert(s->index != PA_IDXSET_INVALID && r >= 0);
+
+ pa_sample_snprint(st, sizeof(st), spec);
+ fprintf(stderr, "sink: created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st);
+
+ return s;
+}
+
+void pa_sink_free(struct pa_sink *s) {
+ struct pa_sink_input *i, *j = NULL;
+ assert(s);
+
+ 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_idxset_free(s->inputs, NULL, NULL);
+
+ pa_source_free(s->monitor_source);
+ pa_idxset_remove_by_data(s->core->sinks, s, NULL);
+
+ fprintf(stderr, "sink: freed %u \"%s\"\n", s->index, s->name);
+
+ free(s->name);
+ free(s->description);
+ free(s);
+}
+
+void pa_sink_notify(struct pa_sink*s) {
+ assert(s);
+
+ if (s->notify)
+ s->notify(s);
+}
+
+static unsigned fill_mix_info(struct pa_sink *s, struct pa_mix_info *info, unsigned maxinfo) {
+ uint32_t index = PA_IDXSET_INVALID;
+ struct pa_sink_input *i;
+ unsigned n = 0;
+
+ assert(s && info);
+
+ for (i = pa_idxset_first(s->inputs, &index); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &index)) {
+ if (pa_sink_input_peek(i, &info->chunk) < 0)
+ continue;
+
+ info->volume = i->volume;
+
+ assert(info->chunk.memblock && info->chunk.memblock->data && info->chunk.length);
+ info->userdata = i;
+
+ info++;
+ maxinfo--;
+ n++;
+ }
+
+ return n;
+}
+
+static void inputs_drop(struct pa_sink *s, struct pa_mix_info *info, unsigned maxinfo, size_t length) {
+ assert(s && info);
+
+ for (; maxinfo > 0; maxinfo--, info++) {
+ struct pa_sink_input *i = info->userdata;
+ assert(i && info->chunk.memblock);
+
+ pa_memblock_unref(info->chunk.memblock);
+ pa_sink_input_drop(i, length);
+ }
+}
+
+int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result) {
+ struct pa_mix_info info[MAX_MIX_CHANNELS];
+ unsigned n;
+ size_t l;
+ assert(s && length && result);
+
+ n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
+
+ if (n <= 0)
+ return -1;
+
+ if (n == 1) {
+ uint32_t volume = PA_VOLUME_NORM;
+ struct pa_sink_input *i = info[0].userdata;
+ assert(i);
+ *result = info[0].chunk;
+ pa_memblock_ref(result->memblock);
+
+ if (result->length > length)
+ result->length = length;
+
+ l = result->length;
+
+ if (s->volume != PA_VOLUME_NORM || info[0].volume != PA_VOLUME_NORM)
+ volume = pa_volume_multiply(s->volume, info[0].volume);
+
+ if (volume != PA_VOLUME_NORM) {
+ pa_memchunk_make_writable(result);
+ pa_volume_memchunk(result, &s->sample_spec, volume);
+ }
+ } else {
+ result->memblock = pa_memblock_new(length);
+ assert(result->memblock);
+
+ result->length = l = pa_mix(info, n, result->memblock->data, length, &s->sample_spec, s->volume);
+ result->index = 0;
+
+ assert(l);
+ }
+
+ inputs_drop(s, info, n, l);
+
+ assert(s->monitor_source);
+ pa_source_post(s->monitor_source, result);
+
+ return 0;
+}
+
+int pa_sink_render_into(struct pa_sink*s, struct pa_memchunk *target) {
+ struct pa_mix_info info[MAX_MIX_CHANNELS];
+ unsigned n;
+ size_t l;
+ assert(s && target && target->length && target->memblock && target->memblock->data);
+
+ n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
+
+ if (n <= 0)
+ return -1;
+
+ if (n == 1) {
+ uint32_t volume = PA_VOLUME_NORM;
+ struct pa_sink_info *i = info[0].userdata;
+ assert(i);
+
+ l = target->length;
+ if (l > info[0].chunk.length)
+ l = info[0].chunk.length;
+
+ memcpy(target->memblock->data+target->index, info[0].chunk.memblock->data + info[0].chunk.index, l);
+ target->length = l;
+
+ if (s->volume != PA_VOLUME_NORM || info[0].volume != PA_VOLUME_NORM)
+ volume = pa_volume_multiply(s->volume, info[0].volume);
+
+ if (volume != PA_VOLUME_NORM)
+ pa_volume_memchunk(target, &s->sample_spec, volume);
+ } else
+ target->length = l = pa_mix(info, n, target->memblock->data+target->index, target->length, &s->sample_spec, s->volume);
+
+ assert(l);
+ inputs_drop(s, info, n, l);
+
+ assert(s->monitor_source);
+ pa_source_post(s->monitor_source, target);
+
+ return 0;
+}
+
+void pa_sink_render_into_full(struct pa_sink *s, struct pa_memchunk *target) {
+ struct pa_memchunk chunk;
+ size_t l, d;
+ assert(s && target && target->memblock && target->length && target->memblock->data);
+
+ 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);
+ }
+}
+
+uint32_t pa_sink_get_latency(struct pa_sink *s) {
+ assert(s);
+
+ if (!s->get_latency)
+ return 0;
+
+ return s->get_latency(s);
+}
+
+struct pa_sink* pa_sink_get_default(struct pa_core *c) {
+ struct pa_sink *sink;
+ assert(c);
+
+ if ((sink = pa_idxset_get_by_index(c->sinks, c->default_sink_index)))
+ return sink;
+
+ if (!(sink = pa_idxset_first(c->sinks, &c->default_sink_index)))
+ return NULL;
+
+ fprintf(stderr, "core: default sink vanished, setting to %u.\n", sink->index);
+ return sink;
+}
+
+void pa_sink_set_owner(struct pa_sink *sink, struct pa_module *m) {
+ sink->owner = m;
+
+ if (sink->monitor_source)
+ pa_source_set_owner(sink->monitor_source, m);
+}
diff --git a/polyp/sink.h b/polyp/sink.h
new file mode 100644
index 00000000..2b5d9495
--- /dev/null
+++ b/polyp/sink.h
@@ -0,0 +1,67 @@
+#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 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 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.
+***/
+
+struct pa_sink;
+
+#include <inttypes.h>
+
+#include "core.h"
+#include "sample.h"
+#include "idxset.h"
+#include "source.h"
+
+struct pa_sink {
+ uint32_t index;
+
+ char *name, *description;
+ struct pa_module *owner;
+ struct pa_core *core;
+ struct pa_sample_spec sample_spec;
+ struct pa_idxset *inputs;
+
+ struct pa_source *monitor_source;
+
+ uint32_t volume;
+
+ void (*notify)(struct pa_sink*sink);
+ uint32_t (*get_latency)(struct pa_sink *s);
+ void *userdata;
+};
+
+struct pa_sink* pa_sink_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec);
+void pa_sink_free(struct pa_sink* s);
+
+int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result);
+int pa_sink_render_into(struct pa_sink*s, struct pa_memchunk *target);
+void pa_sink_render_into_full(struct pa_sink *s, struct pa_memchunk *target);
+
+uint32_t pa_sink_get_latency(struct pa_sink *s);
+
+void pa_sink_notify(struct pa_sink*s);
+
+struct pa_sink* pa_sink_get_default(struct pa_core *c);
+
+void pa_sink_set_owner(struct pa_sink *sink, struct pa_module *m);
+
+#endif
diff --git a/polyp/sioman.c b/polyp/sioman.c
new file mode 100644
index 00000000..999b8a5c
--- /dev/null
+++ b/polyp/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 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 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/polyp/sioman.h b/polyp/sioman.h
new file mode 100644
index 00000000..1b60d4a9
--- /dev/null
+++ b/polyp/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 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 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/polyp/socket-client.c b/polyp/socket-client.c
new file mode 100644
index 00000000..a2187e6a
--- /dev/null
+++ b/polyp/socket-client.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 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 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 <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "socket-client.h"
+#include "socket-util.h"
+#include "util.h"
+
+struct pa_socket_client {
+ struct pa_mainloop_api *mainloop;
+ int fd;
+
+ void *io_source, *fixed_source;
+ void (*callback)(struct pa_socket_client*c, struct pa_iochannel *io, void *userdata);
+ void *userdata;
+};
+
+static struct pa_socket_client*pa_socket_client_new(struct pa_mainloop_api *m) {
+ struct pa_socket_client *c;
+ assert(m);
+
+ c = malloc(sizeof(struct pa_socket_client));
+ assert(c);
+ c->mainloop = m;
+ c->fd = -1;
+ c->io_source = c->fixed_source = NULL;
+ c->callback = NULL;
+ c->userdata = NULL;
+ return c;
+}
+
+static void do_call(struct pa_socket_client *c) {
+ struct pa_iochannel *io;
+ int error, lerror;
+ assert(c && c->callback);
+
+ lerror = sizeof(error);
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &error, &lerror) < 0) {
+ fprintf(stderr, "getsockopt(): %s\n", strerror(errno));
+ goto failed;
+ }
+
+ if (lerror != sizeof(error)) {
+ fprintf(stderr, "getsocktop() returned invalid size.\n");
+ goto failed;
+ }
+
+ if (error != 0) {
+ fprintf(stderr, "connect(): %s\n", strerror(error));
+ goto failed;
+ }
+
+ io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
+ assert(io);
+ c->fd = -1;
+ c->callback(c, io, c->userdata);
+
+ return;
+
+failed:
+ close(c->fd);
+ c->fd = -1;
+ c->callback(c, NULL, c->userdata);
+ return;
+}
+
+static void connect_fixed_cb(struct pa_mainloop_api *m, void *id, void *userdata) {
+ struct pa_socket_client *c = userdata;
+ assert(m && c && c->fixed_source == id);
+ m->cancel_fixed(m, c->fixed_source);
+ c->fixed_source = NULL;
+ do_call(c);
+}
+
+static void connect_io_cb(struct pa_mainloop_api*m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
+ struct pa_socket_client *c = userdata;
+ assert(m && c && c->io_source == id && fd >= 0);
+ m->cancel_io(m, c->io_source);
+ c->io_source = NULL;
+ do_call(c);
+}
+
+static int do_connect(struct 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) {
+ fprintf(stderr, "connect(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ c->io_source = c->mainloop->source_io(c->mainloop, c->fd, PA_MAINLOOP_API_IO_EVENT_OUTPUT, connect_io_cb, c);
+ assert(c->io_source);
+ } else {
+ c->fixed_source = c->mainloop->source_fixed(c->mainloop, connect_fixed_cb, c);
+ assert(c->fixed_source);
+ }
+
+ return 0;
+}
+
+struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) {
+ struct pa_socket_client *c;
+ struct sockaddr_in sa;
+ assert(m && address && port);
+
+ c = pa_socket_client_new(m);
+ assert(c);
+
+ if ((c->fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "socket(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ pa_socket_tcp_low_delay(c->fd);
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(port);
+ sa.sin_addr.s_addr = htonl(address);
+
+ if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0)
+ goto fail;
+
+ return c;
+
+fail:
+ pa_socket_client_free(c);
+ return NULL;
+}
+
+struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename) {
+ struct pa_socket_client *c;
+ struct sockaddr_un sa;
+ assert(m && filename);
+
+ c = pa_socket_client_new(m);
+ assert(c);
+
+ if ((c->fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "socket(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ pa_socket_low_delay(c->fd);
+
+ sa.sun_family = AF_LOCAL;
+ strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
+ sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+
+ if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0)
+ goto fail;
+
+ return c;
+
+fail:
+ pa_socket_client_free(c);
+ return NULL;
+}
+
+struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {
+ struct pa_socket_client *c;
+ assert(m && sa);
+ c = pa_socket_client_new(m);
+ assert(c);
+
+ if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "socket(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (sa->sa_family == AF_INET)
+ pa_socket_tcp_low_delay(c->fd);
+ else
+ pa_socket_low_delay(c->fd);
+
+ if (do_connect(c, sa, salen) < 0)
+ goto fail;
+
+ return c;
+
+fail:
+ pa_socket_client_free(c);
+ return NULL;
+
+}
+
+void pa_socket_client_free(struct pa_socket_client *c) {
+ assert(c && c->mainloop);
+ if (c->io_source)
+ c->mainloop->cancel_io(c->mainloop, c->io_source);
+ if (c->fixed_source)
+ c->mainloop->cancel_fixed(c->mainloop, c->fixed_source);
+ if (c->fd >= 0)
+ close(c->fd);
+ free(c);
+}
+
+void pa_socket_client_set_callback(struct pa_socket_client *c, void (*on_connection)(struct pa_socket_client *c, struct pa_iochannel*io, void *userdata), void *userdata) {
+ assert(c);
+ c->callback = on_connection;
+ c->userdata = userdata;
+}
diff --git a/polyp/socket-client.h b/polyp/socket-client.h
new file mode 100644
index 00000000..2a89210e
--- /dev/null
+++ b/polyp/socket-client.h
@@ -0,0 +1,41 @@
+#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 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 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 "mainloop-api.h"
+#include "iochannel.h"
+
+/* It is safe to destroy the calling socket_client object from the callback */
+
+struct pa_socket_client;
+
+struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port);
+struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename);
+struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m, const struct sockaddr *sa, size_t salen);
+
+void pa_socket_client_free(struct pa_socket_client *c);
+
+void pa_socket_client_set_callback(struct pa_socket_client *c, void (*on_connection)(struct pa_socket_client *c, struct pa_iochannel*io, void *userdata), void *userdata);
+
+#endif
diff --git a/polyp/socket-server.c b/polyp/socket-server.c
new file mode 100644
index 00000000..0f497377
--- /dev/null
+++ b/polyp/socket-server.c
@@ -0,0 +1,209 @@
+/* $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 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 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 <sys/socket.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "socket-server.h"
+#include "socket-util.h"
+
+struct pa_socket_server {
+ int fd;
+ char *filename;
+
+ void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata);
+ void *userdata;
+
+ void *mainloop_source;
+ struct pa_mainloop_api *mainloop;
+ enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX } type;
+};
+
+static void callback(struct pa_mainloop_api *mainloop, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
+ struct pa_socket_server *s = userdata;
+ struct pa_iochannel *io;
+ int nfd;
+ assert(s && s->mainloop == mainloop && s->mainloop_source == id && id && fd >= 0 && fd == s->fd && events == PA_MAINLOOP_API_IO_EVENT_INPUT);
+
+ if ((nfd = accept(fd, NULL, NULL)) < 0) {
+ fprintf(stderr, "accept(): %s\n", strerror(errno));
+ return;
+ }
+
+ if (!s->on_connection) {
+ close(nfd);
+ return;
+ }
+
+ /* 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);
+}
+
+struct pa_socket_server* pa_socket_server_new(struct pa_mainloop_api *m, int fd) {
+ struct pa_socket_server *s;
+ assert(m && fd >= 0);
+
+ s = malloc(sizeof(struct pa_socket_server));
+ assert(s);
+ s->fd = fd;
+ s->filename = NULL;
+ s->on_connection = NULL;
+ s->userdata = NULL;
+
+ s->mainloop = m;
+ s->mainloop_source = m->source_io(m, fd, PA_MAINLOOP_API_IO_EVENT_INPUT, callback, s);
+ assert(s->mainloop_source);
+
+ s->type = SOCKET_SERVER_GENERIC;
+
+ return s;
+}
+
+struct pa_socket_server* pa_socket_server_new_unix(struct pa_mainloop_api *m, const char *filename) {
+ int fd = -1;
+ struct sockaddr_un sa;
+ struct pa_socket_server *s;
+
+ assert(m && filename);
+
+ if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "socket(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ sa.sun_family = AF_LOCAL;
+ 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) {
+ fprintf(stderr, "bind(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (listen(fd, 5) < 0) {
+ fprintf(stderr, "listen(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ s = pa_socket_server_new(m, fd);
+ assert(s);
+
+ s->filename = strdup(filename);
+ assert(s->filename);
+
+ s->type = SOCKET_SERVER_UNIX;
+
+ return s;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return NULL;
+}
+
+struct pa_socket_server* pa_socket_server_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) {
+ struct 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) {
+ fprintf(stderr, "socket(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+ fprintf(stderr, "setsockopt(): %s\n", strerror(errno));
+
+ pa_socket_tcp_low_delay(fd);
+
+ 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) {
+ fprintf(stderr, "bind(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (listen(fd, 5) < 0) {
+ fprintf(stderr, "listen(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if ((ss = pa_socket_server_new(m, fd)))
+ ss->type = SOCKET_SERVER_IPV4;
+
+ return ss;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return NULL;
+}
+
+void pa_socket_server_free(struct pa_socket_server*s) {
+ assert(s);
+ close(s->fd);
+
+ if (s->filename) {
+ unlink(s->filename);
+ free(s->filename);
+ }
+
+
+ s->mainloop->cancel_io(s->mainloop, s->mainloop_source);
+
+ free(s);
+}
+
+void pa_socket_server_set_callback(struct pa_socket_server*s, void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata), void *userdata) {
+ assert(s);
+
+ s->on_connection = on_connection;
+ s->userdata = userdata;
+}
diff --git a/polyp/socket-server.h b/polyp/socket-server.h
new file mode 100644
index 00000000..6661a66e
--- /dev/null
+++ b/polyp/socket-server.h
@@ -0,0 +1,41 @@
+#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 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 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 "mainloop-api.h"
+#include "iochannel.h"
+
+/* It is safe to destroy the calling socket_server object from the callback */
+
+struct pa_socket_server;
+
+struct pa_socket_server* pa_socket_server_new(struct pa_mainloop_api *m, int fd);
+struct pa_socket_server* pa_socket_server_new_unix(struct pa_mainloop_api *m, const char *filename);
+struct pa_socket_server* pa_socket_server_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port);
+
+void pa_socket_server_free(struct pa_socket_server*s);
+
+void pa_socket_server_set_callback(struct pa_socket_server*s, void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata), void *userdata);
+
+#endif
diff --git a/polyp/socket-util.c b/polyp/socket-util.c
new file mode 100644
index 00000000..e0a3c28d
--- /dev/null
+++ b/polyp/socket-util.c
@@ -0,0 +1,216 @@
+/* $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 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 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/un.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <netinet/tcp.h>
+#include <netinet/ip.h>
+
+#include "socket-util.h"
+#include "util.h"
+
+void pa_socket_peer_to_string(int fd, char *c, size_t l) {
+ struct stat st;
+
+ assert(c && l && fd >= 0);
+
+ if (fstat(fd, &st) < 0) {
+ snprintf(c, l, "Invalid client fd");
+ return;
+ }
+
+ if (S_ISSOCK(st.st_mode)) {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_un un;
+ } 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;
+ } else if (sa.sa.sa_family == AF_LOCAL) {
+ snprintf(c, l, "UNIX socket client");
+ return;
+ }
+
+ }
+ 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;
+ }
+
+ snprintf(c, l, "Unknown client");
+}
+
+int pa_socket_low_delay(int fd) {
+ int priority;
+ assert(fd >= 0);
+
+ priority = 7;
+ if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0)
+ return -1;
+
+ return 0;
+}
+
+int pa_socket_tcp_low_delay(int fd) {
+ int ret, tos;
+
+ assert(fd >= 0);
+
+ ret = pa_socket_low_delay(fd);
+
+/* on = 1; */
+/* if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) */
+/* ret = -1; */
+
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(fd, SOL_IP, IP_TOS, &tos, sizeof(tos)) < 0)
+ ret = -1;
+
+ return ret;
+
+}
+
+int pa_socket_set_rcvbuf(int fd, size_t l) {
+ assert(fd >= 0);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &l, sizeof(l)) < 0)
+ return -1;
+
+ return 0;
+}
+
+int pa_socket_set_sndbuf(int fd, size_t l) {
+ assert(fd >= 0);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &l, sizeof(l)) < 0)
+ return -1;
+
+ return 0;
+}
+
+int pa_unix_socket_is_stale(const char *fn) {
+ struct sockaddr_un sa;
+ int fd = -1, ret = -1;
+
+ if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "socket(): %s\n", strerror(errno));
+ goto finish;
+ }
+
+ sa.sun_family = AF_LOCAL;
+ 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;
+}
+
+int pa_unix_socket_make_secure_dir(const char *fn) {
+ int ret = -1;
+ char *slash, *dir = strdup(fn);
+ assert(dir);
+
+ if (!(slash = strrchr(dir, '/')))
+ goto finish;
+ *slash = 0;
+
+ if (pa_make_secure_dir(dir) < 0)
+ goto finish;
+
+ ret = 0;
+
+finish:
+ free(dir);
+ return ret;
+}
+
+int pa_unix_socket_remove_secure_dir(const char *fn) {
+ int ret = -1;
+ char *slash, *dir = strdup(fn);
+ assert(dir);
+
+ if (!(slash = strrchr(dir, '/')))
+ goto finish;
+ *slash = 0;
+
+ if (rmdir(dir) < 0)
+ goto finish;
+
+ ret = 0;
+
+finish:
+ free(dir);
+ return ret;
+}
diff --git a/polyp/socket-util.h b/polyp/socket-util.h
new file mode 100644
index 00000000..85133feb
--- /dev/null
+++ b/polyp/socket-util.h
@@ -0,0 +1,41 @@
+#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 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 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);
+
+int pa_unix_socket_make_secure_dir(const char *fn);
+int pa_unix_socket_remove_secure_dir(const char *fn);
+
+#endif
diff --git a/polyp/source-output.c b/polyp/source-output.c
new file mode 100644
index 00000000..2705fdb3
--- /dev/null
+++ b/polyp/source-output.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 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 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 "source-output.h"
+
+struct pa_source_output* pa_source_output_new(struct pa_source *s, const char *name, const struct pa_sample_spec *spec) {
+ struct pa_source_output *o;
+ struct pa_resampler *resampler = NULL;
+ int r;
+ assert(s && spec);
+
+ if (!pa_sample_spec_equal(&s->sample_spec, spec))
+ if (!(resampler = pa_resampler_new(&s->sample_spec, spec)))
+ return NULL;
+
+ o = malloc(sizeof(struct pa_source_output));
+ assert(o);
+ o->name = name ? strdup(name) : NULL;
+ o->client = NULL;
+ o->owner = NULL;
+ o->source = s;
+ o->sample_spec = *spec;
+
+ o->push = NULL;
+ o->kill = 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);
+
+ return o;
+}
+
+void pa_source_output_free(struct pa_source_output* o) {
+ assert(o);
+
+ assert(o->source && 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);
+
+ if (o->resampler)
+ pa_resampler_free(o->resampler);
+
+ free(o->name);
+ free(o);
+}
+
+void pa_source_output_kill(struct pa_source_output*i) {
+ assert(i);
+
+ if (i->kill)
+ i->kill(i);
+}
+
+void pa_source_output_push(struct pa_source_output *o, const struct pa_memchunk *chunk) {
+ struct pa_memchunk rchunk;
+ assert(o && chunk && chunk->length && o->push);
+
+ 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);
+}
diff --git a/polyp/source-output.h b/polyp/source-output.h
new file mode 100644
index 00000000..0e6e2cfd
--- /dev/null
+++ b/polyp/source-output.h
@@ -0,0 +1,58 @@
+#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 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 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 "source.h"
+#include "sample.h"
+#include "memblockq.h"
+#include "resampler.h"
+#include "module.h"
+#include "client.h"
+
+struct pa_source_output {
+ uint32_t index;
+
+ char *name;
+ struct pa_module *owner;
+ struct pa_client *client;
+ struct pa_source *source;
+ struct pa_sample_spec sample_spec;
+
+ void (*push)(struct pa_source_output *o, const struct pa_memchunk *chunk);
+ void (*kill)(struct pa_source_output* o);
+
+ struct pa_resampler* resampler;
+
+ void *userdata;
+};
+
+struct pa_source_output* pa_source_output_new(struct pa_source *s, const char *name, const struct pa_sample_spec *spec);
+void pa_source_output_free(struct pa_source_output* o);
+
+void pa_source_output_kill(struct pa_source_output*o);
+
+void pa_source_output_push(struct pa_source_output *o, const struct pa_memchunk *chunk);
+
+#endif
diff --git a/polyp/source.c b/polyp/source.c
new file mode 100644
index 00000000..ccde0e2f
--- /dev/null
+++ b/polyp/source.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 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 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"
+
+struct pa_source* pa_source_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec) {
+ struct pa_source *s;
+ char st[256];
+ int r;
+ assert(core && spec && name);
+
+ s = malloc(sizeof(struct pa_source));
+ assert(s);
+
+ if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) {
+ free(s);
+ return NULL;
+ }
+
+ s->name = strdup(name);
+ s->description = NULL;
+
+ s->owner = NULL;
+ s->core = core;
+ s->sample_spec = *spec;
+ s->outputs = pa_idxset_new(NULL, NULL);
+ s->monitor_of = 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_snprint(st, sizeof(st), spec);
+ fprintf(stderr, "source: created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st);
+
+ return s;
+}
+
+void pa_source_free(struct pa_source *s) {
+ struct pa_source_output *o, *j = NULL;
+ assert(s);
+
+ 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_free(s->outputs, NULL, NULL);
+
+ pa_idxset_remove_by_data(s->core->sources, s, NULL);
+
+ fprintf(stderr, "source: freed %u \"%s\"\n", s->index, s->name);
+
+ free(s->name);
+ free(s->description);
+ free(s);
+}
+
+void pa_source_notify(struct pa_source*s) {
+ assert(s);
+
+ if (s->notify)
+ s->notify(s);
+}
+
+static int do_post(void *p, uint32_t index, int *del, void*userdata) {
+ struct pa_memchunk *chunk = userdata;
+ struct pa_source_output *o = p;
+ assert(o && o->push && del && chunk);
+
+ pa_source_output_push(o, chunk);
+ return 0;
+}
+
+void pa_source_post(struct pa_source*s, struct pa_memchunk *chunk) {
+ assert(s && chunk);
+
+ pa_idxset_foreach(s->outputs, do_post, chunk);
+}
+
+struct pa_source* pa_source_get_default(struct pa_core *c) {
+ struct pa_source *source;
+ assert(c);
+
+ if ((source = pa_idxset_get_by_index(c->sources, c->default_source_index)))
+ return source;
+
+ if (!(source = pa_idxset_first(c->sources, &c->default_source_index)))
+ return NULL;
+
+ fprintf(stderr, "core: default source vanished, setting to %u.\n", source->index);
+ return source;
+}
+
+void pa_source_set_owner(struct pa_source *s, struct pa_module *m) {
+ assert(s);
+ s->owner = m;
+}
diff --git a/polyp/source.h b/polyp/source.h
new file mode 100644
index 00000000..9c584a6d
--- /dev/null
+++ b/polyp/source.h
@@ -0,0 +1,61 @@
+#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 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 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.
+***/
+
+struct pa_source;
+
+#include <inttypes.h>
+#include "core.h"
+#include "sample.h"
+#include "idxset.h"
+#include "memblock.h"
+#include "memchunk.h"
+#include "sink.h"
+
+struct pa_source {
+ uint32_t index;
+
+ char *name, *description;
+ struct pa_module *owner;
+ struct pa_core *core;
+ struct pa_sample_spec sample_spec;
+ struct pa_idxset *outputs;
+ struct pa_sink *monitor_of;
+
+ void (*notify)(struct pa_source*source);
+ void *userdata;
+};
+
+struct pa_source* pa_source_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec);
+void pa_source_free(struct pa_source *s);
+
+/* Pass a new memory block to all output streams */
+void pa_source_post(struct pa_source*s, struct pa_memchunk *b);
+
+void pa_source_notify(struct pa_source *s);
+
+struct pa_source* pa_source_get_default(struct pa_core *c);
+
+void pa_source_set_owner(struct pa_source *s, struct pa_module *m);
+
+#endif
diff --git a/polyp/strbuf.c b/polyp/strbuf.c
new file mode 100644
index 00000000..c6a3772d
--- /dev/null
+++ b/polyp/strbuf.c
@@ -0,0 +1,164 @@
+/* $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 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 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 "strbuf.h"
+
+struct chunk {
+ struct chunk *next;
+ size_t length;
+ char text[];
+};
+
+struct pa_strbuf {
+ size_t length;
+ struct chunk *head, *tail;
+};
+
+struct pa_strbuf *pa_strbuf_new(void) {
+ struct pa_strbuf *sb = malloc(sizeof(struct pa_strbuf));
+ assert(sb);
+ sb->length = 0;
+ sb->head = sb->tail = NULL;
+ return sb;
+}
+
+void pa_strbuf_free(struct pa_strbuf *sb) {
+ assert(sb);
+ while (sb->head) {
+ struct chunk *c = sb->head;
+ sb->head = sb->head->next;
+ free(c);
+ }
+
+ free(sb);
+}
+
+char *pa_strbuf_tostring(struct pa_strbuf *sb) {
+ char *t, *e;
+ struct chunk *c;
+ assert(sb);
+
+ t = malloc(sb->length+1);
+ assert(t);
+
+ e = t;
+ for (c = sb->head; c; c = c->next) {
+ memcpy(e, c->text, c->length);
+ e += c->length;
+ }
+
+ *e = 0;
+
+ return t;
+}
+
+char *pa_strbuf_tostring_free(struct pa_strbuf *sb) {
+ char *t;
+ assert(sb);
+ t = pa_strbuf_tostring(sb);
+ pa_strbuf_free(sb);
+ return t;
+}
+
+void pa_strbuf_puts(struct pa_strbuf *sb, const char *t) {
+ assert(sb && t);
+ pa_strbuf_putsn(sb, t, strlen(t));
+}
+
+void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) {
+ struct chunk *c;
+ assert(sb && t);
+
+ if (!l)
+ return;
+
+ c = malloc(sizeof(struct chunk)+l);
+ assert(c);
+
+ c->next = NULL;
+ c->length = l;
+ memcpy(c->text, t, l);
+
+ if (sb->tail) {
+ assert(sb->head);
+ sb->tail->next = c;
+ } else {
+ assert(!sb->head);
+ sb->head = c;
+ }
+
+ sb->tail = c;
+ sb->length += l;
+}
+
+/* The following is based on an example from the GNU libc documentation */
+
+int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) {
+ int r, size = 100;
+ struct chunk *c = NULL;
+
+ assert(sb);
+
+ for(;;) {
+ va_list ap;
+
+ c = realloc(c, sizeof(struct chunk)+size);
+ assert(c);
+
+ va_start(ap, format);
+ r = vsnprintf(c->text, size, format, ap);
+ va_end(ap);
+
+ if (r > -1 && r < size) {
+ c->length = r;
+ c->next = NULL;
+
+ if (sb->tail) {
+ assert(sb->head);
+ sb->tail->next = c;
+ } else {
+ assert(!sb->head);
+ sb->head = c;
+ }
+
+ sb->tail = c;
+ sb->length += r;
+
+ return r;
+ }
+
+ if (r > -1) /* glibc 2.1 */
+ size = r+1;
+ else /* glibc 2.0 */
+ size *= 2;
+ }
+}
diff --git a/polyp/strbuf.h b/polyp/strbuf.h
new file mode 100644
index 00000000..d672c42a
--- /dev/null
+++ b/polyp/strbuf.h
@@ -0,0 +1,36 @@
+#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 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 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.
+***/
+
+struct pa_strbuf;
+
+struct pa_strbuf *pa_strbuf_new(void);
+void pa_strbuf_free(struct pa_strbuf *sb);
+char *pa_strbuf_tostring(struct pa_strbuf *sb);
+char *pa_strbuf_tostring_free(struct pa_strbuf *sb);
+
+int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) __attribute__ ((format (printf, 2, 3)));;
+void pa_strbuf_puts(struct pa_strbuf *sb, const char *t);
+void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t m);
+
+#endif
diff --git a/polyp/tagstruct.c b/polyp/tagstruct.c
new file mode 100644
index 00000000..0d93c1e9
--- /dev/null
+++ b/polyp/tagstruct.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 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 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 <netinet/in.h>
+#include <assert.h>
+
+#include "tagstruct.h"
+
+enum tags {
+ TAG_STRING = 't',
+ TAG_U32 = 'L',
+ TAG_S32 = 'l',
+ TAG_U16 = 'S',
+ TAG_S16 = 's',
+ TAG_U8 = 'B',
+ TAG_S8 = 'b',
+ TAG_SAMPLE_SPEC = 'a',
+ TAG_ARBITRARY = 'x'
+};
+
+struct pa_tagstruct {
+ uint8_t *data;
+ size_t length, allocated;
+ size_t rindex;
+
+ int dynamic;
+};
+
+struct pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
+ struct pa_tagstruct*t;
+
+ assert(!data || (data && length));
+
+ t = malloc(sizeof(struct pa_tagstruct));
+ assert(t);
+ t->data = (uint8_t*) data;
+ t->allocated = t->length = data ? length : 0;
+ t->rindex = 0;
+ t->dynamic = !data;
+ return t;
+}
+
+void pa_tagstruct_free(struct pa_tagstruct*t) {
+ assert(t);
+ if (t->dynamic)
+ free(t->data);
+ free(t);
+}
+
+uint8_t* pa_tagstruct_free_data(struct pa_tagstruct*t, size_t *l) {
+ uint8_t *p;
+ assert(t && t->dynamic && l);
+ p = t->data;
+ *l = t->length;
+ free(t);
+ return p;
+}
+
+static void extend(struct pa_tagstruct*t, size_t l) {
+ assert(t && t->dynamic);
+
+ if (l <= t->allocated)
+ return;
+
+ t->data = realloc(t->data, t->allocated = l+100);
+ assert(t->data);
+}
+
+void pa_tagstruct_puts(struct pa_tagstruct*t, const char *s) {
+ size_t l;
+ assert(t && s);
+ l = strlen(s)+2;
+ extend(t, l);
+ t->data[t->length] = TAG_STRING;
+ strcpy(t->data+t->length+1, s);
+ t->length += l;
+}
+
+void pa_tagstruct_putu32(struct pa_tagstruct*t, uint32_t i) {
+ assert(t);
+ extend(t, 5);
+ t->data[t->length] = TAG_U32;
+ *((uint32_t*) (t->data+t->length+1)) = htonl(i);
+ t->length += 5;
+}
+
+void pa_tagstruct_putu8(struct pa_tagstruct*t, uint8_t c) {
+ assert(t);
+ extend(t, 2);
+ t->data[t->length] = TAG_U8;
+ *(t->data+t->length+1) = c;
+ t->length += 2;
+}
+
+void pa_tagstruct_put_sample_spec(struct pa_tagstruct *t, const struct pa_sample_spec *ss) {
+ assert(t && ss);
+ extend(t, 7);
+ t->data[t->length] = TAG_SAMPLE_SPEC;
+ t->data[t->length+1] = (uint8_t) ss->format;
+ t->data[t->length+2] = ss->channels;
+ *(uint32_t*) (t->data+t->length+3) = htonl(ss->rate);
+ t->length += 7;
+}
+
+
+void pa_tagstruct_put_arbitrary(struct pa_tagstruct *t, const void *p, size_t length) {
+ assert(t && p);
+
+ extend(t, 5+length);
+ t->data[t->length] = TAG_ARBITRARY;
+ *((uint32_t*) (t->data+t->length+1)) = htonl(length);
+ if (length)
+ memcpy(t->data+t->length+5, p, length);
+ t->length += 5+length;
+}
+
+int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s) {
+ int error = 0;
+ size_t n;
+ char *c;
+ assert(t && s);
+
+ if (t->rindex+2 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != 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(struct pa_tagstruct*t, uint32_t *i) {
+ assert(t && i);
+
+ if (t->rindex+5 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != TAG_U32)
+ return -1;
+
+ *i = ntohl(*((uint32_t*) (t->data+t->rindex+1)));
+ t->rindex += 5;
+ return 0;
+}
+
+int pa_tagstruct_getu8(struct pa_tagstruct*t, uint8_t *c) {
+ assert(t && c);
+
+ if (t->rindex+2 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != TAG_U8)
+ return -1;
+
+ *c = t->data[t->rindex+1];
+ t->rindex +=2;
+ return 0;
+}
+
+int pa_tagstruct_get_sample_spec(struct pa_tagstruct *t, struct pa_sample_spec *ss) {
+ assert(t && ss);
+
+ if (t->rindex+7 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != TAG_SAMPLE_SPEC)
+ return -1;
+
+ ss->format = t->data[t->rindex+1];
+ ss->channels = t->data[t->rindex+2];
+ ss->rate = ntohl(*(uint32_t*) (t->data+t->rindex+3));
+
+ t->rindex += 7;
+ return 0;
+}
+
+int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t length) {
+ assert(t && p);
+
+ if (t->rindex+5+length > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != TAG_ARBITRARY)
+ return -1;
+
+ if (ntohl(*((uint32_t*) (t->data+t->rindex+1))) != length)
+ return -1;
+
+ *p = t->data+t->rindex+5;
+ t->rindex += 5+length;
+ return 0;
+}
+
+int pa_tagstruct_eof(struct pa_tagstruct*t) {
+ assert(t);
+ return t->rindex >= t->length;
+}
+
+const uint8_t* pa_tagstruct_data(struct pa_tagstruct*t, size_t *l) {
+ assert(t && t->dynamic && l);
+ *l = t->length;
+ return t->data;
+}
+
diff --git a/polyp/tagstruct.h b/polyp/tagstruct.h
new file mode 100644
index 00000000..aefd03c4
--- /dev/null
+++ b/polyp/tagstruct.h
@@ -0,0 +1,53 @@
+#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 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 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 "sample.h"
+
+struct pa_tagstruct;
+
+struct pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length);
+void pa_tagstruct_free(struct pa_tagstruct*t);
+uint8_t* pa_tagstruct_free_data(struct pa_tagstruct*t, size_t *l);
+
+void pa_tagstruct_puts(struct pa_tagstruct*t, const char *s);
+void pa_tagstruct_putu32(struct pa_tagstruct*t, uint32_t i);
+void pa_tagstruct_putu8(struct pa_tagstruct*t, uint8_t c);
+void pa_tagstruct_put_sample_spec(struct pa_tagstruct *t, const struct pa_sample_spec *ss);
+void pa_tagstruct_put_arbitrary(struct pa_tagstruct*t, const void *p, size_t length);
+
+int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s);
+int pa_tagstruct_getu32(struct pa_tagstruct*t, uint32_t *i);
+int pa_tagstruct_getu8(struct pa_tagstruct*t, uint8_t *c);
+int pa_tagstruct_get_sample_spec(struct pa_tagstruct *t, struct pa_sample_spec *ss);
+int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t length);
+
+int pa_tagstruct_eof(struct pa_tagstruct*t);
+const uint8_t* pa_tagstruct_data(struct pa_tagstruct*t, size_t *l);
+
+
+
+#endif
diff --git a/polyp/tokenizer.c b/polyp/tokenizer.c
new file mode 100644
index 00000000..c7f18d26
--- /dev/null
+++ b/polyp/tokenizer.c
@@ -0,0 +1,89 @@
+/* $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 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 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"
+
+struct pa_tokenizer {
+ struct pa_dynarray *dynarray;
+};
+
+static void token_free(void *p, void *userdata) {
+ free(p);
+}
+
+static void parse(struct 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 = strndup(p, l);
+ assert(n);
+ pa_dynarray_append(a, n);
+ p += l;
+ p += strspn(p, delimiter);
+ args--;
+ }
+
+ if (args && *p) {
+ char *n = strdup(p);
+ assert(n);
+ pa_dynarray_append(a, n);
+ }
+}
+
+struct pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) {
+ struct pa_tokenizer *t;
+
+ t = malloc(sizeof(struct pa_tokenizer));
+ assert(t);
+ t->dynarray = pa_dynarray_new();
+ assert(t->dynarray);
+
+ parse(t->dynarray, s, args);
+ return t;
+}
+
+void pa_tokenizer_free(struct pa_tokenizer *t) {
+ assert(t);
+ pa_dynarray_free(t->dynarray, token_free, NULL);
+ free(t);
+}
+
+const char *pa_tokenizer_get(struct pa_tokenizer *t, unsigned i) {
+ assert(t);
+ return pa_dynarray_get(t->dynarray, i);
+}
diff --git a/polyp/tokenizer.h b/polyp/tokenizer.h
new file mode 100644
index 00000000..7d1cf9b5
--- /dev/null
+++ b/polyp/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 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 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.
+***/
+
+struct pa_tokenizer;
+
+struct pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args);
+void pa_tokenizer_free(struct pa_tokenizer *t);
+
+const char *pa_tokenizer_get(struct pa_tokenizer *t, unsigned i);
+
+#endif
diff --git a/polyp/util.c b/polyp/util.c
new file mode 100644
index 00000000..6e75c240
--- /dev/null
+++ b/polyp/util.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 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 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 <sys/types.h>
+
+#include "util.h"
+
+void pa_make_nonblock_fd(int fd) {
+ int v;
+
+ if ((v = fcntl(fd, F_GETFL)) >= 0)
+ if (!(v & O_NONBLOCK))
+ fcntl(fd, F_SETFL, v|O_NONBLOCK);
+}
+
+int pa_make_secure_dir(const char* dir) {
+ struct stat st;
+
+ if (mkdir(dir, 0700) < 0)
+ if (errno != EEXIST)
+ return -1;
+
+ if (lstat(dir, &st) < 0)
+ goto fail;
+
+ if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700))
+ goto fail;
+
+ return 0;
+
+fail:
+ rmdir(dir);
+ return -1;
+}
+
+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 += r;
+ size -= r;
+ }
+
+ return ret;
+}
+
+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 += r;
+ size -= r;
+ }
+
+ return ret;
+}
+
+void pa_check_for_sigpipe(void) {
+ struct sigaction sa;
+
+ if (sigaction(SIGPIPE, NULL, &sa) < 0) {
+ fprintf(stderr, __FILE__": sigaction() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ if (sa.sa_handler == SIG_DFL)
+ fprintf(stderr, "polypaudio: WARNING: SIGPIPE is not trapped. This might cause malfunction!\n");
+}
+
+/* The following is based on an example from the GNU libc documentation */
+char *pa_sprintf_malloc(const char *format, ...) {
+ int size = 100;
+ char *c = NULL;
+
+ assert(format);
+
+ for(;;) {
+ int r;
+ va_list ap;
+
+ c = realloc(c, size);
+ assert(c);
+
+ 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;
+ }
+}
diff --git a/polyp/util.h b/polyp/util.h
new file mode 100644
index 00000000..96fde11c
--- /dev/null
+++ b/polyp/util.h
@@ -0,0 +1,38 @@
+#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 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 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_make_nonblock_fd(int fd);
+
+int pa_make_secure_dir(const char* dir);
+
+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_for_sigpipe(void);
+
+char *pa_sprintf_malloc(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+
+#endif