]> code.delx.au - pulseaudio/commitdiff
Merge dead branch 'liboil-test'
authorCJ van den Berg <cj@vdbonline.com>
Mon, 23 Jun 2008 14:09:58 +0000 (16:09 +0200)
committerCJ van den Berg <cj@vdbonline.com>
Mon, 23 Jun 2008 14:10:01 +0000 (16:10 +0200)
609 files changed:
.gitignore [new file with mode: 0644]
GPL [new file with mode: 0644]
LGPL [new file with mode: 0644]
LICENSE
Makefile.am
PROTOCOL [new file with mode: 0644]
README [new file with mode: 0644]
acinclude.m4
autogen.sh [new file with mode: 0755]
bootstrap.sh
configure.ac
doc/FAQ.html.in [deleted file]
doc/Makefile.am [deleted file]
doc/README.html.in [deleted file]
doc/cli.html.in [deleted file]
doc/daemon.html.in [deleted file]
doc/modules.html.in [deleted file]
doc/style.css [deleted file]
doc/todo [deleted file]
doxygen/.gitignore [new file with mode: 0644]
doxygen/Makefile.am
doxygen/doxygen.conf.in
libpulse-browse.pc.in [new file with mode: 0644]
libpulse-mainloop-glib.pc.in [new file with mode: 0644]
libpulse-simple.pc.in [new file with mode: 0644]
libpulse.pc.in [new file with mode: 0644]
libtool.m4 [deleted file]
ltdl.m4 [deleted file]
man/.gitignore [new file with mode: 0644]
man/Makefile.am [new file with mode: 0644]
man/default.pa.5.xml.in [new file with mode: 0644]
man/esdcompat.1.xml.in [new file with mode: 0644]
man/pabrowse.1.xml.in [new file with mode: 0644]
man/pacat.1.xml.in [new file with mode: 0644]
man/pacmd.1.xml.in [new file with mode: 0644]
man/pactl.1.xml.in [new file with mode: 0644]
man/padsp.1.xml.in [new file with mode: 0644]
man/paplay.1.xml.in [new file with mode: 0644]
man/pasuspender.1.xml.in [new file with mode: 0644]
man/pax11publish.1.xml.in [new file with mode: 0644]
man/pulse-client.conf.5.xml.in [new file with mode: 0644]
man/pulse-daemon.conf.5.xml.in [new file with mode: 0644]
man/pulseaudio.1.xml.in [new file with mode: 0644]
man/xmltoman.css [new file with mode: 0644]
man/xmltoman.dtd [new file with mode: 0644]
man/xmltoman.xsl [new file with mode: 0644]
polyp/Makefile.am [deleted file]
polyp/alsa-util.c [deleted file]
polyp/alsa-util.h [deleted file]
polyp/authkey-prop.c [deleted file]
polyp/caps.c [deleted file]
polyp/channelmap.c [deleted file]
polyp/channelmap.h [deleted file]
polyp/cli-command.c [deleted file]
polyp/cli-text.c [deleted file]
polyp/cli-text.h [deleted file]
polyp/cli.c [deleted file]
polyp/client.c [deleted file]
polyp/client.conf.in [deleted file]
polyp/core.c [deleted file]
polyp/core.h [deleted file]
polyp/daemon-conf.c [deleted file]
polyp/daemon-conf.h [deleted file]
polyp/daemon.conf.in [deleted file]
polyp/default.pa.in [deleted file]
polyp/dumpmodules.c [deleted file]
polyp/endianmacros.h [deleted file]
polyp/gcc-printf.h [deleted file]
polyp/glib-mainloop.c [deleted file]
polyp/glib-mainloop.h [deleted file]
polyp/glib12-mainloop.c [deleted file]
polyp/howl-wrap.c [deleted file]
polyp/howl-wrap.h [deleted file]
polyp/iochannel.c [deleted file]
polyp/iochannel.h [deleted file]
polyp/llist.h [deleted file]
polyp/log.c [deleted file]
polyp/log.h [deleted file]
polyp/main.c [deleted file]
polyp/mainloop-api.c [deleted file]
polyp/mainloop-api.h [deleted file]
polyp/mainloop-signal.c [deleted file]
polyp/mainloop.c [deleted file]
polyp/mainloop.h [deleted file]
polyp/memblock.c [deleted file]
polyp/memblock.h [deleted file]
polyp/memblockq.c [deleted file]
polyp/memblockq.h [deleted file]
polyp/memchunk.c [deleted file]
polyp/memchunk.h [deleted file]
polyp/modargs.h [deleted file]
polyp/modinfo.c [deleted file]
polyp/module-alsa-sink.c [deleted file]
polyp/module-alsa-source.c [deleted file]
polyp/module-cli.c [deleted file]
polyp/module-combine.c [deleted file]
polyp/module-esound-sink.c [deleted file]
polyp/module-lirc.c [deleted file]
polyp/module-mmkbd-evdev.c [deleted file]
polyp/module-null-sink.c [deleted file]
polyp/module-oss-mmap.c [deleted file]
polyp/module-oss.c [deleted file]
polyp/module-pipe-sink.c [deleted file]
polyp/module-pipe-source.c [deleted file]
polyp/module-protocol-stub.c [deleted file]
polyp/module-sine.c [deleted file]
polyp/module-tunnel.c [deleted file]
polyp/module-x11-bell.c [deleted file]
polyp/module-x11-publish.c [deleted file]
polyp/module-zeroconf-publish.c [deleted file]
polyp/module.c [deleted file]
polyp/module.h [deleted file]
polyp/namereg.c [deleted file]
polyp/namereg.h [deleted file]
polyp/oss-util.c [deleted file]
polyp/oss-util.h [deleted file]
polyp/pacat.c [deleted file]
polyp/packet.c [deleted file]
polyp/pdispatch.h [deleted file]
polyp/pid.c [deleted file]
polyp/play-memchunk.c [deleted file]
polyp/polyplib-browser.c [deleted file]
polyp/polyplib-browser.h [deleted file]
polyp/polyplib-context.c [deleted file]
polyp/polyplib-context.h [deleted file]
polyp/polyplib-def.h [deleted file]
polyp/polyplib-error.c [deleted file]
polyp/polyplib-internal.h [deleted file]
polyp/polyplib-introspect.c [deleted file]
polyp/polyplib-introspect.h [deleted file]
polyp/polyplib-operation.c [deleted file]
polyp/polyplib-scache.c [deleted file]
polyp/polyplib-scache.h [deleted file]
polyp/polyplib-simple.c [deleted file]
polyp/polyplib-simple.h [deleted file]
polyp/polyplib-stream.c [deleted file]
polyp/polyplib-stream.h [deleted file]
polyp/polyplib-subscribe.c [deleted file]
polyp/polyplib-subscribe.h [deleted file]
polyp/polyplib.h [deleted file]
polyp/protocol-cli.c [deleted file]
polyp/protocol-cli.h [deleted file]
polyp/protocol-esound.c [deleted file]
polyp/protocol-esound.h [deleted file]
polyp/protocol-http.h [deleted file]
polyp/protocol-native.c [deleted file]
polyp/protocol-native.h [deleted file]
polyp/protocol-simple.c [deleted file]
polyp/protocol-simple.h [deleted file]
polyp/pstream-util.h [deleted file]
polyp/pstream.c [deleted file]
polyp/pstream.h [deleted file]
polyp/random.c [deleted file]
polyp/resampler.c [deleted file]
polyp/resampler.h [deleted file]
polyp/sample-util.c [deleted file]
polyp/sample-util.h [deleted file]
polyp/sample.c [deleted file]
polyp/sample.h [deleted file]
polyp/scache.c [deleted file]
polyp/scache.h [deleted file]
polyp/sconv-s16be.h [deleted file]
polyp/sconv-s16le.c [deleted file]
polyp/sconv-s16le.h [deleted file]
polyp/sconv.c [deleted file]
polyp/sconv.h [deleted file]
polyp/sink-input.c [deleted file]
polyp/sink-input.h [deleted file]
polyp/sink.c [deleted file]
polyp/sink.h [deleted file]
polyp/socket-client.c [deleted file]
polyp/socket-client.h [deleted file]
polyp/socket-server.c [deleted file]
polyp/socket-server.h [deleted file]
polyp/socket-util.c [deleted file]
polyp/sound-file-stream.c [deleted file]
polyp/sound-file.c [deleted file]
polyp/source-output.c [deleted file]
polyp/source-output.h [deleted file]
polyp/source.c [deleted file]
polyp/source.h [deleted file]
polyp/strbuf.h [deleted file]
polyp/strlist.c [deleted file]
polyp/subscribe.c [deleted file]
polyp/subscribe.h [deleted file]
polyp/tagstruct.c [deleted file]
polyp/tagstruct.h [deleted file]
polyp/typeid.c [deleted file]
polyp/typeid.h [deleted file]
polyp/util.c [deleted file]
polyp/util.h [deleted file]
polyp/volume.h [deleted file]
polyp/x11wrap.c [deleted file]
polyp/x11wrap.h [deleted file]
polyp/xmalloc.h [deleted file]
polyplib-browse.pc.in [deleted file]
polyplib-error.pc.in [deleted file]
polyplib-glib-mainloop.pc.in [deleted file]
polyplib-glib12-mainloop.pc.in [deleted file]
polyplib-mainloop.pc.in [deleted file]
polyplib-simple.pc.in [deleted file]
polyplib.pc.in [deleted file]
pulseaudio-text.svg [new file with mode: 0644]
pulseaudio.svg [new file with mode: 0644]
src/.gitignore [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/daemon/Makefile [new symlink]
src/daemon/caps.c [new file with mode: 0644]
src/daemon/caps.h [moved from polyp/caps.h with 61% similarity]
src/daemon/cmdline.c [moved from polyp/cmdline.c with 63% similarity]
src/daemon/cmdline.h [moved from polyp/cmdline.h with 71% similarity]
src/daemon/cpulimit.c [moved from polyp/cpulimit.c with 66% similarity]
src/daemon/cpulimit.h [moved from polyp/cpulimit.h with 65% similarity]
src/daemon/daemon-conf.c [new file with mode: 0644]
src/daemon/daemon-conf.h [new file with mode: 0644]
src/daemon/daemon.conf.in [new file with mode: 0644]
src/daemon/default.pa.in [new file with mode: 0755]
src/daemon/default.pa.win32 [new file with mode: 0644]
src/daemon/dumpmodules.c [new file with mode: 0644]
src/daemon/dumpmodules.h [moved from polyp/dumpmodules.h with 68% similarity]
src/daemon/esdcompat.in [moved from polyp/esdcompat.sh.in with 88% similarity]
src/daemon/ltdl-bind-now.c [new file with mode: 0644]
src/daemon/ltdl-bind-now.h [moved from polyp/sound-file.h with 54% similarity]
src/daemon/main.c [new file with mode: 0644]
src/daemon/org.pulseaudio.policy [new file with mode: 0644]
src/daemon/polkit.c [new file with mode: 0644]
src/daemon/polkit.h [new file with mode: 0644]
src/daemon/pulseaudio-module-xsmp.desktop [new file with mode: 0644]
src/depmod.py [moved from polyp/depmod.py with 89% similarity]
src/map-file [new file with mode: 0644]
src/modules/.gitignore [new file with mode: 0644]
src/modules/Makefile [new symlink]
src/modules/alsa-util.c [new file with mode: 0644]
src/modules/alsa-util.h [new file with mode: 0644]
src/modules/bt-proximity-helper.c [new file with mode: 0644]
src/modules/dbus-util.c [new file with mode: 0644]
src/modules/dbus-util.h [new file with mode: 0644]
src/modules/gconf/Makefile [new file with mode: 0644]
src/modules/gconf/gconf-helper.c [new file with mode: 0644]
src/modules/gconf/module-gconf.c [new file with mode: 0644]
src/modules/ladspa.h [new file with mode: 0644]
src/modules/module-alsa-sink.c [new file with mode: 0644]
src/modules/module-alsa-source.c [new file with mode: 0644]
src/modules/module-always-sink.c [new file with mode: 0644]
src/modules/module-bt-proximity.c [new file with mode: 0644]
src/modules/module-cli.c [new file with mode: 0644]
src/modules/module-combine.c [new file with mode: 0644]
src/modules/module-console-kit.c [new file with mode: 0644]
src/modules/module-default-device-restore.c [new file with mode: 0644]
src/modules/module-defs.h.m4 [moved from polyp/module-defs.h.m4 with 50% similarity]
src/modules/module-detect.c [new file with mode: 0644]
src/modules/module-device-restore.c [new file with mode: 0644]
src/modules/module-esound-compat-spawnfd.c [moved from polyp/module-esound-compat-spawnfd.c with 56% similarity]
src/modules/module-esound-compat-spawnpid.c [moved from polyp/module-esound-compat-spawnpid.c with 60% similarity]
src/modules/module-esound-sink.c [new file with mode: 0644]
src/modules/module-hal-detect.c [new file with mode: 0644]
src/modules/module-jack-sink.c [new file with mode: 0644]
src/modules/module-jack-source.c [new file with mode: 0644]
src/modules/module-ladspa-sink.c [new file with mode: 0644]
src/modules/module-lirc.c [new file with mode: 0644]
src/modules/module-match.c [moved from polyp/module-match.c with 56% similarity]
src/modules/module-mmkbd-evdev.c [new file with mode: 0644]
src/modules/module-native-protocol-fd.c [moved from polyp/module-native-protocol-fd.c with 53% similarity]
src/modules/module-null-sink.c [new file with mode: 0644]
src/modules/module-oss.c [new file with mode: 0644]
src/modules/module-pipe-sink.c [new file with mode: 0644]
src/modules/module-pipe-source.c [new file with mode: 0644]
src/modules/module-position-event-sounds.c [new file with mode: 0644]
src/modules/module-protocol-stub.c [new file with mode: 0644]
src/modules/module-remap-sink.c [new file with mode: 0644]
src/modules/module-rescue-streams.c [new file with mode: 0644]
src/modules/module-sine.c [new file with mode: 0644]
src/modules/module-solaris.c [new file with mode: 0644]
src/modules/module-suspend-on-idle.c [new file with mode: 0644]
src/modules/module-tunnel.c [new file with mode: 0644]
src/modules/module-volume-restore.c [new file with mode: 0644]
src/modules/module-waveout.c [new file with mode: 0644]
src/modules/module-x11-bell.c [new file with mode: 0644]
src/modules/module-x11-publish.c [new file with mode: 0644]
src/modules/module-x11-xsmp.c [new file with mode: 0644]
src/modules/module-zeroconf-discover.c [new file with mode: 0644]
src/modules/module-zeroconf-publish.c [new file with mode: 0644]
src/modules/oss-util.c [new file with mode: 0644]
src/modules/oss-util.h [new file with mode: 0644]
src/modules/rtp/Makefile [new file with mode: 0644]
src/modules/rtp/module-rtp-recv.c [new file with mode: 0644]
src/modules/rtp/module-rtp-send.c [new file with mode: 0644]
src/modules/rtp/rfc2327.txt [new file with mode: 0644]
src/modules/rtp/rfc2974.txt [new file with mode: 0644]
src/modules/rtp/rfc3550.txt [new file with mode: 0644]
src/modules/rtp/rfc3551.txt [new file with mode: 0644]
src/modules/rtp/rtp.c [new file with mode: 0644]
src/modules/rtp/rtp.h [new file with mode: 0644]
src/modules/rtp/sap.c [new file with mode: 0644]
src/modules/rtp/sap.h [new file with mode: 0644]
src/modules/rtp/sdp.c [new file with mode: 0644]
src/modules/rtp/sdp.h [new file with mode: 0644]
src/pulse/.gitignore [new file with mode: 0644]
src/pulse/Makefile [new file with mode: 0644]
src/pulse/browser.c [new file with mode: 0644]
src/pulse/browser.h [new file with mode: 0644]
src/pulse/cdecl.h [moved from polyp/cdecl.h with 76% similarity]
src/pulse/channelmap.c [new file with mode: 0644]
src/pulse/channelmap.h [new file with mode: 0644]
src/pulse/client-conf-x11.c [moved from polyp/client-conf-x11.c with 58% similarity]
src/pulse/client-conf-x11.h [moved from polyp/client-conf-x11.h with 68% similarity]
src/pulse/client-conf.c [moved from polyp/client-conf.c with 59% similarity]
src/pulse/client-conf.h [moved from polyp/client-conf.h with 60% similarity]
src/pulse/client.conf.in [new file with mode: 0644]
src/pulse/context.c [new file with mode: 0644]
src/pulse/context.h [new file with mode: 0644]
src/pulse/def.h [new file with mode: 0644]
src/pulse/error.c [new file with mode: 0644]
src/pulse/error.h [moved from polyp/polyplib-error.h with 61% similarity]
src/pulse/gccmacro.h [new file with mode: 0644]
src/pulse/glib-mainloop.c [new file with mode: 0644]
src/pulse/glib-mainloop.h [new file with mode: 0644]
src/pulse/internal.h [new file with mode: 0644]
src/pulse/introspect.c [new file with mode: 0644]
src/pulse/introspect.h [new file with mode: 0644]
src/pulse/mainloop-api.c [new file with mode: 0644]
src/pulse/mainloop-api.h [new file with mode: 0644]
src/pulse/mainloop-signal.c [new file with mode: 0644]
src/pulse/mainloop-signal.h [moved from polyp/mainloop-signal.h with 61% similarity]
src/pulse/mainloop.c [new file with mode: 0644]
src/pulse/mainloop.h [new file with mode: 0644]
src/pulse/operation.c [new file with mode: 0644]
src/pulse/operation.h [moved from polyp/polyplib-operation.h with 56% similarity]
src/pulse/proplist.c [new file with mode: 0644]
src/pulse/proplist.h [new file with mode: 0644]
src/pulse/pulseaudio.h [new file with mode: 0644]
src/pulse/sample.c [new file with mode: 0644]
src/pulse/sample.h [new file with mode: 0644]
src/pulse/scache.c [new file with mode: 0644]
src/pulse/scache.h [new file with mode: 0644]
src/pulse/simple.c [new file with mode: 0644]
src/pulse/simple.h [new file with mode: 0644]
src/pulse/stream.c [new file with mode: 0644]
src/pulse/stream.h [new file with mode: 0644]
src/pulse/subscribe.c [new file with mode: 0644]
src/pulse/subscribe.h [new file with mode: 0644]
src/pulse/thread-mainloop.c [new file with mode: 0644]
src/pulse/thread-mainloop.h [new file with mode: 0644]
src/pulse/timeval.c [new file with mode: 0644]
src/pulse/timeval.h [new file with mode: 0644]
src/pulse/utf8.c [new file with mode: 0644]
src/pulse/utf8.h [new file with mode: 0644]
src/pulse/util.c [new file with mode: 0644]
src/pulse/util.h [new file with mode: 0644]
src/pulse/version.h.in [moved from polyp/polyplib-version.h.in with 59% similarity]
src/pulse/volume.c [moved from polyp/volume.c with 50% similarity]
src/pulse/volume.h [new file with mode: 0644]
src/pulse/xmalloc.c [moved from polyp/xmalloc.c with 62% similarity]
src/pulse/xmalloc.h [new file with mode: 0644]
src/pulsecore/Makefile [new symlink]
src/pulsecore/asyncmsgq.c [new file with mode: 0644]
src/pulsecore/asyncmsgq.h [new file with mode: 0644]
src/pulsecore/asyncq.c [new file with mode: 0644]
src/pulsecore/asyncq.h [new file with mode: 0644]
src/pulsecore/atomic.h [new file with mode: 0644]
src/pulsecore/authkey-prop.c [new file with mode: 0644]
src/pulsecore/authkey-prop.h [moved from polyp/authkey-prop.h with 65% similarity]
src/pulsecore/authkey.c [moved from polyp/authkey.c with 53% similarity]
src/pulsecore/authkey.h [moved from polyp/authkey.h with 75% similarity]
src/pulsecore/autoload.c [moved from polyp/autoload.c with 56% similarity]
src/pulsecore/autoload.h [moved from polyp/autoload.h with 51% similarity]
src/pulsecore/avahi-wrap.c [new file with mode: 0644]
src/pulsecore/avahi-wrap.h [new file with mode: 0644]
src/pulsecore/cli-command.c [new file with mode: 0644]
src/pulsecore/cli-command.h [moved from polyp/cli-command.h with 50% similarity]
src/pulsecore/cli-text.c [new file with mode: 0644]
src/pulsecore/cli-text.h [new file with mode: 0644]
src/pulsecore/cli.c [new file with mode: 0644]
src/pulsecore/cli.h [moved from polyp/cli.h with 56% similarity]
src/pulsecore/client.c [new file with mode: 0644]
src/pulsecore/client.h [moved from polyp/client.h with 55% similarity]
src/pulsecore/conf-parser.c [moved from polyp/conf-parser.c with 58% similarity]
src/pulsecore/conf-parser.h [moved from polyp/conf-parser.h with 80% similarity]
src/pulsecore/core-error.c [new file with mode: 0644]
src/pulsecore/core-error.h [new file with mode: 0644]
src/pulsecore/core-scache.c [new file with mode: 0644]
src/pulsecore/core-scache.h [new file with mode: 0644]
src/pulsecore/core-subscribe.c [new file with mode: 0644]
src/pulsecore/core-subscribe.h [new file with mode: 0644]
src/pulsecore/core-util.c [new file with mode: 0644]
src/pulsecore/core-util.h [new file with mode: 0644]
src/pulsecore/core.c [new file with mode: 0644]
src/pulsecore/core.h [new file with mode: 0644]
src/pulsecore/creds.h [new file with mode: 0644]
src/pulsecore/dllmain.c [new file with mode: 0644]
src/pulsecore/dynarray.c [moved from polyp/dynarray.c with 63% similarity]
src/pulsecore/dynarray.h [moved from polyp/dynarray.h with 63% similarity]
src/pulsecore/endianmacros.h [new file with mode: 0644]
src/pulsecore/envelope.c [new file with mode: 0644]
src/pulsecore/envelope.h [new file with mode: 0644]
src/pulsecore/esound.h [moved from polyp/esound.h with 93% similarity]
src/pulsecore/fdsem.c [new file with mode: 0644]
src/pulsecore/fdsem.h [new file with mode: 0644]
src/pulsecore/ffmpeg/Makefile [new file with mode: 0644]
src/pulsecore/ffmpeg/avcodec.h [new file with mode: 0644]
src/pulsecore/ffmpeg/dsputil.h [new file with mode: 0644]
src/pulsecore/ffmpeg/resample2.c [new file with mode: 0644]
src/pulsecore/flist.c [new file with mode: 0644]
src/pulsecore/flist.h [new file with mode: 0644]
src/pulsecore/g711.c [moved from polyp/g711.c with 94% similarity]
src/pulsecore/g711.h [moved from polyp/g711.h with 92% similarity]
src/pulsecore/hashmap.c [moved from polyp/hashmap.c with 53% similarity]
src/pulsecore/hashmap.h [moved from polyp/hashmap.h with 51% similarity]
src/pulsecore/hook-list.c [new file with mode: 0644]
src/pulsecore/hook-list.h [new file with mode: 0644]
src/pulsecore/idxset.c [moved from polyp/idxset.c with 59% similarity]
src/pulsecore/idxset.h [moved from polyp/idxset.h with 62% similarity]
src/pulsecore/inet_ntop.c [new file with mode: 0644]
src/pulsecore/inet_ntop.h [new file with mode: 0644]
src/pulsecore/inet_pton.c [new file with mode: 0644]
src/pulsecore/inet_pton.h [new file with mode: 0644]
src/pulsecore/iochannel.c [new file with mode: 0644]
src/pulsecore/iochannel.h [new file with mode: 0644]
src/pulsecore/ioline.c [moved from polyp/ioline.c with 59% similarity]
src/pulsecore/ioline.h [moved from polyp/ioline.h with 52% similarity]
src/pulsecore/ipacl.c [new file with mode: 0644]
src/pulsecore/ipacl.h [new file with mode: 0644]
src/pulsecore/llist.h [new file with mode: 0644]
src/pulsecore/log.c [new file with mode: 0644]
src/pulsecore/log.h [new file with mode: 0644]
src/pulsecore/ltdl-helper.c [new file with mode: 0644]
src/pulsecore/ltdl-helper.h [moved from polyp/play-memchunk.h with 52% similarity]
src/pulsecore/macro.h [new file with mode: 0644]
src/pulsecore/mcalign.c [moved from polyp/mcalign.c with 63% similarity]
src/pulsecore/mcalign.h [moved from polyp/mcalign.h with 64% similarity]
src/pulsecore/memblock.c [new file with mode: 0644]
src/pulsecore/memblock.h [new file with mode: 0644]
src/pulsecore/memblockq.c [new file with mode: 0644]
src/pulsecore/memblockq.h [new file with mode: 0644]
src/pulsecore/memchunk.c [new file with mode: 0644]
src/pulsecore/memchunk.h [new file with mode: 0644]
src/pulsecore/modargs.c [moved from polyp/modargs.c with 63% similarity]
src/pulsecore/modargs.h [new file with mode: 0644]
src/pulsecore/modinfo.c [new file with mode: 0644]
src/pulsecore/modinfo.h [moved from polyp/modinfo.h with 56% similarity]
src/pulsecore/module.c [new file with mode: 0644]
src/pulsecore/module.h [new file with mode: 0644]
src/pulsecore/msgobject.c [new file with mode: 0644]
src/pulsecore/msgobject.h [new file with mode: 0644]
src/pulsecore/mutex-posix.c [new file with mode: 0644]
src/pulsecore/mutex-win32.c [new file with mode: 0644]
src/pulsecore/mutex.h [new file with mode: 0644]
src/pulsecore/namereg.c [new file with mode: 0644]
src/pulsecore/namereg.h [new file with mode: 0644]
src/pulsecore/native-common.h [moved from polyp/native-common.h with 57% similarity]
src/pulsecore/object.c [new file with mode: 0644]
src/pulsecore/object.h [new file with mode: 0644]
src/pulsecore/once.c [new file with mode: 0644]
src/pulsecore/once.h [new file with mode: 0644]
src/pulsecore/packet.c [new file with mode: 0644]
src/pulsecore/packet.h [moved from polyp/packet.h with 57% similarity]
src/pulsecore/parseaddr.c [moved from polyp/parseaddr.c with 76% similarity]
src/pulsecore/parseaddr.h [moved from polyp/parseaddr.h with 61% similarity]
src/pulsecore/pdispatch.c [moved from polyp/pdispatch.c with 57% similarity]
src/pulsecore/pdispatch.h [new file with mode: 0644]
src/pulsecore/pid.c [new file with mode: 0644]
src/pulsecore/pid.h [moved from polyp/pid.h with 57% similarity]
src/pulsecore/pipe.c [new file with mode: 0644]
src/pulsecore/pipe.h [new file with mode: 0644]
src/pulsecore/play-memblockq.c [new file with mode: 0644]
src/pulsecore/play-memblockq.h [new file with mode: 0644]
src/pulsecore/play-memchunk.c [new file with mode: 0644]
src/pulsecore/play-memchunk.h [new file with mode: 0644]
src/pulsecore/poll.c [new file with mode: 0644]
src/pulsecore/poll.h [new file with mode: 0644]
src/pulsecore/proplist-util.c [new file with mode: 0644]
src/pulsecore/proplist-util.h [new file with mode: 0644]
src/pulsecore/props.c [moved from polyp/props.c with 53% similarity]
src/pulsecore/props.h [moved from polyp/props.h with 67% similarity]
src/pulsecore/protocol-cli.c [new file with mode: 0644]
src/pulsecore/protocol-cli.h [new file with mode: 0644]
src/pulsecore/protocol-esound.c [new file with mode: 0644]
src/pulsecore/protocol-esound.h [new file with mode: 0644]
src/pulsecore/protocol-http.c [moved from polyp/protocol-http.c with 74% similarity]
src/pulsecore/protocol-http.h [new file with mode: 0644]
src/pulsecore/protocol-native.c [new file with mode: 0644]
src/pulsecore/protocol-native.h [new file with mode: 0644]
src/pulsecore/protocol-simple.c [new file with mode: 0644]
src/pulsecore/protocol-simple.h [new file with mode: 0644]
src/pulsecore/pstream-util.c [moved from polyp/pstream-util.c with 50% similarity]
src/pulsecore/pstream-util.h [new file with mode: 0644]
src/pulsecore/pstream.c [new file with mode: 0644]
src/pulsecore/pstream.h [new file with mode: 0644]
src/pulsecore/queue.c [moved from polyp/queue.c with 50% similarity]
src/pulsecore/queue.h [moved from polyp/queue.h with 61% similarity]
src/pulsecore/random.c [new file with mode: 0644]
src/pulsecore/random.h [moved from polyp/random.h with 62% similarity]
src/pulsecore/refcnt.h [new file with mode: 0644]
src/pulsecore/resampler.c [new file with mode: 0644]
src/pulsecore/resampler.h [new file with mode: 0644]
src/pulsecore/rtclock.c [new file with mode: 0644]
src/pulsecore/rtclock.h [new file with mode: 0644]
src/pulsecore/rtpoll.c [new file with mode: 0644]
src/pulsecore/rtpoll.h [new file with mode: 0644]
src/pulsecore/rtsig.c [new file with mode: 0644]
src/pulsecore/rtsig.h [new file with mode: 0644]
src/pulsecore/sample-util.c [new file with mode: 0644]
src/pulsecore/sample-util.h [new file with mode: 0644]
src/pulsecore/sconv-s16be.c [new file with mode: 0644]
src/pulsecore/sconv-s16be.h [new file with mode: 0644]
src/pulsecore/sconv-s16le.c [new file with mode: 0644]
src/pulsecore/sconv-s16le.h [new file with mode: 0644]
src/pulsecore/sconv.c [new file with mode: 0644]
src/pulsecore/sconv.h [new file with mode: 0644]
src/pulsecore/semaphore-posix.c [new file with mode: 0644]
src/pulsecore/semaphore-win32.c [new file with mode: 0644]
src/pulsecore/semaphore.h [new file with mode: 0644]
src/pulsecore/shm.c [new file with mode: 0644]
src/pulsecore/shm.h [new file with mode: 0644]
src/pulsecore/shmasyncq.c [new file with mode: 0644]
src/pulsecore/shmasyncq.h [new file with mode: 0644]
src/pulsecore/sink-input.c [new file with mode: 0644]
src/pulsecore/sink-input.h [new file with mode: 0644]
src/pulsecore/sink.c [new file with mode: 0644]
src/pulsecore/sink.h [new file with mode: 0644]
src/pulsecore/sioman.c [moved from polyp/sioman.c with 57% similarity]
src/pulsecore/sioman.h [moved from polyp/sioman.h with 70% similarity]
src/pulsecore/socket-client.c [new file with mode: 0644]
src/pulsecore/socket-client.h [new file with mode: 0644]
src/pulsecore/socket-server.c [new file with mode: 0644]
src/pulsecore/socket-server.h [new file with mode: 0644]
src/pulsecore/socket-util.c [new file with mode: 0644]
src/pulsecore/socket-util.h [moved from polyp/socket-util.h with 58% similarity]
src/pulsecore/sound-file-stream.c [new file with mode: 0644]
src/pulsecore/sound-file-stream.h [moved from polyp/sound-file-stream.h with 61% similarity]
src/pulsecore/sound-file.c [new file with mode: 0644]
src/pulsecore/sound-file.h [new file with mode: 0644]
src/pulsecore/source-output.c [new file with mode: 0644]
src/pulsecore/source-output.h [new file with mode: 0644]
src/pulsecore/source.c [new file with mode: 0644]
src/pulsecore/source.h [new file with mode: 0644]
src/pulsecore/speex/Makefile [new file with mode: 0644]
src/pulsecore/speex/arch.h [new file with mode: 0644]
src/pulsecore/speex/fixed_generic.h [new file with mode: 0644]
src/pulsecore/speex/resample.c [new file with mode: 0644]
src/pulsecore/speex/speex_resampler.h [new file with mode: 0644]
src/pulsecore/speexwrap.h [new file with mode: 0644]
src/pulsecore/start-child.c [new file with mode: 0644]
src/pulsecore/start-child.h [new file with mode: 0644]
src/pulsecore/strbuf.c [moved from polyp/strbuf.c with 64% similarity]
src/pulsecore/strbuf.h [new file with mode: 0644]
src/pulsecore/strlist.c [new file with mode: 0644]
src/pulsecore/strlist.h [moved from polyp/strlist.h with 62% similarity]
src/pulsecore/tagstruct.c [new file with mode: 0644]
src/pulsecore/tagstruct.h [new file with mode: 0644]
src/pulsecore/thread-mq.c [new file with mode: 0644]
src/pulsecore/thread-mq.h [new file with mode: 0644]
src/pulsecore/thread-posix.c [new file with mode: 0644]
src/pulsecore/thread-win32.c [new file with mode: 0644]
src/pulsecore/thread.h [new file with mode: 0644]
src/pulsecore/time-smoother.c [new file with mode: 0644]
src/pulsecore/time-smoother.h [new file with mode: 0644]
src/pulsecore/tokenizer.c [moved from polyp/tokenizer.c with 52% similarity]
src/pulsecore/tokenizer.h [moved from polyp/tokenizer.h with 55% similarity]
src/pulsecore/winsock.h [new file with mode: 0644]
src/pulsecore/x11prop.c [moved from polyp/x11prop.c with 83% similarity]
src/pulsecore/x11prop.h [moved from polyp/x11prop.h with 75% similarity]
src/pulsecore/x11wrap.c [new file with mode: 0644]
src/pulsecore/x11wrap.h [new file with mode: 0644]
src/tests/Makefile [new symlink]
src/tests/asyncmsgq-test.c [new file with mode: 0644]
src/tests/asyncq-test.c [new file with mode: 0644]
src/tests/channelmap-test.c [new file with mode: 0644]
src/tests/close-test.c [new file with mode: 0644]
src/tests/cpulimit-test.c [moved from polyp/cpulimit-test.c with 72% similarity]
src/tests/envelope-test.c [new file with mode: 0644]
src/tests/flist-test.c [new file with mode: 0644]
src/tests/get-binary-name-test.c [moved from polyp/sconv-s16be.c with 54% similarity]
src/tests/hook-list-test.c [new file with mode: 0644]
src/tests/interpol-test.c [new file with mode: 0644]
src/tests/ipacl-test.c [new file with mode: 0644]
src/tests/mainloop-test.c [moved from polyp/mainloop-test.c with 59% similarity]
src/tests/mcalign-test.c [moved from polyp/mcalign-test.c with 56% similarity]
src/tests/memblock-test.c [new file with mode: 0644]
src/tests/memblockq-test.c [new file with mode: 0644]
src/tests/mix-test.c [new file with mode: 0644]
src/tests/pacat-simple.c [moved from polyp/pacat-simple.c with 64% similarity]
src/tests/parec-simple.c [moved from polyp/parec-simple.c with 79% similarity]
src/tests/proplist-test.c [new file with mode: 0644]
src/tests/queue-test.c [new file with mode: 0644]
src/tests/remix-test.c [new file with mode: 0644]
src/tests/resampler-test.c [new file with mode: 0644]
src/tests/rtpoll-test.c [new file with mode: 0644]
src/tests/rtstutter.c [new file with mode: 0644]
src/tests/sig2str-test.c [new file with mode: 0644]
src/tests/smoother-test.c [new file with mode: 0644]
src/tests/stripnul.c [new file with mode: 0644]
src/tests/strlist-test.c [moved from polyp/strlist-test.c with 79% similarity]
src/tests/sync-playback.c [new file with mode: 0644]
src/tests/thread-mainloop-test.c [new file with mode: 0644]
src/tests/thread-test.c [new file with mode: 0644]
src/tests/utf8-test.c [new file with mode: 0644]
src/tests/voltest.c [moved from polyp/voltest.c with 76% similarity]
src/utils/Makefile [new symlink]
src/utils/pabrowse.c [moved from polyp/pabrowse.c with 61% similarity]
src/utils/pacat.c [new file with mode: 0644]
src/utils/pacmd.c [moved from polyp/pacmd.c with 70% similarity]
src/utils/pactl.c [moved from polyp/pactl.c with 54% similarity]
src/utils/padsp [new file with mode: 0755]
src/utils/padsp.c [new file with mode: 0644]
src/utils/paplay.c [moved from polyp/paplay.c with 64% similarity]
src/utils/pasuspender.c [new file with mode: 0644]
src/utils/pax11publish.c [moved from polyp/pax11publish.c with 60% similarity]
todo [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..cadee1c
--- /dev/null
@@ -0,0 +1,22 @@
+*.tar.gz
+*.pc
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config.rpath
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libltdl
+libtool
+ltmain.sh
+missing
+stamp-*
diff --git a/GPL b/GPL
new file mode 100644 (file)
index 0000000..b7b5f53
--- /dev/null
+++ b/GPL
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program 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.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY 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 this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/LGPL b/LGPL
new file mode 100644 (file)
index 0000000..2d2d780
--- /dev/null
+++ b/LGPL
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/LICENSE b/LICENSE
index b124cf581250c210960185e8fbf6c967a0538721..612c234155cc3f2f63e40223f13c09db5ba8e236 100644 (file)
--- a/LICENSE
+++ b/LICENSE
+All PulseAudio source files are licensed under the GNU Lesser General Public
+License. (see file LGPL for details)
 
-                  GNU LESSER GENERAL PUBLIC LICENSE
-                       Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-                            Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations
-below.
-
-  When we speak of free software, we are referring to freedom of use,
-not price.  Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-\f
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it
-becomes a de-facto standard.  To achieve this, non-free programs must
-be allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-\f
-                  GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control
-compilation and installation of the library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-\f
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-\f
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-\f
-  6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Use a suitable shared library mechanism for linking with the
-    Library.  A suitable mechanism is one that (1) uses at run time a
-    copy of the library already present on the user's computer system,
-    rather than copying library functions into the executable, and (2)
-    will operate properly with a modified version of the library, if
-    the user installs one, as long as the modified version is
-    interface-compatible with the version that the work was made with.
-
-    c) Accompany the work with a written offer, valid for at least
-    three years, to give the same user the materials specified in
-    Subsection 6a, above, for a charge no more than the cost of
-    performing this distribution.
-
-    d) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    e) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-\f
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-\f
-  11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply, and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License
-may add an explicit geographical distribution limitation excluding those
-countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-\f
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
-                            NO WARRANTY
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-                     END OF TERMS AND CONDITIONS
-\f
-           How to Apply These Terms to Your New Libraries
-
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms
-of the ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.
-It is safest to attach them to the start of each source file to most
-effectively convey the exclusion of warranty; and each file should
-have at least the "copyright" line and a pointer to where the full
-notice is found.
-
-
-    <one line to give the library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or
-your school, if any, to sign a "copyright disclaimer" for the library,
-if necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James
-  Random Hacker.
-
-  <signature of Ty Coon>, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!
+However, the server side links to the GPL-only library 'libsamplerate' which
+practically downgrades the license of the server part to GPL (see file GPL for
+details), exercising section 3 of the LGPL.
 
+Hence you should treat the client library ('libpulse') of PulseAudio as being
+LGPL licensed and the server part ('libpulsecore') as being GPL licensed. Since
+the PulseAudio daemon and the modules link to 'libpulsecore' they are of course
+also GPL licensed.
 
+-- Lennart Poettering, April 20th, 2006.
index 7935321da2d8464ec32a82b6ac53403c5a239bb5..613965757a572b8c962632e02c69af48323593b1 100644 (file)
@@ -1,58 +1,62 @@
-# $Id$
+# This file is part of PulseAudio.
 #
-# This file is part of polypaudio.
-#
-# polypaudio is free software; you can redistribute it and/or modify
+# PulseAudio is free software; you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation; either version 2 of the License, or
 # (at your option) any later version.
 #
-# polypaudio is distributed in the hope that it will be useful, but
+# PulseAudio is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software
+# along with PulseAudio; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 # USA.
 
-EXTRA_DIST = bootstrap.sh README LICENSE doxygen/Makefile.am doxygen/Makefile.in doxygen/doxygen.conf.in libtool.m4 ltdl.m4
-SUBDIRS=polyp doc libltdl
+EXTRA_DIST = bootstrap.sh LICENSE GPL LGPL doxygen/Makefile.am doxygen/Makefile.in doxygen/doxygen.conf.in README todo
+SUBDIRS=src doxygen man
 
-MAINTAINERCLEANFILES=README
-noinst_DATA = README
+MAINTAINERCLEANFILES =
+noinst_DATA =
 
 pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = polyplib.pc polyplib-simple.pc polyplib-error.pc polyplib-mainloop.pc polyplib-browse.pc
+pkgconfig_DATA = libpulse.pc libpulse-simple.pc
 
-if HAVE_GLIB20
+if HAVE_AVAHI
 pkgconfig_DATA += \
-        polyplib-glib-mainloop.pc
+        libpulse-browse.pc
 endif
 
-if HAVE_GLIB12
+if HAVE_GLIB20
 pkgconfig_DATA += \
-       polyplib-glib12-mainloop.pc
+        libpulse-mainloop-glib.pc
 endif
 
-README:
-       rm -f README
-       $(MAKE) -C doc README
-       cd $(srcdir) && ln -s doc/README README
-
 homepage: all dist doxygen
        test -d $$HOME/homepage/private
-       mkdir -p $$HOME/homepage/private/projects/polypaudio $$HOME/homepage/private/projects/polypaudio/doxygen
-       cp polypaudio-@PACKAGE_VERSION@.tar.gz $$HOME/homepage/private/projects/polypaudio
-       cp doc/README.html doc/FAQ.html doc/cli.html doc/daemon.html doc/modules.html doc/style.css $$HOME/homepage/private/projects/polypaudio
-       cp -a doxygen/html/* $$HOME/homepage/private/projects/polypaudio/doxygen
-       cp $$HOME/homepage/private/projects/polypaudio/README.html $$HOME/homepage/private/projects/polypaudio/index.html
-
-distcleancheck:
-       @:
+       mkdir -p $$HOME/homepage/private/projects/pulseaudio $$HOME/homepage/private/projects/pulseaudio/doxygen
+       cp pulseaudio-@PACKAGE_VERSION@.tar.gz $$HOME/homepage/private/projects/pulseaudio
+       cp -a doxygen/html/* $$HOME/homepage/private/projects/pulseaudio/doxygen
 
 doxygen:
        $(MAKE) -C doxygen doxygen
 
+eolspace:
+       find \( -name '*.c' -o -name '*.h' -o -name 'Makefile.am' \) -exec perl -i -pe 's/\s+\n$$/\1\n/;' \{\} \;
+
+untabify:
+       find \( -name '*.c' -o -name '*.h' \) -exec perl -i -pe 's/\t/        /g;' \{\} \;
+
+fedora-snapshot: dist
+       cp $(distdir).tar.gz $$HOME/cvs.fedora/pulseaudio/devel/$(distdir).svn`date +%Y%m%d`.tar.gz
+
+dist-hook:
+       if test -d .svn ; then \
+               svn update ; \
+               chmod u+w ${distdir}/ChangeLog || true ; \
+               svn2cl -o ${distdir}/ChangeLog ; \
+       fi
+
 .PHONY: homepage distcleancheck doxygen
diff --git a/PROTOCOL b/PROTOCOL
new file mode 100644 (file)
index 0000000..4439c71
--- /dev/null
+++ b/PROTOCOL
@@ -0,0 +1,136 @@
+### v8, implemented by >= 0.8
+
+First version supported.
+
+### v9, implemented by >= 0.9.0
+
+Reply for PA_COMMAND_CREATE_PLAYBACK_STREAM,
+PA_COMMAND_CREATE_RECORD_STREAM now returns buffer_attrs that are used:
+
+Four new fields in reply of PA_COMMAND_CREATE_PLAYBACK_STREAM:
+
+   maxlength
+   tlength
+   prebuf
+   minreq
+
+Two new fields in reply of PA_COMMAND_CREATE_RECORD_STREAM:
+
+   maxlength
+   fragsize
+
+### v10, implemented by >= 0.9.5
+
+New opcodes:
+
+ PA_COMMAND_MOVE_SINK_INPUT
+ PA_COMMAND_MOVE_SOURCE_OUTPUT
+
+SHM data transfer support
+
+### v11, implemented by >= 0.9.7
+
+Reply to to PA_COMMAND_GET_SINK_INPUT_INFO, PA_COMMAND_GET_SINK_INPUT_INFO_LIST gets new field at the end:
+
+ mute
+
+New opcodes:
+
+ PA_COMMAND_SET_SINK_INPUT_MUTE
+ PA_COMMAND_SUSPEND_SINK
+ PA_COMMAND_SUSPEND_SOURCE
+
+### v12, implemented by >= 0.9.8
+
+S32LE, S32BE is now known as sample spec.
+
+Gained six new bool fields for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM request at the end:
+
+ no_remap_channels
+ no_remix_channels
+ fix_format
+ fix_rate
+ fix_channels
+ no_move
+ variable_rate
+
+Reply to these opcodes now includes:
+
+ sample_spec
+ channel_map
+ device_index
+ device_name
+ suspended
+
+New opcodes for changing buffer attrs:
+
+ PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR
+ PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR
+
+New opcodes for changing sampling rate:
+
+ PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE
+ PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE
+
+New opcodes for notifications:
+
+ PA_COMMAND_PLAYBACK_STREAM_SUSPENDED
+ PA_COMMAND_CAPTURE_STREAM_SUSPENDED
+ PA_COMMAND_PLAYBACK_STREAM_MOVED
+ PA_COMMAND_CAPTURE_STREAM_MOVED
+
+### v13, implemented  by >= 0.9.11
+
+New fields for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM request at the end:
+
+ peak_detect (bool)
+ adjust_latency  (bool)
+
+Replace field "name" for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM at the end:
+
+ proplist
+
+Replace field "name" for PA_COMMAND_SET_CLIENT_NAME request at the end:
+
+ proplist
+
+On response of PA_COMMAND_SET_CLIENT_NAME:
+
+ client_index
+
+New proplist field for sink, source, sink input, source output introspection opcodes and at the end:
+
+ proplist
+
+New opcodes for proplist modifications
+
+  PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST
+  PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST
+  PA_COMMAND_UPDATE_CLIENT_PROPLIST
+  PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST
+  PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST
+  PA_COMMAND_REMOVE_CLIENT_PROPLIST
+
+New field for PA_COMMAND_PLAY_SAMPLE:
+
+  proplist
+
+New field for PA_COMMAND_PLAY_SAMPLE response:
+
+  idx
+
+New field for PA_COMMAND_CREATE_PLAYBACK_STREAM at the end:
+
+  start_muted
+
+Buffer attributes for PA_COMMAND_CREATE_PLAYBACK_STREAM and
+PA_COMMAND_CREATE_RECORD_STREAM may now be 0 for default values.
+
+New filed for PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR at the end:
+
+  adjust_latency (bool)
+
+new message:
+
+  PA_COMMAND_STARTED
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..6ca8d29
--- /dev/null
+++ b/README
@@ -0,0 +1,34 @@
+PULSEAUDIO SOUND SERVER
+
+WEB SITE:
+       http://pulseaudio.org/
+
+GIT:
+       git://git.0pointer.de/pulseaudio.git
+
+GITWEB:
+       http://git.0pointer.de/?p=pulseaudio.git;a=summary
+
+MAILING LIST:
+       https://tango.0pointer.de/mailman/listinfo/pulseaudio-discuss
+
+GIT COMMITS MAILING LIST:
+       https://tango.0pointer.de/mailman/listinfo/pulseaudio-commits
+
+TRAC TICKET CHANGES MAILING LIST:
+       https://tango.0pointer.de/mailman/listinfo/pulseaudio-tickets
+
+IRC:
+       #pulseaudio on irc.freenode.org
+
+CIA:
+       http://cia.navi.cx/stats/project/polypaudio
+
+FRESHMEAT:
+       http://freshmeat.net/projects/pulseaudio/
+
+OHLOH:
+       http://www.ohloh.net/projects/4038
+
+AUTHORS:
+       Several
index bedf51c32afc45cafcb0042f397995ee0c781468..02c051868dc79ff39b27a7baf2ebb90f54121d01 100644 (file)
@@ -1,6 +1,55 @@
-dnl Available from the GNU Autoconf Macro Archive at:
-dnl http://www.gnu.org/software/ac-archive/htmldoc/acx_pthread.html
+dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
 dnl
+dnl @summary figure out how to build C programs using POSIX threads
+dnl
+dnl This macro figures out how to build C programs using POSIX threads.
+dnl It sets the PTHREAD_LIBS output variable to the threads library and
+dnl linker flags, and the PTHREAD_CFLAGS output variable to any special
+dnl C compiler flags that are needed. (The user can also force certain
+dnl compiler flags/libs to be tested by setting these environment
+dnl variables.)
+dnl
+dnl Also sets PTHREAD_CC to any special C compiler that is needed for
+dnl multi-threaded programs (defaults to the value of CC otherwise).
+dnl (This is necessary on AIX to use the special cc_r compiler alias.)
+dnl
+dnl NOTE: You are assumed to not only compile your program with these
+dnl flags, but also link it with them as well. e.g. you should link
+dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
+dnl $LIBS
+dnl
+dnl If you are only building threads programs, you may wish to use
+dnl these variables in your default LIBS, CFLAGS, and CC:
+dnl
+dnl        LIBS="$PTHREAD_LIBS $LIBS"
+dnl        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+dnl        CC="$PTHREAD_CC"
+dnl
+dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
+dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
+dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+dnl
+dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
+dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to
+dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the
+dnl default action will define HAVE_PTHREAD.
+dnl
+dnl Please let the authors know if this macro fails on any platform, or
+dnl if you have any other suggestions or comments. This macro was based
+dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with
+dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros
+dnl posted by Alejandro Forero Cuervo to the autoconf macro repository.
+dnl We are also grateful for the helpful feedback of numerous users.
+dnl
+dnl @category InstalledPackages
+dnl @author Steven G. Johnson <stevenj@alum.mit.edu>
+dnl @version 2006-05-29
+dnl @license GPLWithACException
+dnl
+dnl Checks for GCC shared/pthread inconsistency based on work by
+dnl Marcin Owsiany <marcin@owsiany.pl>
+
+
 AC_DEFUN([ACX_PTHREAD], [
 AC_REQUIRE([AC_CANONICAL_HOST])
 AC_LANG_SAVE
@@ -57,6 +106,7 @@ acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -m
 # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
 #      doesn't hurt to check since this sometimes defines pthreads too;
 #      also defines -D_REENTRANT)
+#      ... -mt is also the pthreads flag for HP/aCC
 # pthread: Linux, etcetera
 # --thread-safe: KAI C++
 # pthread-config: use pthread-config program (for GNU Pth library)
@@ -66,13 +116,13 @@ case "${host_cpu}-${host_os}" in
 
         # On Solaris (at least, for some versions), libc contains stubbed
         # (non-functional) versions of the pthreads routines, so link-based
-        # tests will erroneously succeed.  (We need to link with -pthread or
+        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
         # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
         # a function called by this macro, so we could check for that, but
         # who knows whether they'll stub that too in a future libc.)  So,
         # we'll just look for -pthreads and -lpthread first:
 
-        acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
+        acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
         ;;
 esac
 
@@ -142,43 +192,142 @@ if test "x$acx_pthread_ok" = xyes; then
         save_CFLAGS="$CFLAGS"
         CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
 
-        # Detect AIX lossage: threads are created detached by default
-        # and the JOINABLE attribute has a nonstandard name (UNDETACHED).
-        AC_MSG_CHECKING([for joinable pthread attribute])
-        AC_TRY_LINK([#include <pthread.h>],
-                    [int attr=PTHREAD_CREATE_JOINABLE;],
-                    ok=PTHREAD_CREATE_JOINABLE, ok=unknown)
-        if test x"$ok" = xunknown; then
-                AC_TRY_LINK([#include <pthread.h>],
-                            [int attr=PTHREAD_CREATE_UNDETACHED;],
-                            ok=PTHREAD_CREATE_UNDETACHED, ok=unknown)
-        fi
-        if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then
-                AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok,
-                          [Define to the necessary symbol if this constant
-                           uses a non-standard name on your system.])
-        fi
-        AC_MSG_RESULT(${ok})
-        if test x"$ok" = xunknown; then
-                AC_MSG_WARN([we do not know how to create joinable pthreads])
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+       AC_MSG_CHECKING([for joinable pthread attribute])
+       attr_name=unknown
+       for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+           AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
+                        [attr_name=$attr; break])
+       done
+        AC_MSG_RESULT($attr_name)
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
         fi
 
         AC_MSG_CHECKING([if more special flags are required for pthreads])
         flag=no
         case "${host_cpu}-${host_os}" in
-                *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
-                *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+            *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+            *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
         esac
         AC_MSG_RESULT(${flag})
         if test "x$flag" != xno; then
-                PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
         fi
 
         LIBS="$save_LIBS"
         CFLAGS="$save_CFLAGS"
+        # More AIX lossage: must compile with xlc_r or cc_r
+       if test x"$GCC" != xyes; then
+          AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+        else
+          PTHREAD_CC=$CC
+       fi
+
+   # The next part tries to detect GCC inconsistency with -shared on some
+   # architectures and systems. The problem is that in certain
+   # configurations, when -shared is specified, GCC "forgets" to
+   # internally use various flags which are still necessary.
+
+   AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies])
+   check_inconsistencies=yes
+   case "${host_cpu}-${host_os}" in
+     *-darwin*) check_inconsistencies=no ;;
+   esac
+   if test x"$GCC" != xyes -o "x$check_inconsistencies" != xyes ; then
+      AC_MSG_RESULT([no])
+   else
+      AC_MSG_RESULT([yes])
+
+      # In order not to create several levels of indentation, we test
+      # the value of "$ok" until we find out the cure or run out of
+      # ideas.
+      ok="no"
+
+      #
+      # Prepare the flags
+      #
+      save_CFLAGS="$CFLAGS"
+      save_LIBS="$LIBS"
+      save_CC="$CC"
+      # Try with the flags determined by the earlier checks.
+      #
+      # -Wl,-z,defs forces link-time symbol resolution, so that the
+      # linking checks with -shared actually have any value
+      #
+      # FIXME: -fPIC is required for -shared on many architectures,
+      # so we specify it here, but the right way would probably be to
+      # properly detect whether it is actually required.
+      CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS"
+      LIBS="$PTHREAD_LIBS $LIBS"
+      CC="$PTHREAD_CC"
+
+      AC_MSG_CHECKING([whether -pthread is sufficient with -shared])
+      AC_TRY_LINK([#include <pthread.h>],
+         [pthread_t th; pthread_join(th, 0);
+         pthread_attr_init(0); pthread_cleanup_push(0, 0);
+         pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+         [ok=yes])
+
+      if test "x$ok" = xyes; then
+         AC_MSG_RESULT([yes])
+      else
+         AC_MSG_RESULT([no])
+      fi
 
-        # More AIX lossage: must compile with cc_r
-        AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
+      #
+      # Linux gcc on some architectures such as mips/mipsel forgets
+      # about -lpthread
+      #
+      if test x"$ok" = xno; then
+         AC_MSG_CHECKING([whether -lpthread fixes that])
+         LIBS="-lpthread $PTHREAD_LIBS $save_LIBS"
+         AC_TRY_LINK([#include <pthread.h>],
+            [pthread_t th; pthread_join(th, 0);
+            pthread_attr_init(0); pthread_cleanup_push(0, 0);
+            pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+            [ok=yes])
+
+         if test "x$ok" = xyes; then
+            AC_MSG_RESULT([yes])
+            PTHREAD_LIBS="-lpthread $PTHREAD_LIBS"
+         else
+            AC_MSG_RESULT([no])
+         fi
+      fi
+      #
+      # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc
+      #
+      if test x"$ok" = xno; then
+         AC_MSG_CHECKING([whether -lc_r fixes that])
+         LIBS="-lc_r $PTHREAD_LIBS $save_LIBS"
+         AC_TRY_LINK([#include <pthread.h>],
+             [pthread_t th; pthread_join(th, 0);
+              pthread_attr_init(0); pthread_cleanup_push(0, 0);
+              pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+             [ok=yes])
+
+         if test "x$ok" = xyes; then
+            AC_MSG_RESULT([yes])
+            PTHREAD_LIBS="-lc_r $PTHREAD_LIBS"
+         else
+            AC_MSG_RESULT([no])
+         fi
+      fi
+      if test x"$ok" = xno; then
+         # OK, we have run out of ideas
+         AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries])
+
+         # so it's not safe to assume that we may use pthreads
+         acx_pthread_ok=no
+      fi
+
+      CFLAGS="$save_CFLAGS"
+      LIBS="$save_LIBS"
+      CC="$save_CC"
+   fi
 else
         PTHREAD_CC="$CC"
 fi
@@ -197,3 +346,43 @@ else
 fi
 AC_LANG_RESTORE
 ])dnl ACX_PTHREAD
+AC_DEFUN([AC_CHECK_DEFINE],[
+AS_VAR_PUSHDEF([ac_var],[ac_cv_defined_$1_$2])dnl
+AC_CACHE_CHECK([for $1 in $2], ac_var,
+AC_TRY_COMPILE([#include <$2>],[
+  #ifdef $1
+  int ok;
+  #else
+  choke me
+  #endif
+],AS_VAR_SET(ac_var, yes),AS_VAR_SET(ac_var, no)))
+AS_IF([test AS_VAR_GET(ac_var) != "no"], [$3], [$4])dnl
+AS_VAR_POPDEF([ac_var])dnl
+])
+
+AC_DEFUN([ACX_LIBWRAP], [
+LIBWRAP_LIBS=
+saved_LIBS="$LIBS"
+LIBS="$LIBS -lwrap"
+AC_MSG_CHECKING([for tcpwrap library and headers])
+AC_LINK_IFELSE(
+AC_LANG_PROGRAM(
+[#include <tcpd.h>
+#include <syslog.h>
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;],
+[struct request_info *req;
+return hosts_access (req);]),
+[AC_DEFINE(HAVE_LIBWRAP, [], [Have tcpwrap?])
+LIBWRAP_LIBS="-lwrap"
+AC_MSG_RESULT(yes)],
+[AC_MSG_RESULT(no)])
+LIBS="$saved_LIBS"
+])
+
+AC_DEFUN([ACX_LIRC], [
+LIRC_CFLAGS=
+LIRC_LIBS=
+AC_CHECK_HEADER(lirc/lirc_client.h,[AC_CHECK_LIB(lirc_client,lirc_init,[HAVE_LIRC=1
+LIRC_LIBS=-llirc_client],HAVE_LIRC=0)],HAVE_LIRC=0)
+])
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..4051a22
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+# Only there to make jhbuild happy
+
+NOCONFIGURE=1 ./bootstrap.sh
+
+exec ./configure  "$@"
index 4b9032b646303f0886db9d34f867d238595d6b5c..ceea55b7fe106ae638989ab4366ff2b7a55ff1f4 100755 (executable)
@@ -1,47 +1,63 @@
 #!/bin/bash
-# $Id$
 
-# This file is part of polypaudio.
+# This file is part of PulseAudio.
 #
-# polypaudio is free software; you can redistribute it and/or modify it
+# PulseAudio is free software; you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation; either version 2 of the License, or
 # (at your option) any later version.
 #
-# polypaudio is distributed in the hope that it will be useful, but
+# PulseAudio is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software Foundation,
+# along with PulseAudio; if not, write to the Free Software Foundation,
 # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 
+VERSION=1.10
+
 run_versioned() {
     local P
-    type -p "$1-$2" &> /dev/null && P="$1-$2" || local P="$1"
+    local V
+
+    V=$(echo "$2" | sed -e 's,\.,,g')
+    
+    if [ -e "`which $1$V 2> /dev/null`" ] ; then
+       P="$1$V" 
+    else
+       if [ -e "`which $1-$2 2> /dev/null`" ] ; then
+           P="$1-$2" 
+       else
+           P="$1"
+       fi
+    fi
 
     shift 2
     "$P" "$@"
 }
 
+set -ex
+
 if [ "x$1" = "xam" ] ; then
-    set -ex
-    run_versioned automake 1.7 -a -c --foreign
+    run_versioned automake "$VERSION" -a -c --foreign
     ./config.status
 else 
-    set -ex
-
     rm -rf autom4te.cache
     rm -f config.cache
 
-    run_versioned aclocal 1.7
-    libtoolize -c --force --ltdl
-    autoheader
-    run_versioned automake 1.7 -a -c --foreign
-    autoconf -Wall
+    touch config.rpath
+    test "x$LIBTOOLIZE" = "x" && LIBTOOLIZE=libtoolize
 
-    CFLAGS="-g -O0" ./configure --sysconfdir=/etc "$@"
+    "$LIBTOOLIZE" -c --force --ltdl
+    run_versioned aclocal "$VERSION"
+    run_versioned autoconf 2.59 -Wall
+    run_versioned autoheader 2.59
+    run_versioned automake "$VERSION" --copy --foreign --add-missing
 
-    make clean
+    if test "x$NOCONFIGURE" = "x"; then
+        CFLAGS="-g -O0" ./configure --sysconfdir=/etc --localstatedir=/var --enable-force-preopen "$@" 
+        make clean
+    fi
 fi
index 27cfeecd1bc8fb69a02336ff8703b37bba122d50..296dc17ef0daee7ec2940985235824c78567784a 100644 (file)
 # -*- Autoconf -*-
 # Process this file with autoconf to produce a configure script.
 
-# $Id$
-
-# This file is part of polypaudio.
+# This file is part of PulseAudio.
+#
+# Copyright 2004-2008 Lennart Poettering
+# Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
 #
-# polypaudio is free software; you can redistribute it and/or modify it
+# PulseAudio is free software; you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation; either version 2 of the License, or
 # (at your option) any later version.
 #
-# polypaudio is distributed in the hope that it will be useful, but
+# PulseAudio is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software Foundation,
+# along with PulseAudio; if not, write to the Free Software Foundation,
 # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 
-AC_PREREQ(2.57)
-AC_INIT([polypaudio],[0.8],[mzcbylcnhqvb (at) 0pointer (dot) de])
-AC_CONFIG_SRCDIR([polyp/main.c])
+AC_PREREQ(2.60)
+
+m4_define(PA_MAJOR, [0])
+m4_define(PA_MINOR, [9])
+m4_define(PA_MICRO, [11])
+
+AC_INIT([pulseaudio], PA_MAJOR.PA_MINOR.PA_MICRO,[mzchyfrnhqvb (at) 0pointer (dot) net])
+AC_CONFIG_SRCDIR([src/daemon/main.c])
 AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE([foreign -Wall])
 
-AC_SUBST(PA_MAJORMINOR, "$PACKAGE_VERSION")
-AC_SUBST(PACKAGE_URL, [http://0pointer.de/lennart/projects/polypaudio/])
+AC_SUBST(PA_MAJORMINOR, "PA_MAJOR.PA_MINOR")
+AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
+
+AC_SUBST(PA_API_VERSION, 11)
+AC_SUBST(PA_PROTOCOL_VERSION, 13)
+
+# The stable ABI for client applications, for the version info x:y:z
+# always will hold y=z
+AC_SUBST(LIBPULSE_VERSION_INFO, [5:0:5])
+
+# A simplified, synchronous, ABI-stable interface for client
+# applications, for the version info x:y:z always will hold y=z
+AC_SUBST(LIBPULSE_SIMPLE_VERSION_INFO, [0:1:0])
+
+# The ABI-stable network browsing interface for client applications,
+# for the version info x:y:z always will hold y=z
+AC_SUBST(LIBPULSE_BROWSE_VERSION_INFO, [1:1:1])
+
+# The ABI-stable GLib adapter for client applications, for the version
+# info x:y:z always will hold y=z
+AC_SUBST(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO, [0:3:0])
 
-AC_SUBST(PA_API_VERSION, 8)
+# An internally used, ABI-unstable library that contains the
+# PulseAudio core, SONAMEs are bumped on every release, version info
+# suffix will always be 0:0
+AC_SUBST(LIBPULSECORE_VERSION_INFO, [6:0:0])
+
+AC_CANONICAL_HOST
 
 if type -p stow > /dev/null && test -d /usr/local/stow ; then
    AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***])
    ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}"
 fi
 
-# Checks for programs.
+#### Platform hacks ####
+
+case $host in
+   *-*-solaris* )
+      AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, Needed to get declarations for msg_control and msg_controllen on Solaris)
+      AC_DEFINE(_XOPEN_SOURCE,          2, Needed to get declarations for msg_control and msg_controllen on Solaris)
+      AC_DEFINE(__EXTENSIONS__,         1, Needed to get declarations for msg_control and msg_controllen on Solaris)
+      ;;
+esac
+
+#### Checks for programs. ####
+
+# mkdir -p
+
+AC_PROG_MKDIR_P
+
+# CC
+
 AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_GCC_TRADITIONAL
+AC_GNU_SOURCE
+
+# M4
+
+AC_PATH_PROG([M4], [m4 gm4], [no])
+if test "x$M4" = xno ; then
+   AC_MSG_ERROR([m4 missing])
+fi
+
+# GCC flags
+
+test_gcc_flag() {
+    AC_LANG_CONFTEST([int main(int argc, char*argv[]) {}])
+    $CC -c conftest.c $CFLAGS -o conftest.o > /dev/null 2> /dev/null
+    ret=$?
+    rm -f conftest.o
+    return $ret
+}
+
+# If using GCC specify some additional parameters
+if test "x$GCC" = "xyes" ; then
+
+    # We use gnu99 instead of c99 because many have interpreted the standard
+    # in a way that int64_t isn't defined on non-64 bit platforms.
+    DESIRED_FLAGS="-std=gnu99 -Wall -W -Wextra -pedantic -pipe -Wformat -Wold-style-definition -Wdeclaration-after-statement -Wfloat-equal -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-noreturn -Wshadow -Wendif-labels -Wpointer-arith -Wcast-align -Wwrite-strings -Winline -Wno-unused-parameter -ffast-math"
+
+    for flag in $DESIRED_FLAGS ; do
+        AC_MSG_CHECKING([whether $CC accepts $flag])
+        if test_gcc_flag $flag ; then
+           CFLAGS="$CFLAGS $flag"
+           AC_MSG_RESULT([yes])
+        else
+           AC_MSG_RESULT([no])
+        fi
+    done
+fi
+
+# Native atomic operation support
+AC_ARG_ENABLE([atomic-arm-linux-helpers],
+    AC_HELP_STRING([--disable-atomic-arm-linux-helpers], [use inline asm or libatomic_ops instead]),
+        [
+            case "${enableval}" in
+                yes) atomic_arm_linux_helpers=yes ;;
+                no) atomic_arm_linux_helpers=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-atomic-arm-linux-helpers) ;;
+            esac
+        ],
+        [atomic_arm_linux_helpers=auto])
+
+AC_ARG_ENABLE([atomic-arm-memory-barrier],
+    AC_HELP_STRING([--enable-atomic-arm-memory-barrier], [only really needed in SMP arm systems]),
+        [
+            case "${enableval}" in
+                yes) AC_DEFINE_UNQUOTED(ATOMIC_ARM_MEMORY_BARRIER_ENABLED, 1, [Enable memory barriers]) ;;
+                no) ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-atomic-arm-linux-helpers) ;;
+            esac
+        ],)
 
-# libtool stuff
+AC_MSG_CHECKING([target operating system])
+case $host in
+       *-*-linux*)
+           AC_MSG_RESULT([linux])
+           pulse_target_os=linux
+       ;;
+       *)
+           AC_MSG_RESULT([unknown])
+           pulse_target_os=unknown
+       ;;
+esac
+
+# If everything else fails use libatomic_ops
+need_libatomic_ops=yes
+
+AC_MSG_CHECKING([whether $CC knows __sync_bool_compare_and_swap()])
+AC_LANG_CONFTEST([int main() { int a = 4; __sync_bool_compare_and_swap(&a, 4, 5); }])
+$CC conftest.c $CFLAGS -o conftest > /dev/null 2> /dev/null
+ret=$?
+rm -f conftest.o conftest
+if test $ret -eq 0 ; then
+    AC_DEFINE([HAVE_ATOMIC_BUILTINS], 1, [Have __sync_bool_compare_and_swap() and friends.])
+    AC_MSG_RESULT([yes])
+    need_libatomic_ops=no
+else
+    AC_MSG_RESULT([no])
+    # HW specific atomic ops stuff
+    AC_MSG_CHECKING([architecture for native atomic operations])
+    case $host_cpu in
+        arm*)
+           AC_MSG_RESULT([arm])
+           AC_MSG_CHECKING([whether we can use Linux kernel helpers])
+           # The Linux kernel helper functions have been there since 2.6.16. However
+           # compile time checking for kernel version in cross compile environment
+           # (which is usually the case for arm cpu) is tricky (or impossible).
+           if test "x$pulse_target_os" = "xlinux" && test "x$atomic_arm_linux_helpers" != "xno"; then
+               AC_MSG_RESULT([yes])
+               AC_DEFINE_UNQUOTED(ATOMIC_ARM_LINUX_HELPERS, 1, [special arm linux implementation])
+               need_libatomic_ops=no
+           else
+              AC_MSG_RESULT([no])
+              AC_MSG_CHECKING([compiler support for arm inline asm atomic operations])
+              AC_LANG_CONFTEST([[int main()
+              {
+                   volatile int a=0;
+                  int o=0, n=1, r;
+                  asm volatile ("ldrex %0, [%1]\n"
+                                "subs  %0, %0, %2\n"
+                                "strexeq %0, %3, [%1]\n"
+                                : "=&r" (r)
+                                : "r" (&a), "Ir" (o), "r" (n)
+                                : "cc");
+                   return (a==1 ? 0 : -1);
+              }]])
+              $CC conftest.c $CFLAGS -o conftest > /dev/null 2>&1
+              ret=$?
+              rm -f conftest.o conftest
+              if test $ret -eq 0 ; then
+                  AC_DEFINE([ATOMIC_ARM_INLINE_ASM], 1, [Have ARMv6 instructions.])
+                  AC_MSG_RESULT([yes])
+                  need_libatomic_ops=no
+              else
+                  AC_MSG_RESULT([no])
+               fi
+          fi
+       ;;
+        *)
+           AC_MSG_RESULT([unknown])
+        ;;
+    esac
+fi
+
+AC_MSG_CHECKING([whether $CC knows __thread])
+AC_LANG_CONFTEST([static __thread int a = 6; int main() { a = 5; }])
+$CC conftest.c $CFLAGS -o conftest > /dev/null 2> /dev/null
+ret=$?
+rm -f conftest.o conftest
+if test $ret -eq 0 ; then
+    AC_DEFINE([HAVE_TLS_BUILTIN], 1, [Have __thread().])
+    AC_MSG_RESULT([yes])
+else
+    AC_MSG_RESULT([no])
+fi
+
+AC_MSG_CHECKING([whether $CC knows _Bool])
+AC_LANG_CONFTEST([int main() { _Bool b; }])
+$CC conftest.c $CFLAGS -o conftest > /dev/null 2> /dev/null
+ret=$?
+rm -f conftest.o conftest
+if test $ret -eq 0 ; then
+    AC_DEFINE([HAVE_STD_BOOL], 1, [Have _Bool.])
+    AC_MSG_RESULT([yes])
+else
+    AC_MSG_RESULT([no])
+fi
+
+#### libtool stuff ####
+
+AC_LTDL_ENABLE_INSTALL
 AC_LIBLTDL_INSTALLABLE
-AC_SUBST(LTDLINCL)
-AC_SUBST(LIBLTDL)
 AC_LIBTOOL_DLOPEN
+AC_LIBTOOL_WIN32_DLL
 AC_PROG_LIBTOOL
+AC_SUBST(LTDLINCL)
+AC_SUBST(LIBLTDL)
 AC_CONFIG_SUBDIRS(libltdl)
 
-# Checks for header files.
-AC_HEADER_STDC
-AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h unistd.h syslog.h])
+old_LIBS=$LIBS
+LIBS="$LIBS $LIBLTDL"
+AC_CHECK_FUNCS([lt_dlmutex_register])
+LIBS=$old_LIBS
+AC_CHECK_TYPES([struct lt_user_dlloader, lt_dladvise], , , [#include <ltdl.h>])
 
-ACX_PTHREAD
-AC_PATH_XTRA
+if test "x$enable_ltdl_install" = "xno" && test "x$ac_cv_lib_ltdl_lt_dlinit" = "xno" ; then
+    AC_MSG_ERROR([[
 
-HAVE_X11=0
-test "x$no_x" != "xyes" && HAVE_X11=1
-AC_SUBST(HAVE_X11)
-AM_CONDITIONAL(HAVE_X11, test "x$no_x" != "xyes")
-if test "x$no_x" != "xyes" ; then
-   AC_DEFINE([HAVE_X11], 1, [Have X11])
+        *** Cannot find the libltdl development files.
+        *** Maybe you need to install the libltdl-dev package.
+        ]])
 fi
 
-# Checks for typedefs, structures, and compiler characteristics.
+#### Determine build environment ####
+
+os_is_win32=0
+
+case "$host_os" in
+       mingw*)
+        AC_DEFINE([OS_IS_WIN32], 1, [Build target is Windows.])
+        os_is_win32=1
+               ;;
+       esac
+
+AM_CONDITIONAL(OS_IS_WIN32, test "x$os_is_win32" = "x1")
+
+###################################
+#   Basic environment checks      #
+###################################
+
+#### Checks for header files. ####
+
+# ISO
+AC_HEADER_STDC
+
+# POSIX
+AC_CHECK_HEADERS([arpa/inet.h glob.h grp.h netdb.h netinet/in.h \
+    netinet/in_systm.h netinet/tcp.h poll.h pwd.h sched.h \
+    sys/mman.h sys/resource.h sys/select.h sys/socket.h sys/wait.h \
+    syslog.h sys/dl.h dlfcn.h linux/sockios.h])
+AC_CHECK_HEADERS([netinet/ip.h], [], [],
+                [#include <sys/types.h>
+                 #if HAVE_NETINET_IN_H
+                 # include <netinet/in.h>
+                 #endif
+                 #if HAVE_NETINET_IN_SYSTM_H
+                 # include <netinet/in_systm.h>
+                 #endif
+                ])
+AC_CHECK_HEADERS([regex.h], [HAVE_REGEX=1], [HAVE_REGEX=0])
+AC_CHECK_HEADERS([sys/un.h], [HAVE_AF_UNIX=1], [HAVE_AF_UNIX=0])
+
+AM_CONDITIONAL(HAVE_REGEX, test "x$HAVE_REGEX" = "x1")
+AM_CONDITIONAL(HAVE_AF_UNIX, test "x$HAVE_AF_UNIX" = "x1")
+
+# Linux
+AC_CHECK_HEADERS([linux/input.h], [HAVE_EVDEV=1], [HAVE_EVDEV=0])
+
+AM_CONDITIONAL([HAVE_EVDEV], [test "x$HAVE_EVDEV" = "x1"])
+
+AC_CHECK_HEADERS([sys/prctl.h])
+
+# Solaris
+AC_CHECK_HEADERS([sys/filio.h])
+
+# Windows
+AC_CHECK_HEADERS([windows.h winsock2.h ws2tcpip.h])
+
+# Other
+AC_CHECK_HEADERS([sys/ioctl.h])
+AC_CHECK_HEADERS([byteswap.h])
+AC_CHECK_HEADERS([sys/syscall.h])
+
+#### Typdefs, structures, etc. ####
+
 AC_C_CONST
+AC_C_BIGENDIAN
 AC_TYPE_PID_T
 AC_TYPE_SIZE_T
+AC_CHECK_TYPES(ssize_t, , [AC_DEFINE([ssize_t], [signed long],
+    [Define ssize_t if it is not done by the standard libs.])])
 AC_TYPE_OFF_T
-AC_HEADER_TIME
-
-# Checks for library functions.
-AC_FUNC_FORK
-AC_PROG_GCC_TRADITIONAL
-AC_FUNC_LSTAT
-AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
-AC_FUNC_MALLOC
-AC_FUNC_MEMCMP
-AC_FUNC_MMAP
-AC_FUNC_REALLOC
-AC_FUNC_SETPGRP
-AC_FUNC_VPRINTF
-AC_FUNC_CLOSEDIR_VOID
-AC_FUNC_SELECT_ARGTYPES
 AC_TYPE_SIGNAL
 AC_TYPE_UID_T
-AC_CHECK_FUNCS([gethostname gettimeofday memchr memmove memset mkdir mkfifo munmap rmdir socket strcspn strerror strrchr strspn strstr strtol strtoul strcasecmp putenv strchr strpbrk strdup getgrgid_r getpwuid_r regcomp ftruncate select])
-AC_CHECK_LIB(m, pow)
-AC_CHECK_FUNCS(pow)
-AC_FUNC_STAT
-AC_HEADER_SYS_WAIT
-AC_HEADER_DIRENT
+AC_CHECK_DECLS(environ)
 
-AC_C_BIGENDIAN
+AC_CHECK_DEFINE([SIGXCPU], [signal.h], [
+HAVE_SIGXCPU=1
+AC_DEFINE([HAVE_SIGXCPU], 1, [Have SIGXCPU?])
+], [HAVE_SIGXCPU=0])
+AM_CONDITIONAL(HAVE_SIGXCPU, test "x$HAVE_SIGXCPU" = "x1")
+
+# Solaris lacks this
+AC_CHECK_DEFINE([INADDR_NONE], [netinet/in.h], [],
+    [AC_CHECK_DEFINE([INADDR_NONE], [winsock2.h], [],
+        [AC_DEFINE([INADDR_NONE],  [0xffffffff], [Define INADDR_NONE if not found in <netinet/in.h>])])])
+
+#### POSIX threads ####
+
+ACX_PTHREAD
+
+#### Check for libs ####
+
+# ISO
+AC_SEARCH_LIBS([pow], [m])
+
+# POSIX
+AC_SEARCH_LIBS([sched_setscheduler], [rt])
+AC_SEARCH_LIBS([dlopen], [dl])
+AC_SEARCH_LIBS([shm_open], [rt])
+AC_SEARCH_LIBS([inet_ntop], [nsl])
+AC_SEARCH_LIBS([timer_create], [rt])
+
+# BSD
+AC_SEARCH_LIBS([connect], [socket])
+
+# Non-standard
+
+# This magic is needed so we do not needlessly add static libs to the win32
+# build, disabling its ability to make dlls.
+AC_CHECK_FUNCS([getopt_long], [], [AC_CHECK_LIB([iberty], [getopt_long])])
+
+AC_CHECK_LIB(gdbm, gdbm_open)
+AC_CHECK_HEADERS(gdbm.h)
+
+#### Check for functions ####
+
+# ISO
+AC_CHECK_FUNCS([lrintf strtof])
+
+# POSIX
+AC_FUNC_FORK
 AC_FUNC_GETGROUPS
+AC_FUNC_SELECT_ARGTYPES
+AC_CHECK_FUNCS([chmod chown clock_gettime getaddrinfo getgrgid_r \
+    getpwuid_r gettimeofday getuid inet_ntop inet_pton mlock nanosleep \
+    pipe posix_fadvise posix_madvise posix_memalign setpgid setsid shm_open \
+    sigaction sleep sysconf])
+AC_CHECK_FUNCS([mkfifo], [HAVE_MKFIFO=1], [HAVE_MKFIFO=0])
 
-AC_CHECK_LIB(cap, cap_init, [CAP_LIBS='-lcap'], [CAP_LIBS=''])
-AC_SUBST(CAP_LIBS)
+AM_CONDITIONAL(HAVE_MKFIFO, test "x$HAVE_MKFIFO" = "x1")
 
-AC_CHECK_HEADERS(sys/capability.h)
+# X/OPEN
+AC_CHECK_FUNCS([readlink])
 
-AC_CHECK_FUNCS(setresuid)
-AC_CHECK_FUNCS(setreuid)
+# SUSv2
+AC_CHECK_FUNCS([ctime_r usleep])
 
-PKG_CHECK_MODULES(LIBSAMPLERATE, [ samplerate >= 0.1.0 ])
-AC_SUBST(LIBSAMPLERATE_CFLAGS)
-AC_SUBST(LIBSAMPLERATE_LIBS)
+# SUSv3
+AC_CHECK_FUNCS([strerror_r])
+
+# BSD
+AC_CHECK_FUNCS([lstat])
+
+# Non-standard
+
+AC_CHECK_FUNCS([setresuid setresgid setreuid setregid seteuid setegid ppoll strsignal sig2str strtof_l])
+
+AC_MSG_CHECKING([for PTHREAD_PRIO_INHERIT])
+AC_LANG_CONFTEST([AC_LANG_SOURCE([[
+#include <pthread.h>
+int main() { int i = PTHREAD_PRIO_INHERIT; }]])])
+$PTHREAD_CC conftest.c $PTHREAD_CFLAGS $CFLAGS $PTHREAD_LIBS -o conftest > /dev/null 2> /dev/null
+ret=$?
+rm -f conftest.o conftest
+
+if test $ret -eq 0 ; then
+    AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])
+    AC_MSG_RESULT([yes])
+else
+    AC_MSG_RESULT([no])
+fi
+
+#### Large File-Support (LFS) ####
+
+AC_SYS_LARGEFILE
+
+# Check for open64 to know if the current system does have open64() and similar functions
+AC_CHECK_FUNCS([open64])
+
+#### [lib]iconv ####
+
+AM_ICONV
+
+###################################
+#      External libraries         #
+###################################
+
+#### X11 (optional) ####
+
+HAVE_X11=0
+
+# The macro tests the host, not the build target
+if test "x$os_is_win32" != "x1" ; then
+    AC_PATH_XTRA
+    test "x$no_x" != "xyes" && HAVE_X11=1
+fi
+
+AC_SUBST(HAVE_X11)
+AM_CONDITIONAL(HAVE_X11, test "x$HAVE_X11" = "x1")
+if test "x$HAVE_X11" = "x1" ; then
+    AC_DEFINE([HAVE_X11], 1, [Have X11])
+fi
+
+#### Capabilities (optional) ####
+
+CAP_LIBS=''
+
+AC_ARG_WITH(
+        [caps],
+        AC_HELP_STRING([--without-caps],[Omit support for POSIX capabilities.]))
+
+if test "x${with_caps}" != "xno"; then
+    AC_SEARCH_LIBS([cap_init], [cap], [], [
+                    if test "x${with_caps}" = "xyes" ; then
+                        AC_MSG_ERROR([*** POSIX caps libraries not found])
+                    fi])
+    AC_CHECK_HEADERS([sys/capability.h], [], [
+                    if test "x${with_caps}" = "xyes" ; then
+                        AC_MSG_ERROR([*** POSIX caps headers not found])
+                    fi])
+fi
+
+#### pkg-config ####
+
+# Check for pkg-config manually first, as if its not installed the
+# PKG_PROG_PKG_CONFIG macro won't be defined.
+AC_CHECK_PROG(have_pkg_config, pkg-config, yes, no)
+
+if test x"$have_pkg_config" = "xno"; then
+    AC_MSG_ERROR(pkg-config is required to install this program)
+fi
+
+PKG_PROG_PKG_CONFIG
+
+#### Sound file ####
 
 PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.10 ])
 AC_SUBST(LIBSNDFILE_CFLAGS)
 AC_SUBST(LIBSNDFILE_LIBS)
 
-PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.0 ], [HAVE_ALSA=1], [HAVE_ALSA=0])
+#### atomic-ops ###
+
+AC_MSG_CHECKING([whether we need libatomic_ops])
+if test "x$need_libatomic_ops" = "xyes"; then
+   AC_MSG_RESULT([yes])
+   AC_CHECK_HEADERS([atomic_ops.h], [], [
+   AC_MSG_ERROR([*** libatomic-ops headers not found])
+   ])
+
+   # Win32 does not need the lib and breaks horribly if we try to include it
+   if test "x$os_is_win32" != "x1" ; then
+       LIBS="$LIBS -latomic_ops"
+   fi
+else
+   AC_MSG_RESULT([no])
+fi
+
+#### Libsamplerate support (optional) ####
+
+AC_ARG_ENABLE([samplerate],
+    AC_HELP_STRING([--disable-samplerate], [Disable optional libsamplerate support]),
+        [
+            case "${enableval}" in
+                yes) samplerate=yes ;;
+                no) samplerate=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-samplerate) ;;
+            esac
+        ],
+        [samplerate=auto])
+
+if test "x${samplerate}" != xno ; then
+    PKG_CHECK_MODULES(LIBSAMPLERATE, [ samplerate >= 0.1.0 ],
+        HAVE_LIBSAMPLERATE=1,
+        [
+            HAVE_LIBSAMPLERATE=0
+            if test "x$samplerate" = xyes ; then
+                AC_MSG_ERROR([*** Libsamplerate not found])
+            fi
+        ])
+else
+    HAVE_LIBSAMPLERATE=0
+fi
+
+if test "x${HAVE_LIBSAMPLERATE}" = x1 ; then
+   AC_DEFINE([HAVE_LIBSAMPLERATE], 1, [Have libsamplerate?])
+fi
+
+AC_SUBST(LIBSAMPLERATE_CFLAGS)
+AC_SUBST(LIBSAMPLERATE_LIBS)
+AC_SUBST(HAVE_LIBSAMPLERATE)
+AM_CONDITIONAL([HAVE_LIBSAMPLERATE], [test "x$HAVE_LIBSAMPLERATE" = x1])
+
+#### OSS support (optional) ####
+
+AC_ARG_ENABLE([oss],
+    AC_HELP_STRING([--disable-oss], [Disable optional OSS support]),
+        [
+            case "${enableval}" in
+                yes) oss=yes ;;
+                no) oss=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-oss) ;;
+            esac
+        ],
+        [oss=auto])
+
+if test "x${oss}" != xno ; then
+    AC_CHECK_HEADERS([sys/soundcard.h],
+        [
+            HAVE_OSS=1
+            AC_DEFINE([HAVE_OSS], 1, [Have OSS?])
+        ],
+        [
+            HAVE_OSS=0
+            if test "x$oss" = xyes ; then
+                AC_MSG_ERROR([*** OSS support not found])
+            fi
+        ])
+else
+    HAVE_OSS=0
+fi
+
+AC_SUBST(HAVE_OSS)
+AM_CONDITIONAL([HAVE_OSS], [test "x$HAVE_OSS" = x1])
+
+
+#### ALSA support (optional) ####
+
+AC_ARG_ENABLE([alsa],
+    AC_HELP_STRING([--disable-alsa], [Disable optional ALSA support]),
+        [
+            case "${enableval}" in
+                yes) alsa=yes ;;
+                no) alsa=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-alsa) ;;
+            esac
+        ],
+        [alsa=auto])
+
+if test "x${alsa}" != xno ; then
+    PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.16 ],
+        [
+            HAVE_ALSA=1
+            AC_DEFINE([HAVE_ALSA], 1, [Have ALSA?])
+        ],
+        [
+            HAVE_ALSA=0
+            if test "x$alsa" = xyes ; then
+                AC_MSG_ERROR([*** ALSA support not found])
+            fi
+        ])
+else
+    HAVE_ALSA=0
+fi
+
 AC_SUBST(ASOUNDLIB_CFLAGS)
-AC_SUBST(ASOUNDLIB_LIBS) 
+AC_SUBST(ASOUNDLIB_LIBS)
 AC_SUBST(HAVE_ALSA)
 AM_CONDITIONAL([HAVE_ALSA], [test "x$HAVE_ALSA" = x1])
 
-PKG_CHECK_MODULES(GLIB20, [ glib-2.0 >= 2.4.0 ], HAVE_GLIB20=1, HAVE_GLIB20=0)
+#### Solaris audio support (optional) ####
+
+AC_ARG_ENABLE([solaris],
+    AC_HELP_STRING([--disable-solaris], [Disable optional Solaris audio support]),
+        [
+            case "${enableval}" in
+                yes) solaris=yes ;;
+                no) solaris=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-solaris) ;;
+            esac
+        ],
+        [solaris=auto])
+
+if test "x${solaris}" != xno ; then
+    AC_CHECK_HEADERS([sys/audio.h],
+        [
+            HAVE_SOLARIS=1
+            AC_DEFINE([HAVE_SOLARIS], 1, [Have Solaris audio?])
+        ],
+        [
+            HAVE_SOLARIS=0
+            if test "x$solaris" = xyes ; then
+                AC_MSG_ERROR([*** Solaris audio support not found])
+            fi
+        ])
+else
+    HAVE_SOLARIS=0
+fi
+
+AC_SUBST(HAVE_SOLARIS)
+AM_CONDITIONAL([HAVE_SOLARIS], [test "x$HAVE_SOLARIS" = x1])
+
+#### GLib 2 support (optional) ####
+
+AC_ARG_ENABLE([glib2],
+    AC_HELP_STRING([--disable-glib2], [Disable optional GLib 2 support]),
+        [
+            case "${enableval}" in
+                yes) glib2=yes ;;
+                no) glib2=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-glib2) ;;
+            esac
+        ],
+        [glib2=auto])
+
+if test "x${glib2}" != xno ; then
+    PKG_CHECK_MODULES(GLIB20, [ glib-2.0 >= 2.4.0 ],
+        HAVE_GLIB20=1,
+        [
+            HAVE_GLIB20=0
+            if test "x$glib2" = xyes ; then
+                AC_MSG_ERROR([*** GLib 2 support not found])
+            fi
+        ])
+else
+    HAVE_GLIB20=0
+fi
+
 AC_SUBST(GLIB20_CFLAGS)
 AC_SUBST(GLIB20_LIBS)
 AC_SUBST(HAVE_GLIB20)
 AM_CONDITIONAL([HAVE_GLIB20], [test "x$HAVE_GLIB20" = x1])
 
-PKG_CHECK_MODULES(GLIB12, [ glib >= 1.2.0 ], HAVE_GLIB12=1, HAVE_GLIB12=0)
-AC_SUBST(GLIB12_CFLAGS)
-AC_SUBST(GLIB12_LIBS)
-AC_SUBST(HAVE_GLIB12)
-AM_CONDITIONAL([HAVE_GLIB12], [test "x$HAVE_GLIB12" = x1])
+#### GConf support (optional) ####
+
+AC_ARG_ENABLE([gconf],
+    AC_HELP_STRING([--disable-gconf], [Disable optional GConf support]),
+        [
+            case "${enableval}" in
+                yes) gconf=yes ;;
+                no) gconf=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-gconf) ;;
+            esac
+        ],
+        [glib=auto])
+
+if test "x${gconf}" != xno ; then
+    PKG_CHECK_MODULES(GCONF, [ gconf-2.0 >= 2.4.0 ],
+        HAVE_GCONF=1,
+        [
+            HAVE_GCONF=0
+            if test "x$gconf" = xyes ; then
+                AC_MSG_ERROR([*** GConf support not found])
+            fi
+        ])
+else
+    HAVE_GCONF=0
+fi
+
+AC_SUBST(GCONF_CFLAGS)
+AC_SUBST(GCONF_LIBS)
+AC_SUBST(HAVE_GCONF)
+AM_CONDITIONAL([HAVE_GCONF], [test "x$HAVE_GCONF" = x1])
+
+#### Avahi support (optional) ####
+
+AC_ARG_ENABLE([avahi],
+    AC_HELP_STRING([--disable-avahi], [Disable optional Avahi support]),
+        [
+            case "${enableval}" in
+                yes) avahi=yes ;;
+                no) avahi=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-avahi) ;;
+            esac
+        ],
+        [avahi=auto])
 
-PKG_CHECK_MODULES(HOWL, [ howl >= 0.9.8 ], HAVE_HOWL=1, HAVE_HOWL=0)
-AC_SUBST(HOWL_CFLAGS)
-AC_SUBST(HOWL_LIBS)
-AC_SUBST(HAVE_HOWL)
-AM_CONDITIONAL([HAVE_HOWL], [test "x$HAVE_HOWL" = x1])
+if test "x${avahi}" != xno ; then
+    PKG_CHECK_MODULES(AVAHI, [ avahi-client >= 0.6.0 ],
+        HAVE_AVAHI=1,
+        [
+                HAVE_AVAHI=0
+                if test "x$avahi" = xyes ; then
+                        AC_MSG_ERROR([*** Avahi support not found])
+                fi
+        ])
+else
+    HAVE_AVAHI=0
+fi
+
+AC_SUBST(AVAHI_CFLAGS)
+AC_SUBST(AVAHI_LIBS)
+AC_SUBST(HAVE_AVAHI)
+AM_CONDITIONAL([HAVE_AVAHI], [test "x$HAVE_AVAHI" = x1])
+
+### LIBOIL ####
+
+PKG_CHECK_MODULES(LIBOIL, [ liboil-0.3 >= 0.3.0 ])
+AC_SUBST(LIBOIL_CFLAGS)
+AC_SUBST(LIBOIL_LIBS)
+
+### JACK (optional) ####
+
+AC_ARG_ENABLE([jack],
+    AC_HELP_STRING([--disable-jack], [Disable optional JACK support]),
+        [
+            case "${enableval}" in
+                yes) jack=yes ;;
+                no) jack=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-jack) ;;
+            esac
+        ],
+        [jack=auto])
+
+if test "x${jack}" != xno ; then
+    PKG_CHECK_MODULES(JACK, [ jack >= 0.100 ],
+        HAVE_JACK=1,
+        [
+            HAVE_JACK=0
+            if test "x$jack" = xyes ; then
+                AC_MSG_ERROR([*** JACK support not found])
+            fi
+        ])
+else
+    HAVE_JACK=0
+fi
+
+AC_SUBST(JACK_CFLAGS)
+AC_SUBST(JACK_LIBS)
+AC_SUBST(HAVE_JACK)
+AM_CONDITIONAL([HAVE_JACK], [test "x$HAVE_JACK" = x1])
+
+#### Async DNS support (optional) ####
+
+AC_ARG_ENABLE([asyncns],
+    AC_HELP_STRING([--disable-asyncns], [Disable optional Async DNS support]),
+        [
+            case "${enableval}" in
+                yes) asyncns=yes ;;
+                no) asyncns=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-asyncns) ;;
+            esac
+        ],
+        [asyncns=auto])
+
+if test "x${asyncns}" != xno ; then
+    PKG_CHECK_MODULES(LIBASYNCNS, [ libasyncns >= 0.1 ],
+        HAVE_LIBASYNCNS=1,
+        [
+            HAVE_LIBASYNCNS=0
+            if test "x$asyncns" = xyes ; then
+                AC_MSG_ERROR([*** Async DNS support not found])
+            fi
+        ])
+else
+    HAVE_LIBASYNCNS=0
+fi
 
-PKG_CHECK_MODULES(LIBASYNCNS, [ libasyncns >= 0.1 ], HAVE_LIBASYNCNS=1, HAVE_LIBASYNCNS=0)
 AC_SUBST(LIBASYNCNS_CFLAGS)
 AC_SUBST(LIBASYNCNS_LIBS)
 AC_SUBST(HAVE_LIBASYNCNS)
@@ -143,70 +810,456 @@ if test "x$HAVE_LIBASYNCNS" != "x0" ; then
    AC_DEFINE([HAVE_LIBASYNCNS], 1, [Have libasyncns?])
 fi
 
-AC_PATH_PROG([M4], [m4 gm4], [no])
-if test "x$M4" = xno ; then
-   AC_MSG_ERROR([m4 missing])
+#### TCP wrappers (optional) ####
+
+AC_ARG_ENABLE([tcpwrap],
+    AC_HELP_STRING([--disable-tcpwrap], [Disable optional TCP wrappers support]),
+        [
+            case "${enableval}" in
+                yes) tcpwrap=yes ;;
+                no) tcpwrap=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-tcpwrap) ;;
+            esac
+        ],
+        [tcpwrap=auto])
+
+if test "x${tcpwrap}" != xno ; then
+    ACX_LIBWRAP
+    if test "x${LIBWRAP_LIBS}" = x && test "x$tcpwrap" = xyes ; then
+        AC_MSG_ERROR([*** TCP wrappers support not found])
+    fi
+else
+    LIBWRAP_LIBS=
 fi
 
-AC_MSG_CHECKING([for tcpwrap library and headers])
-LIBWRAP_LIBS=
-saved_LIBS="$LIBS"
-LIBS="$LIBS -lwrap"
-AC_LINK_IFELSE(
-AC_LANG_PROGRAM(
-[#include <tcpd.h>
-#include <syslog.h>
-int allow_severity = LOG_INFO;
-int deny_severity = LOG_WARNING;],
-[struct request_info *req; 
-return hosts_access (req);]),
-[AC_DEFINE(HAVE_LIBWRAP, [], [Have tcpwrap?])
-LIBWRAP_LIBS="-lwrap"
-AC_MSG_RESULT(yes)],
-[AC_MSG_RESULT(no)])
 AC_SUBST(LIBWRAP_LIBS)
-LIBS="$saved_LIBS"
 
-LIRC_CFLAGS=
-LIRC_LIBS=
-AC_CHECK_HEADER(lirc/lirc_client.h,[AC_CHECK_LIB(lirc_client,lirc_init,[HAVE_LIRC=1
-LIRC_LIBS=-llirc_client],HAVE_LIRC=0)],HAVE_LIRC=0)
+#### LIRC support (optional) ####
+
+AC_ARG_ENABLE([lirc],
+    AC_HELP_STRING([--disable-lirc], [Disable optional LIRC support]),
+        [
+            case "${enableval}" in
+                yes) lirc=yes ;;
+                no) lirc=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-lirc) ;;
+            esac
+        ],
+        [lirc=auto])
+
+if test "x${lirc}" != xno ; then
+    ACX_LIRC
+    if test "x${HAVE_LIRC}" = x0 && test "x$lirc" = xyes ; then
+        AC_MSG_ERROR([*** LIRC support not found])
+    fi
+else
+    HAVE_LIRC=0
+fi
+
 AC_SUBST(LIRC_CFLAGS)
 AC_SUBST(LIRC_LIBS)
 AM_CONDITIONAL([HAVE_LIRC], [test "x$HAVE_LIRC" = x1])
 
-AC_CHECK_HEADER(linux/input.h,HAVE_EVDEV=1,HAVE_EVDEV=0)
-AM_CONDITIONAL([HAVE_EVDEV], [test "x$HAVE_EVDEV" = x1])
+#### HAL support (optional) ####
 
-# If using GCC specify some additional parameters
-if test "x$GCC" = "xyes" ; then
-   CFLAGS="$CFLAGS -pipe -W -Wall -pedantic"
+AC_ARG_ENABLE([hal],
+    AC_HELP_STRING([--disable-hal], [Disable optional HAL support]),
+        [
+            case "${enableval}" in
+                yes) hal=yes ;;
+                no) hal=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-hal) ;;
+            esac
+        ],
+        [hal=auto])
+if test "x${hal}" != xno -a \( "x$HAVE_OSS" = "x1" -o "x$HAVE_ALSA" = "x1" \) ; then
+    PKG_CHECK_MODULES(HAL, [ hal >= 0.5.7 ],
+        HAVE_HAL=1,
+        [
+            HAVE_HAL=0
+            if test "x$hal" = xyes ; then
+                AC_MSG_ERROR([*** HAL support not found])
+            fi
+        ])
+else
+    HAVE_HAL=0
+fi
+
+AC_SUBST(HAL_CFLAGS)
+AC_SUBST(HAL_LIBS)
+AC_SUBST(HAVE_HAL)
+AM_CONDITIONAL([HAVE_HAL], [test "x$HAVE_HAL" = x1])
+
+#### BlueZ support (optional) ####
+
+AC_ARG_ENABLE([bluez],
+    AC_HELP_STRING([--disable-bluez], [Disable optional BlueZ support]),
+        [
+            case "${enableval}" in
+                yes) bluez=yes ;;
+                no) bluez=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-bluez) ;;
+            esac
+        ],
+        [bluez=auto])
+if test "x${bluez}" != xno ; then
+    PKG_CHECK_MODULES(BLUEZ, [ bluez >= 3.0 ],
+        HAVE_BLUEZ=1,
+        [
+            HAVE_BLUEZ=0
+            if test "x$bluez" = xyes ; then
+                AC_MSG_ERROR([*** BLUEZ support not found])
+            fi
+        ])
+else
+    HAVE_BLUEZ=0
+fi
+
+AC_SUBST(BLUEZ_CFLAGS)
+AC_SUBST(BLUEZ_LIBS)
+AC_SUBST(HAVE_BLUEZ)
+AM_CONDITIONAL([HAVE_BLUEZ], [test "x$HAVE_BLUEZ" = x1])
+
+#### D-Bus support (optional) ####
+
+AC_ARG_ENABLE([dbus],
+    AC_HELP_STRING([--disable-dbus], [Disable optional D-Bus support]),
+        [
+            case "${enableval}" in
+                yes) dbus=yes ;;
+                no) dbus=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-dbus) ;;
+            esac
+        ],
+        [dbus=auto])
 
-   AC_LANG_CONFTEST([int main() {}])
-   $CC -c conftest.c -std=c99 -Wno-unused-parameter $CFLAGS > /dev/null 2> /dev/null && CFLAGS="$CFLAGS -std=c99 -Wno-unused-parameter"
-   rm -f conftest.o
+if test "x$HAVE_HAL" = x1 ; then
+   dbus=yes
 fi
 
-# LYNX documentation generation
-AC_ARG_ENABLE(lynx,
-        AC_HELP_STRING(--disable-lynx,Turn off lynx usage for documentation generation),
+if test "x${dbus}" != xno || test "x${bluez}" != xno || "x${hal}" != xno ; then
+
+    PKG_CHECK_MODULES(DBUS, [ dbus-1 >= 1.0.0 ],
+        [
+            HAVE_DBUS=1
+            saved_LIBS="$LIBS"
+            LIBS="$LIBS $DBUS_LIBS"
+            AC_CHECK_FUNCS(dbus_watch_get_unix_fd)
+            LIBS="$saved_LIBS"
+            AC_DEFINE([HAVE_DBUS], 1, [Have D-Bus.])
+        ],
+        [
+            HAVE_DBUS=0
+            if test "x$dbus" = xyes ; then
+                AC_MSG_ERROR([*** D-Bus support not found])
+            fi
+        ])
+else
+    HAVE_DBUS=0
+fi
+
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+AC_SUBST(HAVE_DBUS)
+AM_CONDITIONAL([HAVE_DBUS], [test "x$HAVE_DBUS" = x1])
+
+#### PolicyKit support (optional) ####
+
+AC_ARG_ENABLE([polkit],
+    AC_HELP_STRING([--disable-polkit], [Disable optional PolicyKit support]),
+        [
+            case "${enableval}" in
+                yes) polkit=yes ;;
+                no) polkit=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-polkit) ;;
+            esac
+        ],
+        [polkit=auto])
+
+if test "x${polkit}" != xno ; then
+
+    PKG_CHECK_MODULES(POLKIT, [ polkit-dbus >= 0.7 ],
+        [
+            HAVE_POLKIT=1
+            AC_DEFINE([HAVE_POLKIT], 1, [Have PolicyKit])
+            policydir=`pkg-config polkit-dbus --variable prefix`/share/PolicyKit/policy/
+            AC_SUBST(policydir)
+        ],
+        [
+            HAVE_POLKIT=0
+            if test "x$polkit" = xyes ; then
+                AC_MSG_ERROR([*** PolicyKit support not found])
+            fi
+        ])
+else
+    HAVE_POLKIT=0
+fi
+
+AC_SUBST(POLKIT_CFLAGS)
+AC_SUBST(POLKIT_LIBS)
+AC_SUBST(HAVE_POLKIT)
+AM_CONDITIONAL([HAVE_POLKIT], [test "x$HAVE_POLKIT" = x1])
+
+### Build and Install man pages ###
+AC_ARG_ENABLE(manpages,
+        AS_HELP_STRING([--disable-manpages],[Disable building and installation of man pages]),
 [case "${enableval}" in
-  yes) lynx=yes ;;
-  no)  lynx=no ;;
-  *) AC_MSG_ERROR(bad value ${enableval} for --disable-lynx) ;;
-esac],[lynx=yes])
+  yes) manpages=yes ;;
+  no)  manpages=no ;;
+  *) AC_MSG_ERROR([bad value ${enableval} for --disable-manpages]) ;;
+esac],[manpages=yes])
 
-if test x$lynx = xyes ; then
-   AC_CHECK_PROG(have_lynx, lynx, yes, no)
+if test x$manpages = xyes ; then
+    #
+    # XMLTOMAN manpage generation
+    #
+    AC_ARG_ENABLE(xmltoman,
+    AS_HELP_STRING([--disable-xmltoman],[Enable rebuilding of man pages with xmltoman]),
+    [case "${enableval}" in
+      yes) xmltoman=yes ;;
+      no)  xmltoman=no ;;
+      *) AC_MSG_ERROR([bad value ${enableval} for --disable-xmltoman]) ;;
+    esac],[xmltoman=yes])
 
-   if test x$have_lynx = xno ; then
-     AC_MSG_WARN([*** lynx not found, plain text README will not be built ***])
-   fi
+    if test x$xmltoman = xyes ; then
+        AC_CHECK_PROG(have_xmltoman, xmltoman, yes, no)
+    fi
+
+    if test x$have_xmltoman = xno -o x$xmltoman = xno; then
+        if ! test -e man/pulseaudio.1 ; then
+            AC_MSG_ERROR([*** xmltoman was not found or was disabled, it is required to build the manpages as they have not been pre-built, install xmltoman, pass --disable-manpages or dont pass --disable-xmltoman])
+            exit 1
+        fi
+        AC_MSG_WARN([*** Not rebuilding man pages as xmltoman is not found ***])
+        xmltoman=no
+    fi
+fi
+AM_CONDITIONAL([USE_XMLTOMAN], [test "x$xmltoman" = xyes])
+AM_CONDITIONAL([BUILD_MANPAGES], [test "x$manpages" = xyes])
+
+#### PulseAudio system group & user  #####
+
+AC_ARG_WITH(system_user, AS_HELP_STRING([--with-system-user=<user>],[User for running the PulseAudio daemon as a system-wide instance (pulse)]))
+if test -z "$with_system_user" ; then
+    PA_SYSTEM_USER=pulse
+else
+    PA_SYSTEM_USER=$with_system_user
+fi
+AC_SUBST(PA_SYSTEM_USER)
+AC_DEFINE_UNQUOTED(PA_SYSTEM_USER,"$PA_SYSTEM_USER", [User for running the PulseAudio system daemon])
+
+AC_ARG_WITH(system_group,AS_HELP_STRING([--with-system-group=<group>],[Group for running the PulseAudio daemon as a system-wide instance (pulse)]))
+if test -z "$with_system_group" ; then
+    PA_SYSTEM_GROUP=pulse
+else
+    PA_SYSTEM_GROUP=$with_system_group
+fi
+AC_SUBST(PA_SYSTEM_GROUP)
+AC_DEFINE_UNQUOTED(PA_SYSTEM_GROUP,"$PA_SYSTEM_GROUP", [Group for the PulseAudio system daemon])
+
+AC_ARG_WITH(realtime_group,AS_HELP_STRING([--with-realtime-group=<group>],[Group for users that are allowed to start the PulseAudio daemon with realtime scheduling (realtime)]))
+if test -z "$with_realtime_group" ; then
+    PA_REALTIME_GROUP=pulse-rt
+else
+    PA_REALTIME_GROUP=$with_realtime_group
+fi
+AC_SUBST(PA_REALTIME_GROUP)
+AC_DEFINE_UNQUOTED(PA_REALTIME_GROUP,"$PA_REALTIME_GROUP", [Realtime group])
+
+AC_ARG_WITH(access_group,AS_HELP_STRING([--with-access-group=<group>],[Group which is allowed access to a system-wide PulseAudio daemon (pulse-access)]))
+if test -z "$with_access_group" ; then
+    PA_ACCESS_GROUP=pulse-access
+else
+    PA_ACCESS_GROUP=$with_access_group
+fi
+AC_SUBST(PA_ACCESS_GROUP)
+AC_DEFINE_UNQUOTED(PA_ACCESS_GROUP,"$PA_ACCESS_GROUP", [Access group])
+
+AC_ARG_ENABLE(
+        per_user_esound_socket,
+        AS_HELP_STRING([--disable-per-user-esound-socket], [Use global esound socket directory /tmp/.esd/socket.]),
+        [
+            case "${enableval}" in
+                yes) per_user_esound_socket=1 ;;
+                no) per_user_esound_socket=0 ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --disable-per-user-esound-socket) ;;
+            esac
+        ],
+        [per_user_esound_socket=1])
+
+if test "x$per_user_esound_socket" = "x1"; then
+   AC_DEFINE([USE_PER_USER_ESOUND_SOCKET], [1], [Define this if you want per-user esound socket directories])
 fi
 
-AM_CONDITIONAL([USE_LYNX], [test "x$lynx" = xyes])
+#### PulseAudio system runtime dir ####
+PA_SYSTEM_RUNTIME_PATH="${localstatedir}/run/pulse"
+AC_SUBST(PA_SYSTEM_RUNTIME_PATH)
+PA_SYSTEM_CONFIG_PATH="${localstatedir}/lib/pulse"
+AC_SUBST(PA_SYSTEM_CONFIG_PATH)
+PA_SYSTEM_STATE_PATH="${localstatedir}/lib/pulse"
+AC_SUBST(PA_SYSTEM_STATE_PATH)
 
-AM_CONDITIONAL(BUILD_LIBPOLYPCORE, false)
+###################################
+#            Output               #
+###################################
 
-AC_CONFIG_FILES([Makefile polyp/Makefile polyplib.pc polyplib-simple.pc polyplib-mainloop.pc polyplib-browse.pc polyplib-error.pc polyplib-glib-mainloop.pc polyplib-glib12-mainloop.pc doc/Makefile doc/README.html doc/cli.html doc/daemon.html doc/modules.html doxygen/Makefile doxygen/doxygen.conf polyp/polyplib-version.h doc/FAQ.html])
+AC_ARG_ENABLE(
+        [static-bins],
+        AC_HELP_STRING([--enable-static-bins],[Statically link executables.]),
+        [STATIC_BINS=1], [STATIC_BINS=0])
+AM_CONDITIONAL([STATIC_BINS], [test "x$STATIC_BINS" = "x1"])
+
+AC_ARG_WITH(
+        [preopen-mods],
+        AC_HELP_STRING([--with-preopen-mods],[Modules to preopen in daemon (default: all).]),
+        [PREOPEN_MODS=$withval], [PREOPEN_MODS="all"])
+AM_CONDITIONAL([PREOPEN_MODS], [test "x$PREOPEN_MODS" != "xall"])
+if test "x$PREOPEN_MODS" != "xall" ; then
+    tmpLIBS=""
+    for mod in $PREOPEN_MODS; do
+        tmpLIBS="$tmpLIBS module-$mod.la"
+    done
+    PREOPEN_MODS="$tmpLIBS"
+    AC_SUBST(PREOPEN_MODS)
+fi
+
+AC_ARG_WITH(
+        [module-dir],
+        AC_HELP_STRING([--with-module-dir],[Directory where to install the modules to (defaults to ${libdir}/pulse-${PA_MAJORMINOR}/modules/]),
+        [modlibexecdir=$withval], [modlibexecdir="${libdir}/pulse-${PA_MAJORMINOR}/modules/"])
+
+AC_SUBST(modlibexecdir)
+
+AC_ARG_ENABLE(
+        [force-preopen],
+        AC_HELP_STRING([--enable-force-preopen],[Preopen modules, even when dlopen() is supported.]),
+        [FORCE_PREOPEN=1], [FORCE_PREOPEN=0])
+AM_CONDITIONAL([FORCE_PREOPEN], [test "x$FORCE_PREOPEN" = "x1"])
+
+AC_CONFIG_FILES([
+Makefile
+src/Makefile
+man/Makefile
+libpulse.pc
+libpulse-simple.pc
+libpulse-browse.pc
+libpulse-mainloop-glib.pc
+doxygen/Makefile
+doxygen/doxygen.conf
+src/pulse/version.h
+])
 AC_OUTPUT
+
+# ==========================================================================
+ENABLE_X11=no
+if test "x$HAVE_X11" = "x1" ; then
+   ENABLE_X11=yes
+fi
+
+ENABLE_OSS=no
+if test "x$HAVE_OSS" = "x1" ; then
+   ENABLE_OSS=yes
+fi
+
+ENABLE_ALSA=no
+if test "x$HAVE_ALSA" = "x1" ; then
+   ENABLE_ALSA=yes
+fi
+
+ENABLE_SOLARIS=no
+if test "x$HAVE_SOLARIS" = "x1" ; then
+   ENABLE_SOLARIS=yes
+fi
+
+ENABLE_GLIB20=no
+if test "x$HAVE_GLIB20" = "x1" ; then
+   ENABLE_GLIB20=yes
+fi
+
+ENABLE_GCONF=no
+if test "x$HAVE_GCONF" = "x1" ; then
+   ENABLE_GCONF=yes
+fi
+
+ENABLE_AVAHI=no
+if test "x$HAVE_AVAHI" = "x1" ; then
+   ENABLE_AVAHI=yes
+fi
+
+ENABLE_JACK=no
+if test "x$HAVE_JACK" = "x1" ; then
+   ENABLE_JACK=yes
+fi
+
+ENABLE_LIBASYNCNS=no
+if test "x$HAVE_LIBASYNCNS" = "x1" ; then
+   ENABLE_LIBASYNCNS=yes
+fi
+
+ENABLE_LIRC=no
+if test "x$HAVE_LIRC" = "x1" ; then
+   ENABLE_LIRC=yes
+fi
+
+ENABLE_HAL=no
+if test "x$HAVE_HAL" = "x1" ; then
+   ENABLE_HAL=yes
+fi
+
+ENABLE_TCPWRAP=no
+if test "x${LIBWRAP_LIBS}" != x ; then
+   ENABLE_TCPWRAP=yes
+fi
+
+ENABLE_LIBSAMPLERATE=no
+if test "x${HAVE_LIBSAMPLERATE}" = "x1" ; then
+   ENABLE_LIBSAMPLERATE=yes
+fi
+
+ENABLE_BLUEZ=no
+if test "x${HAVE_BLUEZ}" = "x1" ; then
+   ENABLE_BLUEZ=yes
+fi
+
+ENABLE_POLKIT=no
+if test "x${HAVE_POLKIT}" = "x1" ; then
+   ENABLE_POLKIT=yes
+fi
+
+ENABLE_PER_USER_ESOUND_SOCKET=no
+if test "x$per_user_esound_socket" = "x1" ; then
+   ENABLE_PER_USER_ESOUND_SOCKET=yes
+fi
+
+echo "
+ ---{ $PACKAGE_NAME $VERSION }---
+
+    prefix:                        ${prefix}
+    sysconfdir:                    ${sysconfdir}
+    localstatedir:                 ${localstatedir}
+    System Runtime Path:           ${PA_SYSTEM_RUNTIME_PATH}
+    System State Path:             ${PA_SYSTEM_STATE_PATH}
+    System Config Path:            ${PA_SYSTEM_CONFIG_PATH}
+    Compiler:                      ${CC}
+    CFLAGS:                        ${CFLAGS}
+    Have X11:                      ${ENABLE_X11}
+    Enable OSS:                    ${ENABLE_OSS}
+    Enable Alsa:                   ${ENABLE_ALSA}
+    Enable Solaris:                ${ENABLE_SOLARIS}
+    Enable GLib 2.0:               ${ENABLE_GLIB20}
+    Enable GConf:                  ${ENABLE_GCONF}
+    Enable Avahi:                  ${ENABLE_AVAHI}
+    Enable Jack:                   ${ENABLE_JACK}
+    Enable Async DNS:              ${ENABLE_LIBASYNCNS}
+    Enable LIRC:                   ${ENABLE_LIRC}
+    Enable HAL:                    ${ENABLE_HAL}
+    Enable BlueZ:                  ${ENABLE_BLUEZ}
+    Enable TCP Wrappers:           ${ENABLE_TCPWRAP}
+    Enable libsamplerate:          ${ENABLE_LIBSAMPLERATE}
+    Enable PolicyKit:              ${ENABLE_POLKIT}
+    System User:                   ${PA_SYSTEM_USER}
+    System Group:                  ${PA_SYSTEM_GROUP}
+    Realtime Group:                ${PA_REALTIME_GROUP}
+    Access Group:                  ${PA_ACCESS_GROUP}
+    Enable per-user EsounD socket: ${ENABLE_PER_USER_ESOUND_SOCKET}
+"
diff --git a/doc/FAQ.html.in b/doc/FAQ.html.in
deleted file mode 100644 (file)
index 9cacfb4..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?> <!-- -*-html-helper-*- -->
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title>polypaudio: FAQ</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-
-<body>
-
-
-<h1>Frequently Asked Questions</h1>
-
-<ol>
-  <li><p><b>How does Polypaudio compare with ESOUND/aRts/NAS?</b></p>
-
-  <p>Polypaudio is sound daemon similar to ESOUND and NAS, but much more
-  powerful. aRts is a realtime-synthesizer-cum-sound-server, i.e. it
-  does much more than Polypaudio. However, I believe that Polypaudio
-  does what it does much better than any other free sound server.</p>
-  </li>
-
-  <li><p><b>What about ESOUND compatibility?</b></p>
-  <p>Polypaudio is a drop in replacement for ESOUND. That means: you can
-  load a esound compatibility module which implements an ESOUND
-  compatible protocol which allows you to use most of the classic ESOUND
-  compatible programs (including the command line programs like
-  <tt>esdcat</tt>).</p>
-  </li>
-
-  <li><p><b>Is Polypaudio a GNOME program?</b></p>
-  <p>No, Polypaudio has no dependency on GNOME/GTK/GLIB. All it requires
-  is a UNIX-like operating system and very few dependency
-  libraries. However, the accompanying GUI tools are writen with
-  gtkmm, i.e. require both GLIB and GTK.</p></li>
-
-  <li><p><b>Can I integrate Polypaudio in my GLIB/GTK/GNOME application?</b></p>
-  <p>Yes! Polypaudio comes with a GLIB main loop adapter. You can embed
-  both the client library and the daemon (!) into your GLIB based
-  application.</p></li>
-
-  <li><p><b>Can I integrate Polypaudio in my Qt/KDE application?</b></p>
-  <p>Yes! Polypaudio uses a main loop abstraction layer that allows you
-  to integrate Polypaudio in any program that supports main
-  loops. Unfortunately there is no adapter for Qt publicly available yet.</p></li>
-
-  <li><p><b>I want to write a new driver for Polypaudio, are there any docs?</b></p>
-  <p>Currently, only the client API is documented with doxygen. Read
-  the source and base your work on a simple module like
-  <tt>module-pipe-sink</tt>.</p></li>
-
-  <li><p><b>What about compatibility with NAS?</b></p>
-  <p>Is not available (yet?). It is doable, but noone has implemented it yet.</p></li>
-
-  <li><p><b>What about compatibility with aRts?</b></p>
-  <p>Is not available. Since aRts is as synthesizer application you'd have to
-  reimplement very much code for Polypaudio. It should be easy to
-  implement limited support for <tt>libartsc</tt> based
-  applications. Noone has done this yet. It is probably a better idea to
-  run <tt>arts</tt> on top of Polypaudio (through a polypaudio driver
-  for aRts, which nobody has written yet). Another solution would be to
-  embed Polypaudio in the aRts process.</p></li>
-
-  <li><p><b>I often hear noises when playing back with Polypaudio, what can I do?</b></p>
-  <p>There are to possible solutions: run polypaudio with argument
-<tt>--high-priority=1</tt> and make yourself member of the group
-<tt>realtime</tt>, or increase the fragment sizes of the audio
-  drivers. The former will allow Polypaudio to activate
-  <tt>SCHED_FIFO</tt> high priority scheduling (root rights are dropped
-  immediately after this) Keep in mind that this is a potential security hole!</p></li>
-
-   <li><p><b>The <tt>polypaudio</tt>  executable is installed SUID root by default. Why this? Isn't this a potential security hole?</b></p>
-
-  <p>Polypaudio activates <tt>SCHED_FIFO</tt> scheduling if the user
-passes <tt>--high-priority=1</tt>. This will only succeed when
-executed as root, therefore the binary is marked SUID root by
-default. Yes, this is a potential security hole. However, polypaudio
-tries its best to minimize the security threat: immediately after
-startup polypaudio drops all capabilities except
-<tt>CAP_SYS_NICE</tt> (At least on systems that support it, like Linux; see <tt>man 7
-capabilities</tt> for more information). If the calling user is not a
-member of the group <tt>realtime</tt> (which is required to have a GID
-< 1000), root rights are dropped immediately. This means, you can
-install polypaudio SUID root, but only a subset of your users (the
-members of the group <tt>realtime</tt>) may make use of realtime
-scheduling. Keep in mind that these users might load their own binary
-modules into the polypaudio daemon which may freeze the machine. The
-daemon has a minimal protection against CPU hogging (the daemon is
-killed after hogging more than 70% CPU for 5 seconds), but this may
-be circumvented easily by evildoers.</p></li>
-  
-  <li><p><b>I want to run polypaudio only when it is needed, how do I do this?</b></p>
-
-  <p>Set <tt>autospawn = yes</tt> in <tt>client.conf</tt>. That
-configuration file may be found either in <tt>/etc/polypaudio/</tt> or
-in <tt>~/.polypaudio/</tt>.</p></li>
-
-  <li><p><b>How do I list all polypaudio modules installed?</b></p>
-
-   <p><tt>polypaudio --dump-modules</tt></p>
-
-   <p>Add <tt>-v</tt> for terse usage instructions.</p>
-
-<li><p><b>How do I use polypaudio over the network?</b></p>
-
-<p>Just set <tt>$POLYP_SERVER</tt> to the host name of the polypaudio server.</p>
-
-<li><p><b>Is polypaudio capable of providing synchronized audio playback over the network for movie players like <tt>mplayer</tt>?</b></p>
-
-<p>Yes! Unless your network is congested in some way (i.e. transfer latencies vary strongly) it works perfectly. Drop me an email for experimental patches for MPlayer.</p>
-
-   <li><p><b>What environment variables does polypaudio care about?</b></p>
-
-<p>The client honors: <tt>POLYP_SINK</tt> (default sink to connect to), <tt>POLYP_SOURCE</tt> (default source to connect to), <tt>POLYP_SERVER</tt> (default server to connect to, like <tt>ESPEAKER</tt>), <tt>POLYP_BINARY</tt> (the binary to start when autospawning a daemon), <tt>POLYP_CLIENTCONFIG</tt> (path to the client configuration file).</p>
-
-<p>The daemon honors: <tt>POLYP_SCRIPT</tt> (default CLI script file run after startup), <tt>POLYP_CONFIG</tt> (default daemon configuration file), <tt>POLYP_DLPATH</tt> (colon separated list of paths where to look for modules)</p></li>
-
-
-<li><p><b>I saw that SIGUSR2 provokes loading of the module <tt>module-cli-protocol-unix</tt>. But how do I make use of that?</b></p>
-
-<p>A brilliant guy named Lennart Poettering once wrote a nifty tool
-for that purpose: <a
-href="http://0pointer.de/lennart/projects/bidilink/">bidilink</a>. To
-connect to a running polypaudio daemon try using the following commands:</p>
-
-<pre>killall -USR2 polypaudio
-bidilink unix-client:/tmp/polypaudio/cli</pre>
-
-<p><i>BTW: Someone should package that great tool for Debian!</i></p>
-
-
-<p><b>New:</b> There's now a tool <tt>pacmd</tt> that automates sending SIGUSR2 to the daemon and running a bidilink like tool for you.</p>
-</li>
-
-<li><p><b>How do the polypaudio libraries decide where to connect to?</b></p>
-<p>The following rule applies:</p>
-<ol>
-  <li>If the the application using the library specifies a server to connect to it is used. If the connection fails, the library fails too.</li>
-  <li>If the environment variable <tt>POLYP_SERVER</tt> is defined the library connects to that server. If the connection fails, the library fails too.</li>
-  <li>If <tt>$DISPLAY</tt> is set, the library tries to connect to that server and looks for the root window property <tt>POYLP_SERVER</tt> for the host to connect to. If <tt>POLYP_COOKIE</tt> is set it is used as authentication cookie.</li>
-  <li>If the client configuration file (<tt>~/.polypaudio/client.conf</tt> or <tt>/etc/polypaudio/client.conf</tt>) sets the server address, the library connects to that server. If the connection fails, the library fails too.</li>
-  <li>The library tries to connect to the default local UNIX socket for polypaudio servers. If the connection fails, it proceeds with the next item.</li>
-  <li>The library tries to connect to the default local TCP socket for polypaudio servers. If the connection fails, it proceeds with the next item.</li>
-  <li>If <tt>$DISPLAY</tt> is set, the library tries to connect to the default TCP port of that host. If the connection fails, it proceeds with the next item.</li>
-  <li>The connection fails.</li>
-</ol>
-</li>
-
-<li><p><b>Why the heck does libpolyp link against libX11?</b></p>
-<p>The Polypaudio client libraries look for some X11 root window properties for the credentials of the Polypaudio server to access. You may compile Polypaudio without X11 for disabling this.</p></li>
-
-</ol>
-
-<hr/>
-<address class="grey">Lennart Poettering &lt;@PACKAGE_BUGREPORT@&gt;, September 2004</address>
-<div class="grey"><i>$Id$</i></div>
-</body> </html>
diff --git a/doc/Makefile.am b/doc/Makefile.am
deleted file mode 100644 (file)
index fff0655..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-# $Id$
-
-# This file is part of polypaudio.
-#
-# polypaudio is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# polypaudio is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software Foundation,
-# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
-noinst_DATA = README.html cli.html modules.html daemon.html README
-EXTRA_DIST = $(noinst_DATA) style.css README.html.in cli.html.in modules.html.in daemon.html.in todo FAQ.html.in
-
-MAINTAINERCLEANFILES = README.html cli.html modules.html daemon.html FAQ.html
-CLEANFILES =
-
-if USE_LYNX
-README: README.html
-       lynx --dump $^ | sed 's,file://localhost/.*/doc/README.html,README,' > $@
-
-CLEANFILES += README
-endif
-
-tidy: README.html cli.html modules.html daemon.html
-       tidy -qe < README.html ; true
-       tidy -qe < cli.html ; true
-       tidy -qe < daemon.html ; true
-       tidy -qe < modules.html ; true
-       tidy -qe < FAQ.html ; true
-
-.PHONY: tidy
-
diff --git a/doc/README.html.in b/doc/README.html.in
deleted file mode 100644 (file)
index eb57fdb..0000000
+++ /dev/null
@@ -1,289 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?> <!-- -*-html-helper-*- -->
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-<head>
-<title>polypaudio @PACKAGE_VERSION@</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-
-<body>
-<h1><a name="top">polypaudio @PACKAGE_VERSION@</a></h1>
-
-<p><i>Copyright 2004 Lennart Poettering &lt;@PACKAGE_BUGREPORT@&gt;</i></p>
-
-<ul class="toc">
-    <li><a href="#license">License</a></li>
-    <li><a href="#news">News</a></li>
-    <li><a href="#overview">Overview</a></li>
-    <li><a href="#status">Status</a></li>
-    <li><a href="#documentation">Documentation</a></li>
-    <li><a href="#requirements">Requirements</a></li>
-    <li><a href="#installation">Installation</a></li>
-    <li><a href="#acks">Acknowledgements</a></li>
-    <li><a href="#download">Download</a></li>
-</ul>
-
-<h2><a name="license">License</a></h2>
-
-<p>This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public License as
-published by the Free Software Foundation; either version 2 of the
-License, or (at your option) any later version.</p>
-
-<p>This program is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Lesser General Public License for more details.</p>
-
-<p>You should have received a copy of the GNU Lesser General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.</p>
-
-<h2><a name="news">News</a></h2>
-
-<div class="news-date">Sun Nov 21 2004: </div> <p class="news-text"><a
-href="@PACKAGE_URL@polypaudio-0.7.tar.gz">Version 0.7</a> released;
-changes include: IPv6 support; PID file support; publish credentials
-in X11 root window (<tt>module-x11-publish</tt>; new tool <tt>pacmd</tt>; ESOUND backend; new command <tt>load-sample-dir-lazy</tt>; many, many minor fixes.</p>
-
-<div class="news-date">Thu Oct 28 2004: </div> <p class="news-text"><a
-href="@PACKAGE_URL@polypaudio-0.6.tar.gz">Version 0.6</a> released;
-changes include: TCP wrappers support; don't load the complete sound
-file into memory when playing back using <tt>pa_play_file()</tt>;
-autoload API change; don't load all sound files as FLOAT32; shorten
-default buffers; client-side latency interpolation; add new user
-volume metrics; add <tt>module-tunnel</tt>, <tt>module-null-sink</tt>,
-<tt>module-match</tt> and new tool <tt>paplay</tt>; new API version
-macros; many client API improvements; correctly lock cookie file
-generation; correctly lock daemon autospawning; print daemon layout to
-STDERR on SIGHUP; new options for <tt>pacat</tt>: allow sample type specification.</p>
-
-<div class="news-date">Mon Sep 24 2004: </div> <p class="news-text"><a
-href="@PACKAGE_URL@polypaudio-0.5.1.tar.gz">Version 0.5.1</a> released;
-changes include: improve esound protocol compatibility; fix
-autospawning via <tt>libesd</tt>; make use of POSIX capabilities;
-allow <tt>SCHED_FIFO</tt> scheduling only for users in group
-<tt>realtime</tt>; minor build system fix.</p>
-
-<div class="news-date">Mon Sep 20 2004: </div> <p class="news-text"><a
-href="@PACKAGE_URL@polypaudio-0.5.tar.gz">Version 0.5</a> released;
-changes include: extensive API improvements, new module
-<tt>module-combine</tt> for combining multiple sound cards into one,
-gcc 2.95 compatibility, configuration files, add "lazy" samples,
-support for source and network latency measurements, add
-<tt>module-pipe-source</tt>, many other fixes and improvements.</p>
-
-<div class="news-date">Wed Sep 8 2004: </div> <p class="news-text"><a
-href="@PACKAGE_URL@polypaudio-0.4.tar.gz">Version 0.4</a> released;
-changes include: daemon auto spawning, support for <tt>SCHED_FIFO</tt> scheduling, three new modules, proper logging, CPU load watchdog, many fixes.</p>
-
-<div class="news-date">Fri Aug 27 2004: </div> <p class="news-text"><a
-href="@PACKAGE_URL@polypaudio-0.3.tar.gz">Version 0.3</a> released;
-changes include: support for both glib 2.0 and glib 1.2, future cancellation, API updates, many fixes, relicense client library to LGPL.</p>
-
-<div class="news-date">Fri Aug 20 2004: </div> <p class="news-text"><a
-href="@PACKAGE_URL@polypaudio-0.2.tar.gz">Version 0.2</a> released;
-changes include: added sample cache, introspection API, client API
-documentation, module autoloading, glib support, a module for intercepting X11 bell events, and much more.</p>
-
-<div class="news-date">Sat Jul 17 2004: </div> <p class="news-text"><a
-href="@PACKAGE_URL@polypaudio-0.1.tar.gz">Version 0.1</a> released</p>
-
-<h2><a name="overview">Overview</a></h2>
-
-<p><tt>polypaudio</tt> is a sound server for Linux and other Unix like
-operating systems. It is intended to be an improved drop-in
-replacement for the <a
-href="http://www.tux.org/~ricdude/apps.html">Enlightened Sound
-Daemon</a> (ESOUND). It is my ultimate ambition to get Polypaudio into
-<a href="http://www.gnome.org/">Gnome</a> as a replacement for ESOUND. In
-addition to the features ESOUND provides <tt>polypaudio</tt> has:</p>
-
-<ul>
-  <li>Extensible plugin architecture (by loading dynamic loadable modules with <tt>dlopen()</tt>)</li>
-  <li>Support for more than one sink/source</li>
-  <li>Better low latency behaviour</li>
-  <li>Embedabble into other software (the core is available as C library)</li>
-  <li>Completely asynchronous C API</li>
-  <li>Simple command line interface for reconfiguring the daemon while running</li>
-  <li>Flexible, implicit sample type conversion and resampling</li>
-  <li>"Zero-Copy" architecture</li>
-  <li>Module autoloading</li>
-  <li>Very accurate latency measurement for playback and recordin.</li>
-  <li>May be used to combine multiple sound cards to one (with sample rate adjustment)</li>
-  <li>Client side latency interpolation</li>
-</ul>
-
-<p>Both the core and the client API are completely asynchronous making
-use of a simple main loop abstraction layer. This allows easy
-integration with asynchronous applications using the
-<tt>glib</tt>/<tt>gtk</tt> mainloop. Since the asynchronous API
-available through <tt>polyplib</tt> is quite difficult to use there is
-a simplified synchronous API wrapper <tt>polyplib-simple</tt>
-available. A simple main loop implementation is available as well.</p>
-
-<p>The following modules are currently available:</p>
-
-<ul>
-  <li><tt>module-oss</tt>: driver for Open Sound System audio sinks and sources.</li>
-  <li><tt>module-oss-mmap</tt>: same as above, but uses <tt>mmap()</tt> access to the audio buffer. Not as compatible</li>
-  <li><tt>module-alsa-sink</tt>, <tt>module-alsa-source</tt>: drivers for ALSA sinks and sources</li>
-  <li><tt>module-pipe-sink</tt>, <tt>module-pipe-source</tt>: demonstration module providing UNIX fifos backed sinks/sources</li>
-  <li><tt>module-combine</tt>: combine multiple sinks into one.</li>
-  <li><tt>module-sine</tt>: a sine generate sink input.</li>
-  <li><tt>module-x11-bell</tt>: play a sample from the sample cache on every X11 bell event.</li>
-  <li><tt>module-x11-publish</tt>: store Polypaudio credentials in the X11 root window.</li>
-  <li><tt>module-esound-protocol-tcp</tt>, <tt>module-esound-protocol-tcp6</tt>, <tt>module-esound-protocol-unix</tt>: ESOUND compatibility modules (for TCP/IPv6 resp. TCP/IPv6 resp. UNIX domain sockets)</li>
-  <li><tt>module-native-protocol-tcp</tt>, <tt>module-native-protocol-tcp6</tt>, <tt>module-native-protocol-unix</tt>: Native polypaudio protocol (for TCP/IPv4 resp. TCP/IPv6 resp. UNIX domain sockets)</li>
-  <li><tt>module-simple-protocol-tcp</tt>, <tt>module-simple-protocol-tcp6</tt>, <tt>module-simple-protocol-unix</tt>: Simplistic protocol for playback/capture for usage with tools like <tt>netcat</tt> (for TCP/IP resp. UNIX domain sockets)</li>
-  <li><tt>module-cli-protocol-tcp</tt>, <tt>module-cli-protocol-tcp6</tt>, <tt>module-cli-protocol-unix</tt>, <tt>module-cli</tt>: Expose polypaudio's internals whith a simple command line interface. (for TCP/IP resp. UNIX domain sockets resp. STDIN/STDOUT)</li>
-  <li><tt>module-tunnel-sink</tt>, <tt>module-tunnel-source</tt>: make sinks/sources from other hosts available locally.</li>
-  <li><tt>module-match</tt>: adjust volume automatically for newly created playback streams based on a regular expression matching table.</li>
-  <li><tt>module-null-sink</tt>: a clocked sink similar to <tt>/dev/null</tt>.</li>
-  <li><tt>module-esound-sink</tt>: a sink for forwarding audio data to an ESOUND server.</li>
-</ul>
-
-<p><tt>polypaudio</tt> is the successor of my previous, ill-fated
-attempt to write a sound server, <a
-href="http://asd.sf.net/">asd</a>.</p>
-
-<p>A GTK GUI manager application for polypaudio is the <a
-href="http://0pointer.de/lennart/projects/paman/">Polypaudio
-Manager</a>. Another GTK GUI tool for Polypaudio is the <a
-href="http://0pointer.de/lennart/projects/pavumeter">Polypaudio Volume
-Meter</a>.</p>
-
-<p>There are output plugins for <a
-href="http://0pointer.de/lennart/projects/xmms-polyp/">XMMS</a>, <a
-href="http://0pointer.de/lennart/projects/libao-polyp/">libao</a>
-(merged in <tt>libao</tt> SVN) and <a
-href="http://0pointer.de/lennart/projects/gst-polyp/">gstreamer</a>
-(merged in <tt>gstreamer-plugins</tt> CVS), <a
-href="http://mplayerhq.hu">MPlayer</a> (merged in MPlayer CVS) and <a
-href="http://xine.sf.net/">Xine</a> (merged in Xine CVS). Drivers for
-<a href="http://www.portaudio.com/">PortAudio</a> will be released
-shortly.</p>
-
-<h2><a name="status">Status</a></h2>
-
-<p>Version @PACKAGE_VERSION@ is quite usable. It matches and supersedes ESOUND's feature set in nearly all areas.</p>
-
-<p><b>Warning:</b> polypaudio's client API and protocol are not stable
-yet. The client interface is still a moving target and changes from
-release to release. The client API's library version number is currently fixed to <tt>0.0.0</tt>.</p>
-
-<h2><a name="documentation">Documentation</a></h2>
-
-<p>There is some preliminary documentation available: <a
-href="modules.html"><tt>modules.html</tt></a>, <a
-href="cli.html"><tt>cli.html</tt></a>, <a
-href="daemon.html"><tt>daemon.html</tt></a>, <a href="FAQ.html"><tt>FAQ.html</tt></a>, .</p>
-
-<h3>First Steps</h3>
-
-<p>Simply start the polypaudio daemon with the argument <tt>-nC</tt></p>
-
-<pre>polypaudio -nC</pre>
-
-<p>This will present you a screen like this:</p>
-
-<pre>Welcome to polypaudio! Use "help" for usage information.
-&gt;&gt;&gt; </pre>
-
-<p>Now you can issue CLI commands as described in <a
-href="cli.html"><tt>cli.html</tt></a>. Another way to start
-<tt>polypaudio</tt> is by specifying a configuration script like that one included in the distribution on the
-command line :</p>
-
-<pre>polypaudio -nF polypaudio.pa</pre>
-
-<p>This will load some drivers and protocols automatically.</p>
-
-<p>The best idea is to configure your daemon in <tt>/etc/polypaudio/daemon.conf</tt> and <tt>/etc/polypaudio/default.pa</tt> and to run polypaudio without any arguments.</p>
-
-<p><b>Beware!</b> Unless you pass the option <tt>--sysconfdir=/etc</tt> to
-<tt>configure</tt>, the directory <tt>/etc/polypaudio/</tt> is really
-<tt>/usr/local/etc/polypaudio/</tt>.</p>
-
-<h3>Developing polypaudio Clients</h3>
-
-<p>You may browse the <a href="http://www.doxygen.org/">Doxygen</a> generated <a
-href="http://0pointer.de/lennart/projects/polypaudio/doxygen/">programing
-documentation</a> for the client API. (Run <tt>make doxygen</tt> to generate this documentation from the source tree)</p>
-
-<h3>Developing polypaudio Modules</h3>
-
-<p>There are several reasons for writing loadable modules for polypaudio:</p>
-
-<ul>
-  <li>Device driver support in addition to ALSA/OSS</li>
-  <li>Protocol support beyond ESOUND's protocol and the native protocol. (such as NAS or a subset of aRts)</li>
-  <li>New programming interfaces such as XMLRPC or DBUS for controlling the daemon.</li>
-  <li>Hooking audio event sources directly into polypaudio (similar to <tt>module-x11-bell</tt>)</li>
-  <li>For low latency applications such as VOIP: load the VOIP core directly into polypaudio and have a slim GUI frontend to control it.</li>
-</ul>
-
-<p>There is currently no documentation how to write loadable modules
-for polypaudio. <i>Read the source, Luke!</i> If you are interested in
-writing new modules feel free to contact the author in case you have any
-questions.</p>
-
-<h2><a name="requirements">Requirements</a></h2>
-
-<p>Currently, <tt>polypaudio</tt> is tested on Linux and FreeBSD only. It requires an OSS or ALSA compatible soundcard.</p>
-
-<p><tt>polypaudio</tt> was developed and tested on Debian GNU/Linux
-"testing" from November 2004, it should work on most other Linux
-distributions (and maybe Unix versions) since it uses GNU autoconf and
-GNU libtool for source code configuration and shared library
-management.</p>
-
-<p><tt>polypaudio</tt> needs <tt>libwrap</tt>, <a
-href="http://www.mega-nerd.com/SRC/">Secret Rabbit Code (aka
-<tt>libsamplerate</tt>)</a>, <a
-href="http://www.mega-nerd.com/libsndfile"><tt>libsndfile</tt></a>, <a
-href="http://www.alsa-project.org/">alsa-lib</a> and <a
-href="http://www.gtk.org/">GLIB</a>. (The latter is required for
-building the GLIB main loop integration module only.)</p>
-<h2><a name="installation">Installation</a></h2>
-
-<p>As this package is made with the GNU autotools you should run
-<tt>./configure</tt> inside the distribution directory for configuring
-the source tree. After that you should run <tt>make</tt> for
-compilation and <tt>make install</tt> (as root) for installation of
-<tt>polypaudio</tt>.</p>
-
-<h2><a name="acks">Acknowledgements</a></h2>
-
-<p>Eric B. Mitchell for writing ESOUND</p>
-
-<p>Jeff Waugh for creating Ubuntu packages (and hopefully soon Debian)</p>
-
-<p>Miguel Freitas for writing a Polypaudio driver for Xine</p>
-
-<p>Joe Marcus Clarke for porting Polypaudio to FreeBSD</p>
-
-<h2><a name="download">Download</a></h2>
-
-<p>The newest release is always available from <a href="@PACKAGE_URL@">@PACKAGE_URL@</a></p>
-
-<p>The current release is <a href="@PACKAGE_URL@polypaudio-@PACKAGE_VERSION@.tar.gz">@PACKAGE_VERSION@</a></p>
-
-<p>Get <tt>polypaudio</tt>'s development sources from the <a href="http://subversion.tigris.org/">Subversion</a> <a href="svn://seth.intheinter.net/polypaudio">repository</a> (<a href="http://0pointer.de/cgi-bin/viewcvs.cgi/?root=polypaudio">viewcvs</a>): </p>
-
-<pre>svn checkout svn://seth.intheinter.net/polypaudio/trunk polypaudio</pre>
-
-<p>If you want to be notified whenever I release a new version of this software use the subscription feature of <a href="http://freshmeat.net/projects/polypaudio/">Freshmeat</a>.</p>
-
-<p><b>New!</b> There is a general discussion <a href="https://seth.intheinter.net/mailman/listinfo/polypaudio-discuss">mailing list for polypaudio</a> available.</p>
-
-<p><b>New!</b> There is now a <a href="http://0pointer.de/trac/polypaudio/">Polypaudio wiki</a> (based on <a href="http://www.edgewall.com/products/trac/">trac</a>) available.</p>
-
-<hr/>
-<address class="grey">Lennart Poettering &lt;@PACKAGE_BUGREPORT@&gt;, November 2004</address>
-<div class="grey"><i>$Id$</i></div>
-
-</body>
-</html>
diff --git a/doc/cli.html.in b/doc/cli.html.in
deleted file mode 100644 (file)
index 61d29e5..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?> <!-- -*-html-helper-*- -->
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title>polypaudio: Simple Command Line Language</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-
-<body>
-<h1>Simple Command Line Language</h1>
-
-<p><tt>polypaudio</tt> provides a simple command line language used by
-configuration scripts as well as the modules <tt>module-cli</tt>
-and <tt>module-cli-protocol-{unix,tcp}</tt>. Empty lines and lines
-beginning with a hashmark (<tt>#</tt>) are silently ignored. Several
-commands are supported:</p>
-
-<h2>Miscellaneous Commands</h2>
-
-<h3><tt>help</tt></h3>
-
-<p>Show a quick help on the commands available.</p>
-
-<h3><tt>exit</tt></h3>
-
-<p>Terminate the daemon. If you want to terminate a CLI connection
-("log out") you might want to use <tt>C-d</tt>.</p>
-
-<h2>Status Commands</h2>
-
-<h3><tt>list-modules</tt></h3>
-
-<p>Show all currently loaded modules with their arguments.</p>
-
-<h3><tt>list-sinks/list-sources</tt></h3>
-
-<p>Show all currently registered sinks (resp. sources).</p>
-
-<h3><tt>list-clients</tt></h3>
-
-<p>Show all currently active clients.</p>
-
-<h3><tt>list-sink-inputs/list-sink-outputs</tt></h3>
-
-<p>Show all currently active inputs to sinks (resp. outputs of sources).</p>
-
-<h3><tt>stat</tt></h3>
-
-<p>Show some simple statistics about the allocated memory blocks and
-the space used by them.</p>
-
-<h3><tt>info</tt></h3>
-
-<p>A combination of all status commands described above. <tt>ls</tt>
-and <tt>list</tt> are synonyms for <tt>info</tt>.</p>
-
-<h2>Module Management</h2>
-
-<h3><tt>load-module</tt></h3>
-
-<p>Load a module specified by its name and arguments. For most modules
-it is OK to be loaded more than once.</p>
-
-<h3><tt>unload-module</tt></h3>
-
-<p>Unload a module specified by its index in the module list as
-returned by <tt>modules</tt>.</p>
-
-<h2>Configuration Commands</h2>
-
-<h3><tt>set-sink-volume</tt></h3>
-
-<p>Set the volume of the specified sink. You may specify the sink either
-by its index in the sink list or by its name. The volume should be an
-integer value greater or equal than 0 (= muted). Volume 256
-(<tt>0x100</tt>) is normal volume, values greater than this amplify
-the audio signal with clipping.</p>
-
-<h3><tt>set-sink-input-volume</tt></h3>
-
-<p>Set the volume of a sink input specified by its index the the sink
-input list. The same volume rules apply as with <tt>sink_volume</tt>.</p>
-
-<h3><tt>set-default-sink</tt>/<tt>set-default-source</tt></h3>
-
-<p>Make a sink (resp. source) the default. You may specify the sink
-(resp. ssource) by its index in the sink (resp. source) list or by its
-name.</p>
-
-<h2>Sample Cache</h2>
-
-<h3><tt>list-samples</tt></h3>
-
-<p>Lists the contents of the sample cache.</p>
-
-<h3><tt>play-sample</tt></h3>
-
-<p>Play a sample cache entry to a sink. Expects the sample name and the sink name as arguments.</p>
-
-<h3><tt>remove-sample</tt></h3>
-
-<p>Remove an entry from the sample cache. Expects the sample name as argument.</p>
-
-<h3><tt>load-sample</tt></h3>
-
-<p>Load an audio file to the sample cache. Expects the file name to load and the desired sample name as arguments.</p>
-
-<h3><tt>load-sample-lazy</tt></h3>
-
-<p>Create a new entry in the sample cache, but don't load the sample
-immediately. The sample is loaded only when it is first used. After a
-certain idle time it is freed again. Expects the the desired sample
-name and file name to load as arguments.</p>
-
-<h3><tt>load-sample-dir-lazy</tt></h3>
-
-<p>Load all entries in the specified directory into the sample cache
-as lazy entries. A shell globbing expression (e.g. <tt>*.wav</tt>) may
-be appended to the path of the directory to add.</p>
-
-<h2>Module Autoloading</h2>
-
-<h3><tt>list-autoload</tt></h3>
-
-<p>Lists all currently defined autoloading entries.</p>
-
-<h3><tt>add-autoload-sink/add-autoload-source</tt></h3>
-
-<p>Adds an autoloading entry for a sink (resp. source). Expects the sink name (resp. source name), the module name and the module arguments as arguments.</p>
-
-<h3><tt>remove-autoload-sink/remove-autoload-source</tt></h3>
-
-<p>Remove an autoloading entry. Expects the sink name (resp. source name) as argument.</p>
-
-<h2>Miscellaneous Commands</h2>
-
-<h3><tt>play-file</tt></h3>
-
-<p>Play an audio file to a sink. Expects the file name and the sink name as argumens.</p>
-
-<h3><tt>dump</tt></h3>
-
-<p>Dump the daemon's current configuration in CLI commands.</p>
-
-<h2>Killing Clients/Streams</h2>
-
-<h3><tt>kill-client</tt></h3>
-
-<p>Remove a client forcibly from the server. There is no protection that
-the client reconnects immediately.</p>
-
-<h3><tt>kill-sink-input/kill-source-output</tt></h3>
-
-<p>Remove a sink input (resp. source output) forcibly from the
-server. This will not remove the owning client or any other streams
-opened by the client from the server.</p>
-
-<h2>Meta Commands</h2>
-
-<p>In addition the the commands described above there a few meta
-directives supported by the command line interpreter:</p>
-
-<h3><tt>.include</tt></h3>
-
-<p>Executes the commands from the specified script file.</p>
-
-<h3><tt>.fail/.nofail</tt></h3>
-
-<p>Enable (resp. disable) that following failing commands will cancel
-the execution of the current script file. This is a ignored when used
-on the interactive command line.</p>
-
-<h3><tt>.verbose/.noverbose</tt></h3>
-<p>Enable (resp. disable) extra verbosity.</p>
-
-<h2>Example Configuration Script</h2>
-
-<p>Mark the following script as executable (<tt>chmod +x</tt>) and run it for a sensible polypaudio configuration.</p>
-
-<pre>
-#!/usr/bin/polaudio -nF
-
-# Create autoload entries for the device drivers
-add-autoload-sink output module-alsa-sink device=plughw:0,0 rate=48000 sink_name=output
-add-autoload-sink output2 module-oss device=/dev/dsp1 record=0 sink_name=output2
-add-autoload-sink combined module-combine master=output slaves=output2 sink_name=combined
-
-add-autoload-source input module-alsa-source device=hw:1,0 source_name=input
-
-# Load several protocols
-load-module module-esound-protocol-unix
-load-module module-simple-protocol-tcp
-load-module module-native-protocol-unix
-load-module module-cli-protocol-unix
-
-# Make some devices default
-set-default-sink combined
-set-default-source input
-
-# Don't fail if the audio files referred to below don't exist
-.nofail
-
-# Load an audio to the sample cache for usage with module-x11-bell
-load-sample-lazy  /usr/share/sounds/KDE_Notify.wav x11-bell
-load-module module-x11-bell sample=x11-bell
-
-# Play a welcome sound
-play-file /usr/share/sounds/startup3.wav combined
-</pre>
-
-<hr/>
-<address class="grey">Lennart Poettering &lt;@PACKAGE_BUGREPORT@&gt;, November 2004</address>
-<div class="grey"><i>$Id$</i></div>
-</body> </html>
diff --git a/doc/daemon.html.in b/doc/daemon.html.in
deleted file mode 100644 (file)
index a4db0bd..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?> <!-- -*-html-helper-*- -->
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title>polypaudio: Daemon</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-
-<body>
-<h1>Daemon</h1>
-
-<h2>Command Line Arguments</h2>
-
-The <tt>polypaudio</tt> daemon accepts several command line arguments:
-
-<pre>
-COMMANDS:
-  -h, --help                            Show this help
-      --version                         Show version
-      --dump-conf                       Dump default configuration
-      --dump-modules                    Dump list of available modules
-  -k  --kill                            Kill a running daemon
-      --check                           Check for a running daemon
-
-OPTIONS:
-  -D, --daemonize[=BOOL]                Daemonize after startup
-      --fail[=BOOL]                     Quit when startup fails
-      --verbose[=BOOL]                  Be slightly more verbose
-      --high-priority[=BOOL]            Try to set high process priority
-                                        (only available as root)
-      --disallow-module-loading[=BOOL]  Disallow module loading after startup
-      --exit-idle-time=SECS             Terminate the daemon when idle and this
-                                        time passed
-      --module-idle-time=SECS           Unload autoloaded modules when idle and
-                                        this time passed
-      --scache-idle-time=SECS           Unload autoloaded samples when idle and
-                                        this time passed
-      --log-target={auto,syslog,stderr} Specify the log target
-  -p, --dl-search-path=PATH             Set the search path for dynamic shared
-                                        objects (plugins)
-      --resample-method=[METHOD]        Use the specified resampling method
-                                        (one of src-sinc-medium-quality,
-                                        src-sinc-best-quality,src-sinc-fastest
-                                        src-zero-order-hold,src-linear,trivial)
-      --use-pid-file[=BOOL]             Create a PID file
-
-STARTUP SCRIPT:
-  -L, --load="MODULE ARGUMENTS"         Load the specified plugin module with
-                                        the specified argument
-  -F, --file=FILENAME                   Run the specified script
-  -C                                    Open a command line on the running TTY
-                                        after startup
-
-  -n                                    Don't load default script file
-</pre>
-
-<h3>Example</h3>
-
-<p>It is a good idea to run the daemon like this:</p>
-
-<pre>polypaudio -D</pre>
-
-<p>This will run <tt>/etc/polypaudio/default.pa</tt> after startup. This should be a script written in the CLI language described in <a href="cli.html">cli.html</a>. </p>
-
-<h2>Signals</h2>
-
-<p>The following signals are trapped specially:</p>
-
-<h3>SIGINT</h3>
-
-<p>The daemon is shut down cleanly.</p>
-
-<h3>SIGUSR1</h3>
-
-<p>The daemon tries to load the module <a href="modules.html#module-cli"><tt>module-cli</tt></a>, effectively providing a command line interface on the calling TTY.</p>
-
-<h3>SIGUSR2</h3>
-
-<p>The daemon tries to load the module <a href="modules.html#module-cli-protocol-unix"><tt>module-cli-protocol-unix</tt></a>, effectively providing a command line interface on a special UNIX domain socket.</p>
-
-<h3>SIGHUP</h3>
-
-<p>The daemon logs the current server layout.</p>
-
-<hr/>
-<address class="grey">Lennart Poettering &lt;@PACKAGE_BUGREPORT@&gt;, November 2004</address>
-<div class="grey"><i>$Id$</i></div>
-</body> </html>
diff --git a/doc/modules.html.in b/doc/modules.html.in
deleted file mode 100644 (file)
index a549396..0000000
+++ /dev/null
@@ -1,326 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?> <!-- -*-html-helper-*- -->
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title>polypaudio: Loadable Modules</title>
-<link rel="stylesheet" type="text/css" href="style.css" />
-</head>
-
-<body>
-
-<h1>Loadable Modules</h1>
-
-<p>The following loadable modules are provided with the <tt>polypaudio</tt> distribution:</p>
-
-<h2>Device Drivers</h2>
-
-<p>All device driver modules support the following parameters:</p>
-<table>
-  <tr><td><tt>format=</tt></td><td>The sample format (one of <tt>u8</tt>, <tt>s16</tt>, <tt>s16le</tt>, <tt>s16le</tt>, <tt>float32</tt>, <tt>float32be</tt>, <tt>float32le</tt>, <tt>alaw</tt>, <tt>ulaw</tt>) (defaults to <tt>s16</tt>)</td></tr>
-  <tr><td><tt>rate=</tt></td><td>The sample rate (defaults to 44100)</td></tr>
-  <tr><td><tt>channels=</tt></td><td>Audio channels (defaults to 2)</td></tr>
-  <tr><td><tt>sink_name=</tt>, <tt>source_name=</tt></td><td>Name for the sink (resp. source)</td></tr>
-</table>
-
-<h3>module-pipe-sink</h3>
-
-<p>Provides a simple test sink that writes the audio data to a FIFO
-special file in the file system. The sink name defaults to <tt>pipe_output</tt>.</p>
-
-<p>The following option is supported:</p>
-
-<table>
-  <tr><td><tt>file=</tt></td><td>The name of the FIFO special file to use. (defaults to: <tt>/tmp/music.output</tt>)</td></tr>
-</table>
-
-<h3>module-pipe-source</h3>
-
-<p>Provides a simple test source that reads the audio data from a FIFO
-special file in the file system. The source name defaults to <tt>pipe_input</tt>.</p>
-
-<p>The following option is supported:</p>
-
-<table>
-  <tr><td><tt>file=</tt></td><td>The name of the FIFO special file to use. (defaults to: <tt>/tmp/music.input</tt>)</td></tr>
-</table>
-
-
-<h3>module-null-sink</h3>
-
-<p>Provides a simple null sink. All data written to this sink is silently dropped. This sink is clocked using the system time.</p>
-
-<p>This module doesn't support any special parameters</p>
-
-<a name="module-alsa-sink"/>
-
-<h3>module-alsa-sink</h3>
-
-<p>Provides a playback sink for devices supported by the <a href="http://www.alsa-project.org/">Advanced Linux
-Sound Architecture</a> (ALSA). The sink name defaults to <tt>alsa_output</tt>.</p>
-
-<p>In addition to the general device driver options described above this module supports:</p>
-
-<table>
-  <tr><td><tt>device=</tt></td><td>The ALSA device to use. (defaults to "plughw:0,0")</td></tr>
-  <tr><td><tt>fragments=</tt></td><td>The desired fragments when opening the device. (defaults to 12)</td></tr>
-  <tr><td><tt>fragment_size=</tt></td><td>The desired fragment size in bytes when opening the device (defaults to 1024)</td></tr>
-</table>
-
-<h3>module-alsa-source</h3>
-
-<p>Provides a recording source for devices supported by the Advanced
-Linux Sound Architecture (ALSA). The source name defaults to <tt>alsa_input</tt>.</p>
-
-<p>This module supports <tt>device=</tt>, <tt>fragments=</tt> and <tt>fragment_size=</tt> arguments the same way as <a href="#module-alsa-sink"><tt>module-alsa-sink</tt></a>.</p>
-
-<a name="module-oss"/>
-
-<h3>module-oss</h3>
-
-<p>Provides both a sink and a source for playback, resp. recording on
-<a href="http://www.opensound.com">Open Sound System</a> (OSS) compatible devices.</p>
-
-<p>This module supports <tt>device=</tt> (which defaults to <tt>/dev/dsp</tt>), <tt>fragments=</tt> and <tt>fragment_size=</tt> arguments the same way as <a href="#module-alsa-sink"><tt>module-alsa-sink</tt></a>.</p>
-
-<p>In addition this module supports the following options:</p>
-
-<table>
-  <tr><td><tt>record=</tt></td><td>Accepts a binary numerical value for enabling (resp. disabling) the recording on this device. (defaults: to 1)</td></tr>
-  <tr><td><tt>playback=</tt></td><td>Accepts a binary numerical value for enabling (resp. disabling) the playback on this device. (defaults: to 1)</td></tr>
-</table>
-
-<p>The sink name (resp. source name) defaults to <tt>oss_output</tt> (resp. <tt>oss_input</tt>).</p>
-
-<h3>module-oss-mmap</h3>
-
-<p>Similar to <tt>module-oss</tt> but uses memory mapped
-(<tt>mmap()</tt>) access to the input/output buffers of the audio
-device. This provides better latency behaviour but is not as
-compatible as <tt>module-oss</tt>.</p>
-
-<p>This module accepts exactly the same arguments as <a href="#module-oss"><tt>module-oss</tt></a>.</p>
-
-<h3>module-combine</h3>
-
-<p>This combines two or more sinks into one. A new virtual sink is
-allocated. All data written to it is forwarded to all connected
-sinks. In aequidistant intervals the sample rates of the output sinks
-is recalculated: i.e. even when the sink's crystals deviate (which is
-normally the case) output appears synchronously to the human ear. The
-resampling required for this may be very CPU intensive.</p>
-
-<table>
-  <tr><td><tt>sink_name=</tt></td><td>The name for the combined sink. (defaults to  <tt>combined</tt>)</td></tr>
-  <tr><td><tt>master=</tt></td><td>The name of the first sink to link into the combined think. The sample rate/type is taken from this sink.</td></tr>
-  <tr><td><tt>slaves=</tt></td><td>Name of additional sinks to link into the combined think, seperated by commas.</td></tr>
-  <tr><td><tt>adjust_time=</tt></td><td>Time in seconds when to readjust the sample rate of all sinks. (defaults to 20)</td></tr>
-  <tr><td><tt>resample_method=</tt></td><td>Resampling algorithm to
-use. See <tt>libsamplerate</tt>'s documentation for more
-information. Use one of <tt>sinc-best-quality</tt>,
-<tt>sinc-medium-quality</tt>, <tt>sinc-fastest</tt>,
-<tt>zero-order-hold</tt>, <tt>linear</tt>. If the default happens to
-be to slow on your machine try using <tt>zero-order-hold</tt>. This
-will decrease output quality however. (defaults to
-<tt>sinc-fastest</tt>)</td></tr> </table>
-
-<h3>module-tunnel-{sink,source}</h3>
-
-<p>Tunnel a remote sink/source to a local "ghost"
-sink/source. Requires a running polypaudio daemon on the remote server
-with <tt>module-native-protocol-tcp</tt> loaded. It's probably a
-better idea to connect to the remote sink/source directly since some
-buffer control is lost through this tunneling.</p>
-
-<table>
-  <tr><td><tt>server=</tt></td><td>The server to connect to</td></tr>
-  <tr><td><tt>source=</tt></td><td>The source on the remote server. Only available for <tt>module-tunnel-source</tt>.</td></tr>
-  <tr><td><tt>sink=</tt></td><td>The sink on the remote server. Only available for <tt>module-tunnel-sink</tt>.</td></tr>
-  <tr><td><tt>cookie=</tt></td><td>The authentication cookie file to use.</td></tr>
-</table>
-
-<h3>module-esound-sink</h3>
-
-<p>Create a playback sink using an ESOUND server as backend. Whenever you can, try to omit this
-module since it has many disadvantages including bad latency
-and even worse latency measurement. </p>
-
-<table>
-  <tr><td><tt>server=</tt></td><td>The server to connect to</td></tr>
-  <tr><td><tt>cookie=</tt></td><td>The authentication cookie file to use.</td></tr>
-</table>
-
-<h2>Protocols</h2>
-
-<a name="module-cli"/>
-
-<h3>module-cli</h3>
-
-<p>Provides the user with a simple command line interface on the
-controlling TTY of the daemon. This module may not be loaded more than
-once.</p>
-
-<p>For an explanation of the simple command line language used by this
-module see <a href="cli.html"><tt>cli.html</tt></a>.
-
-<p>This module doesn't accept any arguments.</p>
-
-<a name="module-cli-protocol-unix"/>
-<a name="module-cli-protocol-tcp"/>
-<a name="module-cli-protocol"/>
-
-<h3>module-cli-protocol-{unix,tcp,tcp6}</h3>
-
-<p>An implemenation of a simple command line based protocol for
-controlling the <tt>polypaudio</tt> daemon. If loaded, the user may
-connect with tools like <tt>netcat</tt>, <tt>telnet</tt> or
-<a href="http://0pointer.de/lennart/projects/bidilink/"><tt>bidilink</tt></a> to the listening sockets and execute commands the
-same way as with <tt>module-cli</tt>.</p>
-
-<p><b>Beware!</b> Users are not authenticated when connecting to this
-service.</p>
-
-<p>This module exists in two versions: with the suffix <tt>-unix</tt>
-the service will listen on an UNIX domain socket in the local file
-system. With the suffix <tt>-tcp</tt> it will listen on a network
-transparent TCP/IP socket.</p>
-
-<p>This module supports the following options:</p>
-
-<table>
-  <tr><td><tt>port=</tt></td><td>(only for <tt>-tcp</tt>) The port number to listen on (defaults to 4712)</td></tr>
-  <tr><td><tt>loopback=</tt></td><td>(only for <tt>-tcp</tt>) Accepts
-a numerical binary value. If 1 the socket is bound to the loopback
-device, i.e. not publicly accessible. (defaults to 1)</td></tr>
-  <tr><td><tt>socket=</tt></td><td>(only for <tt>-unix</tt>) The UNIX socket name (defaults to <tt>/tmp/polypaudio/cli</tt>)</td></tr>
-</table>
-
-<h3>module-simple-protocol-{unix,tcp,tcp6}</h3>
-
-<p>An implementation of a simple protocol which allows playback by using
-simple tools like <tt>netcat</tt>. Just connect to the listening
-socket of this module and write the audio data to it, or read it from
-it for playback, resp. recording.</p>
-
-<p><b>Beware!</b> Users are not authenticated when connecting to this
-service.</p>
-
-<p>See <tt>module-cli-protocol-{unix,tcp}</tt> for more information
-about the two possible suffixes of this module.</p>
-
-<p>In addition to the options supported by <a href="module-cli-protocol"><tt>module-cli-protocol-*</tt></a>, this module supports:</p>
-
-<table>
-  <tr><td><tt>rate=</tt>, <tt>format=</tt>, <tt>channels=</tt></td><td>Sample format for streams connecting to this service.</td></tr>
-  <tr><td><tt>playback=</tt>, <tt>record=</tt></td><td>Enable/disable playback/recording</td></tr>
-  <tr><td><tt>sink=</tt>, <tt>source=</tt></td><td>Specify the sink/source this service connects to</td></tr>
-</table>
-
-<h3>module-esound-protocol-{unix,tcp}</h3>
-
-<p>An implemenation of a protocol compatible with the <a
-href="http://www.tux.org/~ricdude/EsounD.html">Enlightened Sound
-Daemon</a> (ESOUND, <tt>esd</tt>). When you load this module you may
-access the <tt>polypaudio</tt> daemon with tools like <tt>esdcat</tt>,
-<tt>esdrec</tt> or even <tt>esdctl</tt>. Many applications, such as
-XMMS, include support for this protocol.</p>
-
-<p>See <tt>module-cli-protocol-{unix,tcp}</tt> for more information
-about the two possible suffixes of this module.</p>
-
-<p>In addition to the options supported by <a href="module-cli-protocol"><tt>module-cli-protocol-*</tt></a>, this module supports:</p>
-
-<table>
-  <tr><td><tt>sink=</tt>, <tt>source=</tt></td><td>Specify the sink/source this service connects to</td></tr>
-  <tr><td><tt>public=</tt></td><td>If set to 0 not authentication is required to connect to the service</td></tr>
-  <tr><td><tt>cookie=</tt></td><td>Name of the cookie file for authentication purposes</td></tr>
-</table>
-
-<p>This implementation misses some features the original ESOUND has: e.g. there is no sample cache yet. However: XMMS works fine.</p>
-
-<h3>module-native-protocol-{unix,tcp,tcp6}</h3>
-
-<p>The native protocol of <tt>polypaudio</tt>.</p>
-
-<p>See <tt>module-cli-protocol-{unix,tcp}</tt> for more information
-about the two possible suffixes of this module.</p>
-
-<p>In addition to the options supported by <a href="module-cli-protocol"><tt>module-cli-protocol-*</tt></a>, this module supports:</p>
-
-<table>
-  <tr><td><tt>public=</tt></td><td>If set to 0 not authentication is required to connect to the service</td></tr>
-  <tr><td><tt>cookie=</tt></td><td>Name of the cookie file for authentication purposes</td></tr>
-</table>
-
-<h3>module-native-protocol-fd</h3>
-
-<p>This is used internally when auto spawning a new daemon. Don't use it directly.</p>
-
-<h2>Miscellaneous</h2>
-
-<h3>module-x11-bell</h3>
-
-<p>Intercepts X11 bell events and plays a sample from the sample cache on each occurence.</p>
-
-<table>
-  <tr><td><tt>display=</tt></td><td>X11 display to connect to. If ommited defaults to the value of <tt>$DISPLAY</tt></td></tr>
-  <tr><td><tt>sample=</tt></td><td>The sample to play. If ommited defaults to <tt>x11-bell</tt>.</td></tr>
-  <tr><td><tt>sink=</tt></td><td>Name of the sink to play the sample on. If ommited defaults to the default sink.</td></tr>
-</table>
-
-<h3>module-x11-publish</h3>
-
-<p>Publishes the access credentials to the Polypaudio server in the
-X11 root window. The following properties are used:
-<tt>POLYP_SERVER</tt>, <tt>POYLP_SINK</tt>, <tt>POLYP_SOURCE</tt>,
-<tt>POLYP_COOKIE</tt>. This is very useful when using SSH or any other
-remote login tool for logging into other machines and getting audio
-playback to your local speakers. The Polypaudio client libraries make
-use of this data automatically. Instead of using this module you may
-use the tool <tt>pax11publish</tt> which may be used to access, modify
-and import credential data from/to the X11 display.</p>
-
-<table>
-  <tr><td><tt>display=</tt></td><td>X11 display to connect to. If ommited defaults to the value of <tt>$DISPLAY</tt></td></tr>
-  <tr><td><tt>sink=</tt></td><td>Name of the default sink. If ommited this property isn't stored in the X11 display.</td></tr>
-  <tr><td><tt>source=</tt></td><td>Name of the default source. If ommited this property isn't stored in the X11 display.</td></tr>
-  <tr><td><tt>cookie=</tt></td><td>Name of the cookie file of the
-cookie to store in the X11 display. If ommited the cookie of an
-already loaded protocol module is used.</td></tr> </table>
-
-<h3>module-sine</h3>
-
-<p>Creates a sink input and generates a sine waveform stream.</p>
-
-<table>
-  <tr><td><tt>sink=</tt></td><td>The sink to connect to. If ommited defaults to the default sink.</td></tr>
-  <tr><td><tt>frequency=</tt></td><td>The frequency to generate in Hertz. Defaults to 440.</td></tr>
-</table>
-
-<h3>module-esound-compat-spawnfd</h3>
-
-<p>This is a compatibility module for <tt>libesd</tt> based autospawning of polypaudio. Don't use it directly.</p>
-
-<h3>module-esound-compat-spawnpid</h3>
-
-<p>This is a compatibility module for <tt>libesd</tt> based autospawning of polypaudio. Don't use it directly.</p>
-
-<h3>module-match</h3>
-
-<p>Adjust the volume of a playback stream automatically based on its name.</p>
-
-<table>
-  <tr><td><tt>table=</tt></td><td>The regular expression matching table file to use</td></tr>
-</table>
-
-<p>The table file should contain a regexp and volume on each line, seperated by spaces. An example:</p>
-
-<pre>
-^sample: 25
-</pre>
-
-<p>The volumes of all streams with titles starting with <tt>sample:</tt> are automatically set to 25. (FYI: All sample cache streams start with <tt>sample:</tt>)</p>
-
-<hr/>
-<address class="grey">Lennart Poettering &lt;@PACKAGE_BUGREPORT@&gt;, November 2004</address>
-<div class="grey"><i>$Id$</i></div>
-</body> </html>
diff --git a/doc/style.css b/doc/style.css
deleted file mode 100644 (file)
index a46592a..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/* $Id$ */
-
-/***
- * This file is part of polypaudio.
- *
- * polypaudio is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * polypaudio is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with polypaudio; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- ***/
-
-body { color: black; background-color: white; margin: 0.5cm; } 
-a:link, a:visited { color: #900000; }       
-p { margin-left: 0.5cm; margin-right: 0.5cm; }
-div.news-date { margin-left: 0.5cm; font-size: 80%; color: #4f0000; } 
-p.news-text { margin-left: 1cm; }
-h1 { color: #00009F; }
-h2 { color: #00009F; }
-h3 { color: #00004F; margin-left: 0.5cm; }
-ul { margin-left: .5cm; }
-ol { margin-left: .5cm; }
-pre { margin-left: .5cm; background-color: #f0f0f0; padding: 0.4cm;}
-.grey { color: #afafaf; }
-table {  margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }
-td { padding-left:10px; padding-right:10px;  }
diff --git a/doc/todo b/doc/todo
deleted file mode 100644 (file)
index 0da8320..0000000
--- a/doc/todo
+++ /dev/null
@@ -1,33 +0,0 @@
-*** $Id$ ***
-
-Architectural changes:
-- per-channel volume
-- channel mapping ("left", "right", "rear", "subwoofer")
-- hardware volume support
-- add API for synchronizing multiple sinks/sources to a common clock
-- absolutely indexed write()s from client
-- remove "polyplib-" prefix
-
-Fixes:
-- improve module-oss-mmap latency measurement
-- module-tunnel: improve latency calculation
-- make alsa modules use mmap
-- even more commenting
-
-Features:
-- add radio module
-- xmlrpc
-- dbus/hal
-- rendezvous autotunnel module
-- polish for starting polypaudio as root/system-wide instance
-- export connection fd
-
-Long term:
-- pass meta info for hearing impaired
-- X11: support for the X11 synchronization extension
-
-Backends for:
-- portaudio  (semi-done)
-- alsa-lib
-- sdl
-- OSS (esddsp style)
diff --git a/doxygen/.gitignore b/doxygen/.gitignore
new file mode 100644 (file)
index 0000000..ca9b444
--- /dev/null
@@ -0,0 +1 @@
+doxygen.conf
index 79354b21719a19c1cf9f6e26f29c2974fb992747..60b0060515728c022cc68d893a5fff3af5a7fffe 100644 (file)
@@ -1,19 +1,17 @@
-# $Id$
+# This file is part of PulseAudio.
 #
-# This file is part of polypaudio.
-#
-# polypaudio is free software; you can redistribute it and/or modify
+# PulseAudio is free software; you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation; either version 2 of the License, or
 # (at your option) any later version.
 #
-# polypaudio is distributed in the hope that it will be useful, but
+# PulseAudio is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software
+# along with PulseAudio; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 # USA.
 
@@ -21,6 +19,6 @@ doxygen: doxygen.conf
        doxygen $<
 
 clean-local:
-       rm -rf html
+       -rm -rf html
 
 .PHONY: all doxygen
index c0e122ea83d8b10205fd074e768446402fa5e045..7ad5d2f31c9efcc68c1049c8493520784f212d47 100644 (file)
@@ -17,7 +17,7 @@
 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
 # by quotes) that should identify the project.
 
-PROJECT_NAME           = @PACKAGE_NAME@
+PROJECT_NAME           = PulseAudio
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number. 
 # This could be handy for archiving the generated documentation or 
@@ -170,7 +170,7 @@ DISTRIBUTE_GROUP_DOC   = NO
 # The TAB_SIZE tag can be used to set the number of spaces in a tab. 
 # Doxygen uses this value to replace tabs by spaces in code fragments.
 
-TAB_SIZE               = 8
+TAB_SIZE               = 4
 
 # This tag can be used to specify a number of aliases that acts 
 # as commands in the documentation. An alias has the form "name=value". 
@@ -417,7 +417,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories 
 # with spaces.
 
-INPUT                  = ../polyp/polyplib-context.h ../polyp/polyplib-stream.h ../polyp/polyplib.h ../polyp/sample.h ../polyp/polyplib-def.h ../polyp/polyplib-subscribe.h ../polyp/polyplib-introspect.h ../polyp/polyplib-scache.h ../polyp/mainloop-api.h ../polyp/cdecl.h ../polyp/glib-mainloop.h ../polyp/mainloop.h ../polyp/mainloop-signal.h ../polyp/polyplib-error.h ../polyp/polyplib-operation.h ../polyp/polyplib-simple.h ../polyp/polyplib-version.h
+INPUT                  = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/proplist.h ../src/pulse/gccmacro.h
 
 # If the value of the INPUT tag contains directories, you can use the 
 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
@@ -455,7 +455,7 @@ EXCLUDE_PATTERNS       =
 # directories that contain example code fragments that are included (see 
 # the \include command).
 
-EXAMPLE_PATH           = ../polyp/
+EXAMPLE_PATH           = ../src/utils ../src/tests
 
 # If the value of the EXAMPLE_PATH tag contains directories, you can use the 
 # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
@@ -550,7 +550,7 @@ VERBATIM_HEADERS       = YES
 # of all compounds will be generated. Enable this if the project 
 # contains a lot of classes, structs, unions or interfaces.
 
-ALPHABETICAL_INDEX     = NO
+ALPHABETICAL_INDEX     = YES
 
 # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
 # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
@@ -563,7 +563,7 @@ COLS_IN_ALPHA_INDEX    = 5
 # The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
 # should be ignored while generating the index headers.
 
-IGNORE_PREFIX          = 
+IGNORE_PREFIX          = pa_ PA_
 
 #---------------------------------------------------------------------------
 # configuration options related to the HTML output
@@ -660,7 +660,7 @@ DISABLE_INDEX          = NO
 # This tag can be used to set the number of enum values (range [1..20]) 
 # that doxygen will group on one line in the generated HTML documentation.
 
-ENUM_VALUES_PER_LINE   = 4
+ENUM_VALUES_PER_LINE   = 1
 
 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
 # generated containing a tree-like index structure (just like the one that 
@@ -950,14 +950,14 @@ INCLUDE_FILE_PATTERNS  =
 # or name=definition (no spaces). If the definition and the = are 
 # omitted =1 is assumed.
 
-PREDEFINED             = PA_C_DECL_BEGIN=,PA_C_DECL_END
+PREDEFINED             = PA_C_DECL_BEGIN= PA_C_DECL_END=
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
 # this tag can be used to specify a list of macro names that should be expanded. 
 # The macro definition that is found in the sources will be used. 
 # Use the PREDEFINED tag if you want to use a different macro definition.
 
-EXPAND_AS_DEFINED      = PA_C_DECL_BEGIN, PA_C_DECL_END
+#EXPAND_AS_DEFINED      = PA_C_DECL_BEGIN, PA_C_DECL_END
 
 # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
 # doxygen's preprocessor will remove all function-like macros that are alone 
diff --git a/libpulse-browse.pc.in b/libpulse-browse.pc.in
new file mode 100644 (file)
index 0000000..66438b2
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=${prefix}
+libdir=@libdir@
+includedir=${prefix}/include
+
+Name: libpulse-browse
+Description: PulseAudio Network Browsing API
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lpulse-browse @PTHREAD_LIBS@
+Cflags: -D_REENTRANT -I${includedir}
+Requires: libpulse
diff --git a/libpulse-mainloop-glib.pc.in b/libpulse-mainloop-glib.pc.in
new file mode 100644 (file)
index 0000000..1a8bfa4
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=${prefix}
+libdir=@libdir@
+includedir=${prefix}/include
+
+Name: libpulse-mainloop-glib
+Description: GLIB 2.0 Main Loop Wrapper for PulseAudio
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lpulse-mainloop-glib @PTHREAD_LIBS@
+Cflags: -D_REENTRANT -I${includedir}
+Requires: libpulse glib-2.0
diff --git a/libpulse-simple.pc.in b/libpulse-simple.pc.in
new file mode 100644 (file)
index 0000000..00d1245
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=${prefix}
+libdir=@libdir@
+includedir=${prefix}/include
+
+Name: libpulse-simple
+Description: Simplified Synchronous Client Interface to PulseAudio
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lpulse-simple @PTHREAD_LIBS@
+Cflags: -D_REENTRANT -I${includedir}
+Requires: libpulse
diff --git a/libpulse.pc.in b/libpulse.pc.in
new file mode 100644 (file)
index 0000000..2193b2b
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=${prefix}
+libdir=@libdir@
+includedir=${prefix}/include
+
+Name: libpulse
+Description: Client Interface to PulseAudio
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lpulse @PTHREAD_LIBS@
+Cflags: -D_REENTRANT -I${includedir}
diff --git a/libtool.m4 b/libtool.m4
deleted file mode 100644 (file)
index 4f5ac79..0000000
+++ /dev/null
@@ -1,5951 +0,0 @@
-# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
-## Copyright 1996, 1997, 1998, 1999, 2000, 2001
-## Free Software Foundation, Inc.
-## Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
-##
-## This program 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.
-##
-## This program is distributed in the hope that it will be useful, but
-## WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY 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 this program; if not, write to the Free Software
-## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-##
-## As a special exception to the GNU General Public License, if you
-## distribute this file as part of a program that contains a
-## configuration script generated by Autoconf, you may include it under
-## the same distribution terms that you use for the rest of that program.
-
-# serial 47 AC_PROG_LIBTOOL
-# Debian $Rev: 214 $
-
-
-# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED)
-# -----------------------------------------------------------
-# If this macro is not defined by Autoconf, define it here.
-m4_ifdef([AC_PROVIDE_IFELSE],
-         [],
-         [m4_define([AC_PROVIDE_IFELSE],
-                [m4_ifdef([AC_PROVIDE_$1],
-                          [$2], [$3])])])
-
-
-# AC_PROG_LIBTOOL
-# ---------------
-AC_DEFUN([AC_PROG_LIBTOOL],
-[AC_REQUIRE([_AC_PROG_LIBTOOL])dnl
-dnl If AC_PROG_CXX has already been expanded, run AC_LIBTOOL_CXX
-dnl immediately, otherwise, hook it in at the end of AC_PROG_CXX.
-  AC_PROVIDE_IFELSE([AC_PROG_CXX],
-    [AC_LIBTOOL_CXX],
-    [define([AC_PROG_CXX], defn([AC_PROG_CXX])[AC_LIBTOOL_CXX
-  ])])
-dnl And a similar setup for Fortran 77 support
-  AC_PROVIDE_IFELSE([AC_PROG_F77],
-    [AC_LIBTOOL_F77],
-    [define([AC_PROG_F77], defn([AC_PROG_F77])[AC_LIBTOOL_F77
-])])
-
-dnl Quote A][M_PROG_GCJ so that aclocal doesn't bring it in needlessly.
-dnl If either AC_PROG_GCJ or A][M_PROG_GCJ have already been expanded, run
-dnl AC_LIBTOOL_GCJ immediately, otherwise, hook it in at the end of both.
-  AC_PROVIDE_IFELSE([AC_PROG_GCJ],
-    [AC_LIBTOOL_GCJ],
-    [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
-      [AC_LIBTOOL_GCJ],
-      [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ],
-       [AC_LIBTOOL_GCJ],
-      [ifdef([AC_PROG_GCJ],
-            [define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[AC_LIBTOOL_GCJ])])
-       ifdef([A][M_PROG_GCJ],
-            [define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[AC_LIBTOOL_GCJ])])
-       ifdef([LT_AC_PROG_GCJ],
-            [define([LT_AC_PROG_GCJ],
-               defn([LT_AC_PROG_GCJ])[AC_LIBTOOL_GCJ])])])])
-])])# AC_PROG_LIBTOOL
-
-
-# _AC_PROG_LIBTOOL
-# ----------------
-AC_DEFUN([_AC_PROG_LIBTOOL],
-[AC_REQUIRE([AC_LIBTOOL_SETUP])dnl
-AC_BEFORE([$0],[AC_LIBTOOL_CXX])dnl
-AC_BEFORE([$0],[AC_LIBTOOL_F77])dnl
-AC_BEFORE([$0],[AC_LIBTOOL_GCJ])dnl
-
-# This can be used to rebuild libtool when needed
-LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh"
-
-# Always use our own libtool.
-LIBTOOL='$(SHELL) $(top_builddir)/libtool'
-AC_SUBST(LIBTOOL)dnl
-
-# Prevent multiple expansion
-define([AC_PROG_LIBTOOL], [])
-])# _AC_PROG_LIBTOOL
-
-
-# AC_LIBTOOL_SETUP
-# ----------------
-AC_DEFUN([AC_LIBTOOL_SETUP],
-[AC_PREREQ(2.50)dnl
-AC_REQUIRE([AC_ENABLE_SHARED])dnl
-AC_REQUIRE([AC_ENABLE_STATIC])dnl
-AC_REQUIRE([AC_ENABLE_FAST_INSTALL])dnl
-AC_REQUIRE([AC_CANONICAL_HOST])dnl
-AC_REQUIRE([AC_CANONICAL_BUILD])dnl
-AC_REQUIRE([AC_PROG_CC])dnl
-AC_REQUIRE([AC_PROG_LD])dnl
-AC_REQUIRE([AC_PROG_LD_RELOAD_FLAG])dnl
-AC_REQUIRE([AC_PROG_NM])dnl
-
-AC_REQUIRE([AC_PROG_LN_S])dnl
-AC_REQUIRE([AC_DEPLIBS_CHECK_METHOD])dnl
-# Autoconf 2.13's AC_OBJEXT and AC_EXEEXT macros only works for C compilers!
-AC_REQUIRE([AC_OBJEXT])dnl
-AC_REQUIRE([AC_EXEEXT])dnl
-dnl
-
-AC_LIBTOOL_SYS_MAX_CMD_LEN
-AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE
-AC_LIBTOOL_OBJDIR
-
-AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl
-_LT_AC_PROG_ECHO_BACKSLASH
-
-case $host_os in
-aix3*)
-  # AIX sometimes has problems with the GCC collect2 program.  For some
-  # reason, if we set the COLLECT_NAMES environment variable, the problems
-  # vanish in a puff of smoke.
-  if test "X${COLLECT_NAMES+set}" != Xset; then
-    COLLECT_NAMES=
-    export COLLECT_NAMES
-  fi
-  ;;
-esac
-
-# Sed substitution that helps us do robust quoting.  It backslashifies
-# metacharacters that are still active within double-quoted strings.
-Xsed='sed -e s/^X//'
-[sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g']
-
-# Same as above, but do not quote variable references.
-[double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g']
-
-# Sed substitution to delay expansion of an escaped shell variable in a
-# double_quote_subst'ed string.
-delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
-
-# Sed substitution to avoid accidental globbing in evaled expressions
-no_glob_subst='s/\*/\\\*/g'
-
-# Constants:
-rm="rm -f"
-
-# Global variables:
-default_ofile=libtool
-can_build_shared=yes
-
-# All known linkers require a `.a' archive for static linking (except M$VC,
-# which needs '.lib').
-libext=a
-ltmain="$ac_aux_dir/ltmain.sh"
-ofile="$default_ofile"
-with_gnu_ld="$lt_cv_prog_gnu_ld"
-
-AC_CHECK_TOOL(AR, ar, false)
-AC_CHECK_TOOL(RANLIB, ranlib, :)
-AC_CHECK_TOOL(STRIP, strip, :)
-
-old_CC="$CC"
-old_CFLAGS="$CFLAGS"
-
-# Set sane defaults for various variables
-test -z "$AR" && AR=ar
-test -z "$AR_FLAGS" && AR_FLAGS=cru
-test -z "$AS" && AS=as
-test -z "$CC" && CC=cc
-test -z "$LTCC" && LTCC=$CC
-test -z "$DLLTOOL" && DLLTOOL=dlltool
-test -z "$LD" && LD=ld
-test -z "$LN_S" && LN_S="ln -s"
-test -z "$MAGIC_CMD" && MAGIC_CMD=file
-test -z "$NM" && NM=nm
-test -z "$SED" && SED=sed
-test -z "$OBJDUMP" && OBJDUMP=objdump
-test -z "$RANLIB" && RANLIB=:
-test -z "$STRIP" && STRIP=:
-test -z "$ac_objext" && ac_objext=o
-
-# Determine commands to create old-style static archives.
-old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs'
-old_postinstall_cmds='chmod 644 $oldlib'
-old_postuninstall_cmds=
-
-if test -n "$RANLIB"; then
-  case $host_os in
-  openbsd*)
-    old_postinstall_cmds="\$RANLIB -t \$oldlib~$old_postinstall_cmds"
-    ;;
-  *)
-    old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds"
-    ;;
-  esac
-  old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
-fi
-
-# Only perform the check for file, if the check method requires it
-case $deplibs_check_method in
-file_magic*)
-  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
-    AC_PATH_MAGIC
-  fi
-  ;;
-esac
-
-AC_PROVIDE_IFELSE([AC_LIBTOOL_DLOPEN], enable_dlopen=yes, enable_dlopen=no)
-AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL],
-enable_win32_dll=yes, enable_win32_dll=no)
-
-AC_ARG_ENABLE([libtool-lock],
-    [AC_HELP_STRING([--disable-libtool-lock],
-       [avoid locking (might break parallel builds)])])
-test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
-
-AC_ARG_WITH([pic],
-    [AC_HELP_STRING([--with-pic],
-       [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
-    [pic_mode="$withval"],
-    [pic_mode=default])
-test -z "$pic_mode" && pic_mode=default
-
-# Use C for the default configuration in the libtool script
-tagname=
-AC_LIBTOOL_LANG_C_CONFIG
-_LT_AC_TAGCONFIG
-])# AC_LIBTOOL_SETUP
-
-
-# _LT_AC_SYS_COMPILER
-# -------------------
-AC_DEFUN([_LT_AC_SYS_COMPILER],
-[AC_REQUIRE([AC_PROG_CC])dnl
-
-# If no C compiler was specified, use CC.
-LTCC=${LTCC-"$CC"}
-
-# Allow CC to be a program name with arguments.
-compiler=$CC
-])# _LT_AC_SYS_COMPILER
-
-
-# _LT_AC_SYS_LIBPATH_AIX
-# ----------------------
-# Links a minimal program and checks the executable
-# for the system default hardcoded library path. In most cases,
-# this is /usr/lib:/lib, but when the MPI compilers are used
-# the location of the communication and MPI libs are included too.
-# If we don't find anything, use the default library path according
-# to the aix ld manual.
-AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX],
-[AC_LINK_IFELSE(AC_LANG_PROGRAM,[
-aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
-}'`
-# Check for a 64-bit object if we didn't find anything.
-if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
-}'`; fi],[])
-if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
-])# _LT_AC_SYS_LIBPATH_AIX
-
-
-# _LT_AC_SHELL_INIT(ARG)
-# ----------------------
-AC_DEFUN([_LT_AC_SHELL_INIT],
-[ifdef([AC_DIVERSION_NOTICE],
-            [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
-        [AC_DIVERT_PUSH(NOTICE)])
-$1
-AC_DIVERT_POP
-])# _LT_AC_SHELL_INIT
-
-
-# _LT_AC_PROG_ECHO_BACKSLASH
-# --------------------------
-# Add some code to the start of the generated configure script which
-# will find an echo command which doesn't interpret backslashes.
-AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH],
-[_LT_AC_SHELL_INIT([
-# Check that we are running under the correct shell.
-SHELL=${CONFIG_SHELL-/bin/sh}
-
-case X$ECHO in
-X*--fallback-echo)
-  # Remove one level of quotation (which was required for Make).
-  ECHO=`echo "$ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','`
-  ;;
-esac
-
-echo=${ECHO-echo}
-if test "X[$]1" = X--no-reexec; then
-  # Discard the --no-reexec flag, and continue.
-  shift
-elif test "X[$]1" = X--fallback-echo; then
-  # Avoid inline document here, it may be left over
-  :
-elif test "X`($echo '\t') 2>/dev/null`" = 'X\t' ; then
-  # Yippee, $echo works!
-  :
-else
-  # Restart under the correct shell.
-  exec $SHELL "[$]0" --no-reexec ${1+"[$]@"}
-fi
-
-if test "X[$]1" = X--fallback-echo; then
-  # used as fallback echo
-  shift
-  cat <<EOF
-[$]*
-EOF
-  exit 0
-fi
-
-# The HP-UX ksh and POSIX shell print the target directory to stdout
-# if CDPATH is set.
-if test "X${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi
-
-if test -z "$ECHO"; then
-if test "X${echo_test_string+set}" != Xset; then
-# find a string as large as possible, as long as the shell can cope with it
-  for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do
-    # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
-    if (echo_test_string="`eval $cmd`") 2>/dev/null &&
-       echo_test_string="`eval $cmd`" &&
-       (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null
-    then
-      break
-    fi
-  done
-fi
-
-if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
-   echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` &&
-   test "X$echo_testing_string" = "X$echo_test_string"; then
-  :
-else
-  # The Solaris, AIX, and Digital Unix default echo programs unquote
-  # backslashes.  This makes it impossible to quote backslashes using
-  #   echo "$something" | sed 's/\\/\\\\/g'
-  #
-  # So, first we look for a working echo in the user's PATH.
-
-  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
-  for dir in $PATH /usr/ucb; do
-    IFS="$lt_save_ifs"
-    if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
-       test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
-       echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
-       test "X$echo_testing_string" = "X$echo_test_string"; then
-      echo="$dir/echo"
-      break
-    fi
-  done
-  IFS="$lt_save_ifs"
-
-  if test "X$echo" = Xecho; then
-    # We didn't find a better echo, so look for alternatives.
-    if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' &&
-       echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` &&
-       test "X$echo_testing_string" = "X$echo_test_string"; then
-      # This shell has a builtin print -r that does the trick.
-      echo='print -r'
-    elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) &&
-        test "X$CONFIG_SHELL" != X/bin/ksh; then
-      # If we have ksh, try running configure again with it.
-      ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
-      export ORIGINAL_CONFIG_SHELL
-      CONFIG_SHELL=/bin/ksh
-      export CONFIG_SHELL
-      exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"}
-    else
-      # Try using printf.
-      echo='printf %s\n'
-      if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
-        echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` &&
-        test "X$echo_testing_string" = "X$echo_test_string"; then
-       # Cool, printf works
-       :
-      elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
-          test "X$echo_testing_string" = 'X\t' &&
-          echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
-          test "X$echo_testing_string" = "X$echo_test_string"; then
-       CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
-       export CONFIG_SHELL
-       SHELL="$CONFIG_SHELL"
-       export SHELL
-       echo="$CONFIG_SHELL [$]0 --fallback-echo"
-      elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
-          test "X$echo_testing_string" = 'X\t' &&
-          echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
-          test "X$echo_testing_string" = "X$echo_test_string"; then
-       echo="$CONFIG_SHELL [$]0 --fallback-echo"
-      else
-       # maybe with a smaller string...
-       prev=:
-
-       for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do
-         if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null
-         then
-           break
-         fi
-         prev="$cmd"
-       done
-
-       if test "$prev" != 'sed 50q "[$]0"'; then
-         echo_test_string=`eval $prev`
-         export echo_test_string
-         exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"}
-       else
-         # Oops.  We lost completely, so just stick with echo.
-         echo=echo
-       fi
-      fi
-    fi
-  fi
-fi
-fi
-
-# Copy echo and quote the copy suitably for passing to libtool from
-# the Makefile, instead of quoting the original, which is used later.
-ECHO=$echo
-if test "X$ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then
-   ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo"
-fi
-
-AC_SUBST(ECHO)
-])])# _LT_AC_PROG_ECHO_BACKSLASH
-
-
-# _LT_AC_LOCK
-# -----------
-AC_DEFUN([_LT_AC_LOCK],
-[AC_ARG_ENABLE([libtool-lock],
-    [AC_HELP_STRING([--disable-libtool-lock],
-       [avoid locking (might break parallel builds)])])
-test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
-
-# Some flags need to be propagated to the compiler or linker for good
-# libtool support.
-case $host in
-ia64-*-hpux*)
-  # Find out which ABI we are using.
-  echo 'int i;' > conftest.$ac_ext
-  if AC_TRY_EVAL(ac_compile); then
-    case `/usr/bin/file conftest.$ac_objext` in
-    *ELF-32*)
-      HPUX_IA64_MODE="32"
-      ;;
-    *ELF-64*)
-      HPUX_IA64_MODE="64"
-      ;;
-    esac
-  fi
-  rm -rf conftest*
-  ;;
-*-*-irix6*)
-  # Find out which ABI we are using.
-  echo '[#]line __oline__ "configure"' > conftest.$ac_ext
-  if AC_TRY_EVAL(ac_compile); then
-   if test "$lt_cv_prog_gnu_ld" = yes; then
-    case `/usr/bin/file conftest.$ac_objext` in
-    *32-bit*)
-      LD="${LD-ld} -melf32bsmip"
-      ;;
-    *N32*)
-      LD="${LD-ld} -melf32bmipn32"
-      ;;
-    *64-bit*)
-      LD="${LD-ld} -melf64bmip"
-      ;;
-    esac
-   else
-    case `/usr/bin/file conftest.$ac_objext` in
-    *32-bit*)
-      LD="${LD-ld} -32"
-      ;;
-    *N32*)
-      LD="${LD-ld} -n32"
-      ;;
-    *64-bit*)
-      LD="${LD-ld} -64"
-      ;;
-    esac
-   fi
-  fi
-  rm -rf conftest*
-  ;;
-
-x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*)
-  # Find out which ABI we are using.
-  echo 'int i;' > conftest.$ac_ext
-  if AC_TRY_EVAL(ac_compile); then
-    case "`/usr/bin/file conftest.o`" in
-    *32-bit*)
-      case $host in
-        x86_64-*linux*)
-          LD="${LD-ld} -m elf_i386"
-          ;;
-        ppc64-*linux*|powerpc64-*linux*)
-          LD="${LD-ld} -m elf32ppclinux"
-          ;;
-        s390x-*linux*)
-          LD="${LD-ld} -m elf_s390"
-          ;;
-        sparc64-*linux*)
-          LD="${LD-ld} -m elf32_sparc"
-          ;;
-      esac
-      ;;
-    *64-bit*)
-      case $host in
-        x86_64-*linux*)
-          LD="${LD-ld} -m elf_x86_64"
-          ;;
-        ppc*-*linux*|powerpc*-*linux*)
-          LD="${LD-ld} -m elf64ppc"
-          ;;
-        s390*-*linux*)
-          LD="${LD-ld} -m elf64_s390"
-          ;;
-        sparc*-*linux*)
-          LD="${LD-ld} -m elf64_sparc"
-          ;;
-      esac
-      ;;
-    esac
-  fi
-  rm -rf conftest*
-  ;;
-
-*-*-sco3.2v5*)
-  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
-  SAVE_CFLAGS="$CFLAGS"
-  CFLAGS="$CFLAGS -belf"
-  AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
-    [AC_LANG_PUSH(C)
-     AC_TRY_LINK([],[],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
-     AC_LANG_POP])
-  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
-    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
-    CFLAGS="$SAVE_CFLAGS"
-  fi
-  ;;
-AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL],
-[*-*-cygwin* | *-*-mingw* | *-*-pw32*)
-  AC_CHECK_TOOL(DLLTOOL, dlltool, false)
-  AC_CHECK_TOOL(AS, as, false)
-  AC_CHECK_TOOL(OBJDUMP, objdump, false)
-  ;;
-  ])
-esac
-
-need_locks="$enable_libtool_lock"
-
-])# _LT_AC_LOCK
-
-
-# AC_LIBTOOL_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
-#              [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
-# ----------------------------------------------------------------
-# Check whether the given compiler option works
-AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION],
-[AC_REQUIRE([LT_AC_PROG_SED])
-AC_CACHE_CHECK([$1], [$2],
-  [$2=no
-  ifelse([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
-   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
-   lt_compiler_flag="$3"
-   # Insert the option either (1) after the last *FLAGS variable, or
-   # (2) before a word containing "conftest.", or (3) at the end.
-   # Note that $ac_compile itself does not contain backslashes and begins
-   # with a dollar sign (not a hyphen), so the echo should work correctly.
-   # The option is referenced via a variable to avoid confusing sed.
-   lt_compile=`echo "$ac_compile" | $SED \
-   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
-   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
-   -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
-   (eval "$lt_compile" 2>conftest.err)
-   ac_status=$?
-   cat conftest.err >&AS_MESSAGE_LOG_FD
-   echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
-   if (exit $ac_status) && test -s "$ac_outfile"; then
-     # The compiler can only warn and ignore the option if not recognized
-     # So say no if there are warnings
-     if test ! -s conftest.err; then
-       $2=yes
-     fi
-   fi
-   $rm conftest*
-])
-
-if test x"[$]$2" = xyes; then
-    ifelse([$5], , :, [$5])
-else
-    ifelse([$6], , :, [$6])
-fi
-])# AC_LIBTOOL_COMPILER_OPTION
-
-
-# AC_LIBTOOL_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
-#                          [ACTION-SUCCESS], [ACTION-FAILURE])
-# ------------------------------------------------------------
-# Check whether the given compiler option works
-AC_DEFUN([AC_LIBTOOL_LINKER_OPTION],
-[AC_CACHE_CHECK([$1], [$2],
-  [$2=no
-   save_LDFLAGS="$LDFLAGS"
-   LDFLAGS="$LDFLAGS $3"
-   printf "$lt_simple_link_test_code" > conftest.$ac_ext
-   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
-     # The compiler can only warn and ignore the option if not recognized
-     # So say no if there are warnings
-     if test -s conftest.err; then
-       # Append any errors to the config.log.
-       cat conftest.err 1>&AS_MESSAGE_LOG_FD
-     else
-       $2=yes
-     fi
-   fi
-   $rm conftest*
-   LDFLAGS="$save_LDFLAGS"
-])
-
-if test x"[$]$2" = xyes; then
-    ifelse([$4], , :, [$4])
-else
-    ifelse([$5], , :, [$5])
-fi
-])# AC_LIBTOOL_LINKER_OPTION
-
-
-# AC_LIBTOOL_SYS_MAX_CMD_LEN
-# --------------------------
-AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN],
-[# find the maximum length of command line arguments
-AC_MSG_CHECKING([the maximum length of command line arguments])
-AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
-  i=0
-  teststring="ABCD"
-
-  case $build_os in
-  msdosdjgpp*)
-    # On DJGPP, this test can blow up pretty badly due to problems in libc
-    # (any single argument exceeding 2000 bytes causes a buffer overrun
-    # during glob expansion).  Even if it were fixed, the result of this
-    # check would be larger than it should be.
-    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
-    ;;
-
-  gnu*)
-    # Under GNU Hurd, this test is not required because there is
-    # no limit to the length of command line arguments.
-    # Libtool will interpret -1 as no limit whatsoever
-    lt_cv_sys_max_cmd_len=-1;
-    ;;
-
-  cygwin* | mingw*)
-    # On Win9x/ME, this test blows up -- it succeeds, but takes
-    # about 5 minutes as the teststring grows exponentially.
-    # Worse, since 9x/ME are not pre-emptively multitasking,
-    # you end up with a "frozen" computer, even though with patience
-    # the test eventually succeeds (with a max line length of 256k).
-    # Instead, let's just punt: use the minimum linelength reported by
-    # all of the supported platforms: 8192 (on NT/2K/XP).
-    lt_cv_sys_max_cmd_len=8192;
-    ;;
-
-  amigaos*)
-    # On AmigaOS with pdksh, this test takes hours, literally.
-    # So we just punt and use a minimum line length of 8192.
-    lt_cv_sys_max_cmd_len=8192;
-    ;;
-
- *)
-    # If test is not a shell built-in, we'll probably end up computing a
-    # maximum length that is only half of the actual maximum length, but
-    # we can't tell.
-    while (test "X"`$CONFIG_SHELL [$]0 --fallback-echo "X$teststring" 2>/dev/null` \
-              = "XX$teststring") >/dev/null 2>&1 &&
-           new_result=`expr "X$teststring" : ".*" 2>&1` &&
-           lt_cv_sys_max_cmd_len=$new_result &&
-           test $i != 17 # 1/2 MB should be enough
-    do
-      i=`expr $i + 1`
-      teststring=$teststring$teststring
-    done
-    teststring=
-    # Add a significant safety factor because C++ compilers can tack on massive
-    # amounts of additional arguments before passing them to the linker.
-    # It appears as though 1/2 is a usable value.
-    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
-    ;;
-  esac
-])
-if test -n $lt_cv_sys_max_cmd_len ; then
-  AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
-else
-  AC_MSG_RESULT(none)
-fi
-])# AC_LIBTOOL_SYS_MAX_CMD_LEN
-
-
-# _LT_AC_CHECK_DLFCN
-# --------------------
-AC_DEFUN([_LT_AC_CHECK_DLFCN],
-[AC_CHECK_HEADERS(dlfcn.h)dnl
-])# _LT_AC_CHECK_DLFCN
-
-
-# _LT_AC_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
-#                           ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
-# ------------------------------------------------------------------
-AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF],
-[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl
-if test "$cross_compiling" = yes; then :
-  [$4]
-else
-  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
-  lt_status=$lt_dlunknown
-  cat > conftest.$ac_ext <<EOF
-[#line __oline__ "configure"
-#include "confdefs.h"
-
-#if HAVE_DLFCN_H
-#include <dlfcn.h>
-#endif
-
-#include <stdio.h>
-
-#ifdef RTLD_GLOBAL
-#  define LT_DLGLOBAL          RTLD_GLOBAL
-#else
-#  ifdef DL_GLOBAL
-#    define LT_DLGLOBAL                DL_GLOBAL
-#  else
-#    define LT_DLGLOBAL                0
-#  endif
-#endif
-
-/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
-   find out it does not work in some platform. */
-#ifndef LT_DLLAZY_OR_NOW
-#  ifdef RTLD_LAZY
-#    define LT_DLLAZY_OR_NOW           RTLD_LAZY
-#  else
-#    ifdef DL_LAZY
-#      define LT_DLLAZY_OR_NOW         DL_LAZY
-#    else
-#      ifdef RTLD_NOW
-#        define LT_DLLAZY_OR_NOW       RTLD_NOW
-#      else
-#        ifdef DL_NOW
-#          define LT_DLLAZY_OR_NOW     DL_NOW
-#        else
-#          define LT_DLLAZY_OR_NOW     0
-#        endif
-#      endif
-#    endif
-#  endif
-#endif
-
-#ifdef __cplusplus
-extern "C" void exit (int);
-#endif
-
-void fnord() { int i=42;}
-int main ()
-{
-  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
-  int status = $lt_dlunknown;
-
-  if (self)
-    {
-      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
-      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
-      /* dlclose (self); */
-    }
-
-    exit (status);
-}]
-EOF
-  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
-    (./conftest; exit; ) 2>/dev/null
-    lt_status=$?
-    case x$lt_status in
-      x$lt_dlno_uscore) $1 ;;
-      x$lt_dlneed_uscore) $2 ;;
-      x$lt_unknown|x*) $3 ;;
-    esac
-  else :
-    # compilation failed
-    $3
-  fi
-fi
-rm -fr conftest*
-])# _LT_AC_TRY_DLOPEN_SELF
-
-
-# AC_LIBTOOL_DLOPEN_SELF
-# -------------------
-AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF],
-[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl
-if test "x$enable_dlopen" != xyes; then
-  enable_dlopen=unknown
-  enable_dlopen_self=unknown
-  enable_dlopen_self_static=unknown
-else
-  lt_cv_dlopen=no
-  lt_cv_dlopen_libs=
-
-  case $host_os in
-  beos*)
-    lt_cv_dlopen="load_add_on"
-    lt_cv_dlopen_libs=
-    lt_cv_dlopen_self=yes
-    ;;
-
-  mingw* | pw32*)
-    lt_cv_dlopen="LoadLibrary"
-    lt_cv_dlopen_libs=
-   ;;
-
-  cygwin*)
-    lt_cv_dlopen="dlopen"
-    lt_cv_dlopen_libs=
-   ;;
-
-  darwin*)
-  # if libdl is installed we need to link against it
-    AC_CHECK_LIB([dl], [dlopen],
-               [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
-    lt_cv_dlopen="dyld"
-    lt_cv_dlopen_libs=
-    lt_cv_dlopen_self=yes
-    ])
-   ;;
-
-  *)
-    AC_CHECK_FUNC([shl_load],
-         [lt_cv_dlopen="shl_load"],
-      [AC_CHECK_LIB([dld], [shl_load],
-           [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"],
-       [AC_CHECK_FUNC([dlopen],
-             [lt_cv_dlopen="dlopen"],
-         [AC_CHECK_LIB([dl], [dlopen],
-               [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
-           [AC_CHECK_LIB([svld], [dlopen],
-                 [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
-             [AC_CHECK_LIB([dld], [dld_link],
-                   [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"])
-             ])
-           ])
-         ])
-       ])
-      ])
-    ;;
-  esac
-
-  if test "x$lt_cv_dlopen" != xno; then
-    enable_dlopen=yes
-  else
-    enable_dlopen=no
-  fi
-
-  case $lt_cv_dlopen in
-  dlopen)
-    save_CPPFLAGS="$CPPFLAGS"
-    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
-
-    save_LDFLAGS="$LDFLAGS"
-    eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
-
-    save_LIBS="$LIBS"
-    LIBS="$lt_cv_dlopen_libs $LIBS"
-
-    AC_CACHE_CHECK([whether a program can dlopen itself],
-         lt_cv_dlopen_self, [dnl
-         _LT_AC_TRY_DLOPEN_SELF(
-           lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
-           lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
-    ])
-
-    if test "x$lt_cv_dlopen_self" = xyes; then
-      LDFLAGS="$LDFLAGS $link_static_flag"
-      AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
-         lt_cv_dlopen_self_static, [dnl
-         _LT_AC_TRY_DLOPEN_SELF(
-           lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
-           lt_cv_dlopen_self_static=no,  lt_cv_dlopen_self_static=cross)
-      ])
-    fi
-
-    CPPFLAGS="$save_CPPFLAGS"
-    LDFLAGS="$save_LDFLAGS"
-    LIBS="$save_LIBS"
-    ;;
-  esac
-
-  case $lt_cv_dlopen_self in
-  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
-  *) enable_dlopen_self=unknown ;;
-  esac
-
-  case $lt_cv_dlopen_self_static in
-  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
-  *) enable_dlopen_self_static=unknown ;;
-  esac
-fi
-])# AC_LIBTOOL_DLOPEN_SELF
-
-
-# AC_LIBTOOL_PROG_CC_C_O([TAGNAME])
-# ---------------------------------
-# Check to see if options -c and -o are simultaneously supported by compiler
-AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O],
-[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl
-AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
-  [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
-  [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
-   $rm -r conftest 2>/dev/null
-   mkdir conftest
-   cd conftest
-   mkdir out
-   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
-
-   lt_compiler_flag="-o out/conftest2.$ac_objext"
-   # Insert the option either (1) after the last *FLAGS variable, or
-   # (2) before a word containing "conftest.", or (3) at the end.
-   # Note that $ac_compile itself does not contain backslashes and begins
-   # with a dollar sign (not a hyphen), so the echo should work correctly.
-   lt_compile=`echo "$ac_compile" | $SED \
-   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
-   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
-   -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
-   (eval "$lt_compile" 2>out/conftest.err)
-   ac_status=$?
-   cat out/conftest.err >&AS_MESSAGE_LOG_FD
-   echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
-   if (exit $ac_status) && test -s out/conftest2.$ac_objext
-   then
-     # The compiler can only warn and ignore the option if not recognized
-     # So say no if there are warnings
-     if test ! -s out/conftest.err; then
-       _LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
-     fi
-   fi
-   chmod u+w .
-   $rm conftest*
-   # SGI C++ compiler will create directory out/ii_files/ for
-   # template instantiation
-   test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files
-   $rm out/* && rmdir out
-   cd ..
-   rmdir conftest
-   $rm conftest*
-])
-])# AC_LIBTOOL_PROG_CC_C_O
-
-
-# AC_LIBTOOL_SYS_HARD_LINK_LOCKS([TAGNAME])
-# -----------------------------------------
-# Check to see if we can do hard links to lock some files if needed
-AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS],
-[AC_REQUIRE([_LT_AC_LOCK])dnl
-
-hard_links="nottested"
-if test "$_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
-  # do not overwrite the value of need_locks provided by the user
-  AC_MSG_CHECKING([if we can lock with hard links])
-  hard_links=yes
-  $rm conftest*
-  ln conftest.a conftest.b 2>/dev/null && hard_links=no
-  touch conftest.a
-  ln conftest.a conftest.b 2>&5 || hard_links=no
-  ln conftest.a conftest.b 2>/dev/null && hard_links=no
-  AC_MSG_RESULT([$hard_links])
-  if test "$hard_links" = no; then
-    AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
-    need_locks=warn
-  fi
-else
-  need_locks=no
-fi
-])# AC_LIBTOOL_SYS_HARD_LINK_LOCKS
-
-
-# AC_LIBTOOL_OBJDIR
-# -----------------
-AC_DEFUN([AC_LIBTOOL_OBJDIR],
-[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
-[rm -f .libs 2>/dev/null
-mkdir .libs 2>/dev/null
-if test -d .libs; then
-  lt_cv_objdir=.libs
-else
-  # MS-DOS does not allow filenames that begin with a dot.
-  lt_cv_objdir=_libs
-fi
-rmdir .libs 2>/dev/null])
-objdir=$lt_cv_objdir
-])# AC_LIBTOOL_OBJDIR
-
-
-# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH([TAGNAME])
-# ----------------------------------------------
-# Check hardcoding attributes.
-AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH],
-[AC_MSG_CHECKING([how to hardcode library paths into programs])
-_LT_AC_TAGVAR(hardcode_action, $1)=
-if test -n "$_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)" || \
-   test -n "$_LT_AC_TAGVAR(runpath_var $1)" || \
-   test "X$_LT_AC_TAGVAR(hardcode_automatic, $1)"="Xyes" ; then
-
-  # We can hardcode non-existant directories.
-  if test "$_LT_AC_TAGVAR(hardcode_direct, $1)" != no &&
-     # If the only mechanism to avoid hardcoding is shlibpath_var, we
-     # have to relink, otherwise we might link with an installed library
-     # when we should be linking with a yet-to-be-installed one
-     ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
-     test "$_LT_AC_TAGVAR(hardcode_minus_L, $1)" != no; then
-    # Linking always hardcodes the temporary library directory.
-    _LT_AC_TAGVAR(hardcode_action, $1)=relink
-  else
-    # We can link without hardcoding, and we can hardcode nonexisting dirs.
-    _LT_AC_TAGVAR(hardcode_action, $1)=immediate
-  fi
-else
-  # We cannot hardcode anything, or else we can only hardcode existing
-  # directories.
-  _LT_AC_TAGVAR(hardcode_action, $1)=unsupported
-fi
-AC_MSG_RESULT([$_LT_AC_TAGVAR(hardcode_action, $1)])
-
-if test "$_LT_AC_TAGVAR(hardcode_action, $1)" = relink; then
-  # Fast installation is not supported
-  enable_fast_install=no
-elif test "$shlibpath_overrides_runpath" = yes ||
-     test "$enable_shared" = no; then
-  # Fast installation is not necessary
-  enable_fast_install=needless
-fi
-])# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH
-
-
-# AC_LIBTOOL_SYS_LIB_STRIP
-# ------------------------
-AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP],
-[striplib=
-old_striplib=
-AC_MSG_CHECKING([whether stripping libraries is possible])
-if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then
-  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
-  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
-  AC_MSG_RESULT([yes])
-else
-# FIXME - insert some real tests, host_os isn't really good enough
-  case $host_os in
-   darwin*)
-       if test -n "$STRIP" ; then
-         striplib="$STRIP -x"
-         AC_MSG_RESULT([yes])
-       else
-  AC_MSG_RESULT([no])
-fi
-       ;;
-   *)
-  AC_MSG_RESULT([no])
-    ;;
-  esac
-fi
-])# AC_LIBTOOL_SYS_LIB_STRIP
-
-
-# AC_LIBTOOL_SYS_DYNAMIC_LINKER
-# -----------------------------
-# PORTME Fill in your ld.so characteristics
-AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER],
-[AC_MSG_CHECKING([dynamic linker characteristics])
-library_names_spec=
-libname_spec='lib$name'
-soname_spec=
-shrext_cmds=".so"
-postinstall_cmds=
-postuninstall_cmds=
-finish_cmds=
-finish_eval=
-shlibpath_var=
-shlibpath_overrides_runpath=unknown
-version_type=none
-dynamic_linker="$host_os ld.so"
-sys_lib_dlsearch_path_spec="/lib /usr/lib"
-if test "$GCC" = yes; then
-  sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
-  if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then
-    # if the path contains ";" then we assume it to be the separator
-    # otherwise default to the standard path separator (i.e. ":") - it is
-    # assumed that no part of a normal pathname contains ";" but that should
-    # okay in the real world where ";" in dirpaths is itself problematic.
-    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
-  else
-    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
-  fi
-else
-  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
-fi
-need_lib_prefix=unknown
-hardcode_into_libs=no
-
-# when you set need_version to no, make sure it does not cause -set_version
-# flags to be left without arguments
-need_version=unknown
-
-case $host_os in
-aix3*)
-  version_type=linux
-  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
-  shlibpath_var=LIBPATH
-
-  # AIX 3 has no versioning support, so we append a major version to the name.
-  soname_spec='${libname}${release}${shared_ext}$major'
-  ;;
-
-aix4* | aix5*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  hardcode_into_libs=yes
-  if test "$host_cpu" = ia64; then
-    # AIX 5 supports IA64
-    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
-    shlibpath_var=LD_LIBRARY_PATH
-  else
-    # With GCC up to 2.95.x, collect2 would create an import file
-    # for dependence libraries.  The import file would start with
-    # the line `#! .'.  This would cause the generated library to
-    # depend on `.', always an invalid library.  This was fixed in
-    # development snapshots of GCC prior to 3.0.
-    case $host_os in
-      aix4 | aix4.[[01]] | aix4.[[01]].*)
-      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
-          echo ' yes '
-          echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then
-       :
-      else
-       can_build_shared=no
-      fi
-      ;;
-    esac
-    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
-    # soname into executable. Probably we can add versioning support to
-    # collect2, so additional links can be useful in future.
-    if test "$aix_use_runtimelinking" = yes; then
-      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
-      # instead of lib<name>.a to let people know that these are not
-      # typical AIX shared libraries.
-      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-    else
-      # We preserve .a as extension for shared libraries through AIX4.2
-      # and later when we are not doing run time linking.
-      library_names_spec='${libname}${release}.a $libname.a'
-      soname_spec='${libname}${release}${shared_ext}$major'
-    fi
-    shlibpath_var=LIBPATH
-  fi
-  ;;
-
-amigaos*)
-  library_names_spec='$libname.ixlibrary $libname.a'
-  # Create ${libname}_ixlibrary.a entries in /sys/libs.
-  finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
-  ;;
-
-beos*)
-  library_names_spec='${libname}${shared_ext}'
-  dynamic_linker="$host_os ld.so"
-  shlibpath_var=LIBRARY_PATH
-  ;;
-
-bsdi4*)
-  version_type=linux
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
-  shlibpath_var=LD_LIBRARY_PATH
-  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
-  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
-  # the default ld.so.conf also contains /usr/contrib/lib and
-  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
-  # libtool to hard-code these into programs
-  ;;
-
-cygwin* | mingw* | pw32*)
-  version_type=windows
-  shrext_cmds=".dll"
-  need_version=no
-  need_lib_prefix=no
-
-  case $GCC,$host_os in
-  yes,cygwin* | yes,mingw* | yes,pw32*)
-    library_names_spec='$libname.dll.a'
-    # DLL is installed to $(libdir)/../bin by postinstall_cmds
-    postinstall_cmds='base_file=`basename \${file}`~
-      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~
-      dldir=$destdir/`dirname \$dlpath`~
-      test -d \$dldir || mkdir -p \$dldir~
-      $install_prog $dir/$dlname \$dldir/$dlname'
-    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
-      dlpath=$dir/\$dldll~
-       $rm \$dlpath'
-    shlibpath_overrides_runpath=yes
-
-    case $host_os in
-    cygwin*)
-      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
-      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
-      sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
-      ;;
-    mingw*)
-      # MinGW DLLs use traditional 'lib' prefix
-      soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
-      sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
-      if echo "$sys_lib_search_path_spec" | [grep ';[c-zC-Z]:/' >/dev/null]; then
-        # It is most probably a Windows format PATH printed by
-        # mingw gcc, but we are running on Cygwin. Gcc prints its search
-        # path with ; separators, and with drive letters. We can handle the
-        # drive letters (cygwin fileutils understands them), so leave them,
-        # especially as we might pass files found there to a mingw objdump,
-        # which wouldn't understand a cygwinified path. Ahh.
-        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
-      else
-        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
-      fi
-      ;;
-    pw32*)
-      # pw32 DLLs use 'pw' prefix rather than 'lib'
-      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
-      ;;
-    esac
-    ;;
-
-  *)
-    library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
-    ;;
-  esac
-  dynamic_linker='Win32 ld.exe'
-  # FIXME: first we should search . and the directory the executable is in
-  shlibpath_var=PATH
-  ;;
-
-darwin* | rhapsody*)
-  dynamic_linker="$host_os dyld"
-  version_type=darwin
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext'
-  soname_spec='${libname}${release}${major}$shared_ext'
-  shlibpath_overrides_runpath=yes
-  shlibpath_var=DYLD_LIBRARY_PATH
-  shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)'
-  # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same.
-  if test "$GCC" = yes; then
-    sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"`
-  else
-    sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib'
-  fi
-  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
-  ;;
-
-dgux*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  ;;
-
-freebsd1*)
-  dynamic_linker=no
-  ;;
-
-kfreebsd*-gnu)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  hardcode_into_libs=yes
-  dynamic_linker='GNU ld.so'
-  ;;
-
-freebsd*)
-  objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout`
-  version_type=freebsd-$objformat
-  case $version_type in
-    freebsd-elf*)
-      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
-      need_version=no
-      need_lib_prefix=no
-      ;;
-    freebsd-*)
-      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
-      need_version=yes
-      ;;
-  esac
-  shlibpath_var=LD_LIBRARY_PATH
-  case $host_os in
-  freebsd2*)
-    shlibpath_overrides_runpath=yes
-    ;;
-  freebsd3.[01]* | freebsdelf3.[01]*)
-    shlibpath_overrides_runpath=yes
-    hardcode_into_libs=yes
-    ;;
-  *) # from 3.2 on
-    shlibpath_overrides_runpath=no
-    hardcode_into_libs=yes
-    ;;
-  esac
-  ;;
-
-gnu*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  hardcode_into_libs=yes
-  ;;
-
-hpux9* | hpux10* | hpux11*)
-  # Give a soname corresponding to the major version so that dld.sl refuses to
-  # link against other versions.
-  version_type=sunos
-  need_lib_prefix=no
-  need_version=no
-  case "$host_cpu" in
-  ia64*)
-    shrext_cmds='.so'
-    hardcode_into_libs=yes
-    dynamic_linker="$host_os dld.so"
-    shlibpath_var=LD_LIBRARY_PATH
-    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
-    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-    soname_spec='${libname}${release}${shared_ext}$major'
-    if test "X$HPUX_IA64_MODE" = X32; then
-      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
-    else
-      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
-    fi
-    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
-    ;;
-   hppa*64*)
-     shrext_cmds='.sl'
-     hardcode_into_libs=yes
-     dynamic_linker="$host_os dld.sl"
-     shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
-     shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
-     library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-     soname_spec='${libname}${release}${shared_ext}$major'
-     sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
-     sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
-     ;;
-   *)
-    shrext_cmds='.sl'
-    dynamic_linker="$host_os dld.sl"
-    shlibpath_var=SHLIB_PATH
-    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
-    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-    soname_spec='${libname}${release}${shared_ext}$major'
-    ;;
-  esac
-  # HP-UX runs *really* slowly unless shared libraries are mode 555.
-  postinstall_cmds='chmod 555 $lib'
-  ;;
-
-irix5* | irix6* | nonstopux*)
-  case $host_os in
-    nonstopux*) version_type=nonstopux ;;
-    *)
-       if test "$lt_cv_prog_gnu_ld" = yes; then
-               version_type=linux
-       else
-               version_type=irix
-       fi ;;
-  esac
-  need_lib_prefix=no
-  need_version=no
-  soname_spec='${libname}${release}${shared_ext}$major'
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
-  case $host_os in
-  irix5* | nonstopux*)
-    libsuff= shlibsuff=
-    ;;
-  *)
-    case $LD in # libtool.m4 will add one of these switches to LD
-    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
-      libsuff= shlibsuff= libmagic=32-bit;;
-    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
-      libsuff=32 shlibsuff=N32 libmagic=N32;;
-    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
-      libsuff=64 shlibsuff=64 libmagic=64-bit;;
-    *) libsuff= shlibsuff= libmagic=never-match;;
-    esac
-    ;;
-  esac
-  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
-  shlibpath_overrides_runpath=no
-  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
-  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
-  hardcode_into_libs=yes
-  ;;
-
-# No shared lib support for Linux oldld, aout, or coff.
-linux*oldld* | linux*aout* | linux*coff*)
-  dynamic_linker=no
-  ;;
-
-# This must be Linux ELF.
-linux*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  # This implies no fast_install, which is unacceptable.
-  # Some rework will be needed to allow for fast_install
-  # before this can be enabled.
-  hardcode_into_libs=yes
-
-  # Append ld.so.conf contents to the search path
-  if test -f /etc/ld.so.conf; then
-    lt_ld_extra=`$SED -e 's/[:,\t]/ /g;s/=[^=]*$//;s/=[^= ]* / /g' /etc/ld.so.conf | tr '\n' ' '`
-    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
-  fi
-
-  # We used to test for /lib/ld.so.1 and disable shared libraries on
-  # powerpc, because MkLinux only supported shared libraries with the
-  # GNU dynamic linker.  Since this was broken with cross compilers,
-  # most powerpc-linux boxes support dynamic linking these days and
-  # people can always --disable-shared, the test was removed, and we
-  # assume the GNU/Linux dynamic linker is in use.
-  dynamic_linker='GNU/Linux ld.so'
-  ;;
-
-netbsdelf*-gnu)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  hardcode_into_libs=yes
-  dynamic_linker='NetBSD ld.elf_so'
-  ;;
-
-knetbsd*-gnu)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  hardcode_into_libs=yes
-  dynamic_linker='GNU ld.so'
-  ;;
-
-netbsd*)
-  version_type=sunos
-  need_lib_prefix=no
-  need_version=no
-  if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
-    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
-    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
-    dynamic_linker='NetBSD (a.out) ld.so'
-  else
-    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
-    soname_spec='${libname}${release}${shared_ext}$major'
-    dynamic_linker='NetBSD ld.elf_so'
-  fi
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=yes
-  hardcode_into_libs=yes
-  ;;
-
-newsos6)
-  version_type=linux
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=yes
-  ;;
-
-nto-qnx*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=yes
-  ;;
-
-openbsd*)
-  version_type=sunos
-  need_lib_prefix=no
-  need_version=yes
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
-  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
-  shlibpath_var=LD_LIBRARY_PATH
-  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
-    case $host_os in
-      openbsd2.[[89]] | openbsd2.[[89]].*)
-       shlibpath_overrides_runpath=no
-       ;;
-      *)
-       shlibpath_overrides_runpath=yes
-       ;;
-      esac
-  else
-    shlibpath_overrides_runpath=yes
-  fi
-  ;;
-
-os2*)
-  libname_spec='$name'
-  shrext_cmds=".dll"
-  need_lib_prefix=no
-  library_names_spec='$libname${shared_ext} $libname.a'
-  dynamic_linker='OS/2 ld.exe'
-  shlibpath_var=LIBPATH
-  ;;
-
-osf3* | osf4* | osf5*)
-  version_type=osf
-  need_lib_prefix=no
-  need_version=no
-  soname_spec='${libname}${release}${shared_ext}$major'
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  shlibpath_var=LD_LIBRARY_PATH
-  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
-  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
-  ;;
-
-sco3.2v5*)
-  version_type=osf
-  soname_spec='${libname}${release}${shared_ext}$major'
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  shlibpath_var=LD_LIBRARY_PATH
-  ;;
-
-solaris*)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=yes
-  hardcode_into_libs=yes
-  # ldd complains unless libraries are executable
-  postinstall_cmds='chmod +x $lib'
-  ;;
-
-sunos4*)
-  version_type=sunos
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
-  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=yes
-  if test "$with_gnu_ld" = yes; then
-    need_lib_prefix=no
-  fi
-  need_version=yes
-  ;;
-
-sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
-  version_type=linux
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  case $host_vendor in
-    sni)
-      shlibpath_overrides_runpath=no
-      need_lib_prefix=no
-      export_dynamic_flag_spec='${wl}-Blargedynsym'
-      runpath_var=LD_RUN_PATH
-      ;;
-    siemens)
-      need_lib_prefix=no
-      ;;
-    motorola)
-      need_lib_prefix=no
-      need_version=no
-      shlibpath_overrides_runpath=no
-      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
-      ;;
-  esac
-  ;;
-
-sysv4*MP*)
-  if test -d /usr/nec ;then
-    version_type=linux
-    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
-    soname_spec='$libname${shared_ext}.$major'
-    shlibpath_var=LD_LIBRARY_PATH
-  fi
-  ;;
-
-uts4*)
-  version_type=linux
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  ;;
-
-*)
-  dynamic_linker=no
-  ;;
-esac
-AC_MSG_RESULT([$dynamic_linker])
-test "$dynamic_linker" = no && can_build_shared=no
-])# AC_LIBTOOL_SYS_DYNAMIC_LINKER
-
-
-# _LT_AC_TAGCONFIG
-# ----------------
-AC_DEFUN([_LT_AC_TAGCONFIG],
-[AC_ARG_WITH([tags],
-    [AC_HELP_STRING([--with-tags@<:@=TAGS@:>@],
-        [include additional configurations @<:@automatic@:>@])],
-    [tagnames="$withval"])
-
-if test -f "$ltmain" && test -n "$tagnames"; then
-  if test ! -f "${ofile}"; then
-    AC_MSG_WARN([output file `$ofile' does not exist])
-  fi
-
-  if test -z "$LTCC"; then
-    eval "`$SHELL ${ofile} --config | grep '^LTCC='`"
-    if test -z "$LTCC"; then
-      AC_MSG_WARN([output file `$ofile' does not look like a libtool script])
-    else
-      AC_MSG_WARN([using `LTCC=$LTCC', extracted from `$ofile'])
-    fi
-  fi
-
-  # Extract list of available tagged configurations in $ofile.
-  # Note that this assumes the entire list is on one line.
-  available_tags=`grep "^available_tags=" "${ofile}" | $SED -e 's/available_tags=\(.*$\)/\1/' -e 's/\"//g'`
-
-  lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
-  for tagname in $tagnames; do
-    IFS="$lt_save_ifs"
-    # Check whether tagname contains only valid characters
-    case `$echo "X$tagname" | $Xsed -e 's:[[-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,/]]::g'` in
-    "") ;;
-    *)  AC_MSG_ERROR([invalid tag name: $tagname])
-       ;;
-    esac
-
-    if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "${ofile}" > /dev/null
-    then
-      AC_MSG_ERROR([tag name \"$tagname\" already exists])
-    fi
-
-    # Update the list of available tags.
-    if test -n "$tagname"; then
-      echo appending configuration tag \"$tagname\" to $ofile
-
-      case $tagname in
-      CXX)
-       if test -n "$CXX" && test "X$CXX" != "Xno"; then
-         AC_LIBTOOL_LANG_CXX_CONFIG
-       else
-         tagname=""
-       fi
-       ;;
-
-      F77)
-       if test -n "$F77" && test "X$F77" != "Xno"; then
-         AC_LIBTOOL_LANG_F77_CONFIG
-       else
-         tagname=""
-       fi
-       ;;
-
-      GCJ)
-       if test -n "$GCJ" && test "X$GCJ" != "Xno"; then
-         AC_LIBTOOL_LANG_GCJ_CONFIG
-       else
-         tagname=""
-       fi
-       ;;
-
-      RC)
-       AC_LIBTOOL_LANG_RC_CONFIG
-       ;;
-
-      *)
-       AC_MSG_ERROR([Unsupported tag name: $tagname])
-       ;;
-      esac
-
-      # Append the new tag name to the list of available tags.
-      if test -n "$tagname" ; then
-      available_tags="$available_tags $tagname"
-    fi
-    fi
-  done
-  IFS="$lt_save_ifs"
-
-  # Now substitute the updated list of available tags.
-  if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then
-    mv "${ofile}T" "$ofile"
-    chmod +x "$ofile"
-  else
-    rm -f "${ofile}T"
-    AC_MSG_ERROR([unable to update list of available tagged configurations.])
-  fi
-fi
-])# _LT_AC_TAGCONFIG
-
-
-# AC_LIBTOOL_DLOPEN
-# -----------------
-# enable checks for dlopen support
-AC_DEFUN([AC_LIBTOOL_DLOPEN],
- [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])
-])# AC_LIBTOOL_DLOPEN
-
-
-# AC_LIBTOOL_WIN32_DLL
-# --------------------
-# declare package support for building win32 dll's
-AC_DEFUN([AC_LIBTOOL_WIN32_DLL],
-[AC_BEFORE([$0], [AC_LIBTOOL_SETUP])
-])# AC_LIBTOOL_WIN32_DLL
-
-
-# AC_ENABLE_SHARED([DEFAULT])
-# ---------------------------
-# implement the --enable-shared flag
-# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
-AC_DEFUN([AC_ENABLE_SHARED],
-[define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl
-AC_ARG_ENABLE([shared],
-    [AC_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
-       [build shared libraries @<:@default=]AC_ENABLE_SHARED_DEFAULT[@:>@])],
-    [p=${PACKAGE-default}
-    case $enableval in
-    yes) enable_shared=yes ;;
-    no) enable_shared=no ;;
-    *)
-      enable_shared=no
-      # Look at the argument we got.  We use all the common list separators.
-      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
-      for pkg in $enableval; do
-       IFS="$lt_save_ifs"
-       if test "X$pkg" = "X$p"; then
-         enable_shared=yes
-       fi
-      done
-      IFS="$lt_save_ifs"
-      ;;
-    esac],
-    [enable_shared=]AC_ENABLE_SHARED_DEFAULT)
-])# AC_ENABLE_SHARED
-
-
-# AC_DISABLE_SHARED
-# -----------------
-#- set the default shared flag to --disable-shared
-AC_DEFUN([AC_DISABLE_SHARED],
-[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
-AC_ENABLE_SHARED(no)
-])# AC_DISABLE_SHARED
-
-
-# AC_ENABLE_STATIC([DEFAULT])
-# ---------------------------
-# implement the --enable-static flag
-# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
-AC_DEFUN([AC_ENABLE_STATIC],
-[define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl
-AC_ARG_ENABLE([static],
-    [AC_HELP_STRING([--enable-static@<:@=PKGS@:>@],
-       [build static libraries @<:@default=]AC_ENABLE_STATIC_DEFAULT[@:>@])],
-    [p=${PACKAGE-default}
-    case $enableval in
-    yes) enable_static=yes ;;
-    no) enable_static=no ;;
-    *)
-     enable_static=no
-      # Look at the argument we got.  We use all the common list separators.
-      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
-      for pkg in $enableval; do
-       IFS="$lt_save_ifs"
-       if test "X$pkg" = "X$p"; then
-         enable_static=yes
-       fi
-      done
-      IFS="$lt_save_ifs"
-      ;;
-    esac],
-    [enable_static=]AC_ENABLE_STATIC_DEFAULT)
-])# AC_ENABLE_STATIC
-
-
-# AC_DISABLE_STATIC
-# -----------------
-# set the default static flag to --disable-static
-AC_DEFUN([AC_DISABLE_STATIC],
-[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
-AC_ENABLE_STATIC(no)
-])# AC_DISABLE_STATIC
-
-
-# AC_ENABLE_FAST_INSTALL([DEFAULT])
-# ---------------------------------
-# implement the --enable-fast-install flag
-# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
-AC_DEFUN([AC_ENABLE_FAST_INSTALL],
-[define([AC_ENABLE_FAST_INSTALL_DEFAULT], ifelse($1, no, no, yes))dnl
-AC_ARG_ENABLE([fast-install],
-    [AC_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
-    [optimize for fast installation @<:@default=]AC_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
-    [p=${PACKAGE-default}
-    case $enableval in
-    yes) enable_fast_install=yes ;;
-    no) enable_fast_install=no ;;
-    *)
-      enable_fast_install=no
-      # Look at the argument we got.  We use all the common list separators.
-      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
-      for pkg in $enableval; do
-       IFS="$lt_save_ifs"
-       if test "X$pkg" = "X$p"; then
-         enable_fast_install=yes
-       fi
-      done
-      IFS="$lt_save_ifs"
-      ;;
-    esac],
-    [enable_fast_install=]AC_ENABLE_FAST_INSTALL_DEFAULT)
-])# AC_ENABLE_FAST_INSTALL
-
-
-# AC_DISABLE_FAST_INSTALL
-# -----------------------
-# set the default to --disable-fast-install
-AC_DEFUN([AC_DISABLE_FAST_INSTALL],
-[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
-AC_ENABLE_FAST_INSTALL(no)
-])# AC_DISABLE_FAST_INSTALL
-
-
-# AC_LIBTOOL_PICMODE([MODE])
-# --------------------------
-# implement the --with-pic flag
-# MODE is either `yes' or `no'.  If omitted, it defaults to `both'.
-AC_DEFUN([AC_LIBTOOL_PICMODE],
-[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
-pic_mode=ifelse($#,1,$1,default)
-])# AC_LIBTOOL_PICMODE
-
-
-# AC_PROG_EGREP
-# -------------
-# This is predefined starting with Autoconf 2.54, so this conditional
-# definition can be removed once we require Autoconf 2.54 or later.
-m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP],
-[AC_CACHE_CHECK([for egrep], [ac_cv_prog_egrep],
-   [if echo a | (grep -E '(a|b)') >/dev/null 2>&1
-    then ac_cv_prog_egrep='grep -E'
-    else ac_cv_prog_egrep='egrep'
-    fi])
- EGREP=$ac_cv_prog_egrep
- AC_SUBST([EGREP])
-])])
-
-
-# AC_PATH_TOOL_PREFIX
-# -------------------
-# find a file program which can recognise shared library
-AC_DEFUN([AC_PATH_TOOL_PREFIX],
-[AC_REQUIRE([AC_PROG_EGREP])dnl
-AC_MSG_CHECKING([for $1])
-AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
-[case $MAGIC_CMD in
-[[\\/*] |  ?:[\\/]*])
-  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
-  ;;
-*)
-  lt_save_MAGIC_CMD="$MAGIC_CMD"
-  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
-dnl $ac_dummy forces splitting on constant user-supplied paths.
-dnl POSIX.2 word splitting is done only on the output of word expansions,
-dnl not every word.  This closes a longstanding sh security hole.
-  ac_dummy="ifelse([$2], , $PATH, [$2])"
-  for ac_dir in $ac_dummy; do
-    IFS="$lt_save_ifs"
-    test -z "$ac_dir" && ac_dir=.
-    if test -f $ac_dir/$1; then
-      lt_cv_path_MAGIC_CMD="$ac_dir/$1"
-      if test -n "$file_magic_test_file"; then
-       case $deplibs_check_method in
-       "file_magic "*)
-         file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`"
-         MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
-         if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
-           $EGREP "$file_magic_regex" > /dev/null; then
-           :
-         else
-           cat <<EOF 1>&2
-
-*** Warning: the command libtool uses to detect shared libraries,
-*** $file_magic_cmd, produces output that libtool cannot recognize.
-*** The result is that libtool may fail to recognize shared libraries
-*** as such.  This will affect the creation of libtool libraries that
-*** depend on shared libraries, but programs linked with such libtool
-*** libraries will work regardless of this problem.  Nevertheless, you
-*** may want to report the problem to your system manager and/or to
-*** bug-libtool@gnu.org
-
-EOF
-         fi ;;
-       esac
-      fi
-      break
-    fi
-  done
-  IFS="$lt_save_ifs"
-  MAGIC_CMD="$lt_save_MAGIC_CMD"
-  ;;
-esac])
-MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
-if test -n "$MAGIC_CMD"; then
-  AC_MSG_RESULT($MAGIC_CMD)
-else
-  AC_MSG_RESULT(no)
-fi
-])# AC_PATH_TOOL_PREFIX
-
-
-# AC_PATH_MAGIC
-# -------------
-# find a file program which can recognise a shared library
-AC_DEFUN([AC_PATH_MAGIC],
-[AC_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
-if test -z "$lt_cv_path_MAGIC_CMD"; then
-  if test -n "$ac_tool_prefix"; then
-    AC_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
-  else
-    MAGIC_CMD=:
-  fi
-fi
-])# AC_PATH_MAGIC
-
-
-# AC_PROG_LD
-# ----------
-# find the pathname to the GNU or non-GNU linker
-AC_DEFUN([AC_PROG_LD],
-[AC_ARG_WITH([gnu-ld],
-    [AC_HELP_STRING([--with-gnu-ld],
-       [assume the C compiler uses GNU ld @<:@default=no@:>@])],
-    [test "$withval" = no || with_gnu_ld=yes],
-    [with_gnu_ld=no])
-AC_REQUIRE([LT_AC_PROG_SED])dnl
-AC_REQUIRE([AC_PROG_CC])dnl
-AC_REQUIRE([AC_CANONICAL_HOST])dnl
-AC_REQUIRE([AC_CANONICAL_BUILD])dnl
-ac_prog=ld
-if test "$GCC" = yes; then
-  # Check if gcc -print-prog-name=ld gives a path.
-  AC_MSG_CHECKING([for ld used by $CC])
-  case $host in
-  *-*-mingw*)
-    # gcc leaves a trailing carriage return which upsets mingw
-    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
-  *)
-    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
-  esac
-  case $ac_prog in
-    # Accept absolute paths.
-    [[\\/]]* | ?:[[\\/]]*)
-      re_direlt='/[[^/]][[^/]]*/\.\./'
-      # Canonicalize the pathname of ld
-      ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'`
-      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
-       ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"`
-      done
-      test -z "$LD" && LD="$ac_prog"
-      ;;
-  "")
-    # If it fails, then pretend we aren't using GCC.
-    ac_prog=ld
-    ;;
-  *)
-    # If it is relative, then search for the first ld in PATH.
-    with_gnu_ld=unknown
-    ;;
-  esac
-elif test "$with_gnu_ld" = yes; then
-  AC_MSG_CHECKING([for GNU ld])
-else
-  AC_MSG_CHECKING([for non-GNU ld])
-fi
-AC_CACHE_VAL(lt_cv_path_LD,
-[if test -z "$LD"; then
-  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
-  for ac_dir in $PATH; do
-    IFS="$lt_save_ifs"
-    test -z "$ac_dir" && ac_dir=.
-    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
-      lt_cv_path_LD="$ac_dir/$ac_prog"
-      # Check to see if the program is GNU ld.  I'd rather use --version,
-      # but apparently some GNU ld's only accept -v.
-      # Break only if it was the GNU/non-GNU ld that we prefer.
-      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
-      *GNU* | *'with BFD'*)
-       test "$with_gnu_ld" != no && break
-       ;;
-      *)
-       test "$with_gnu_ld" != yes && break
-       ;;
-      esac
-    fi
-  done
-  IFS="$lt_save_ifs"
-else
-  lt_cv_path_LD="$LD" # Let the user override the test with a path.
-fi])
-LD="$lt_cv_path_LD"
-if test -n "$LD"; then
-  AC_MSG_RESULT($LD)
-else
-  AC_MSG_RESULT(no)
-fi
-test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
-AC_PROG_LD_GNU
-])# AC_PROG_LD
-
-
-# AC_PROG_LD_GNU
-# --------------
-AC_DEFUN([AC_PROG_LD_GNU],
-[AC_REQUIRE([AC_PROG_EGREP])dnl
-AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
-[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
-case `$LD -v 2>&1 </dev/null` in
-*GNU* | *'with BFD'*)
-  lt_cv_prog_gnu_ld=yes
-  ;;
-*)
-  lt_cv_prog_gnu_ld=no
-  ;;
-esac])
-with_gnu_ld=$lt_cv_prog_gnu_ld
-])# AC_PROG_LD_GNU
-
-
-# AC_PROG_LD_RELOAD_FLAG
-# ----------------------
-# find reload flag for linker
-#   -- PORTME Some linkers may need a different reload flag.
-AC_DEFUN([AC_PROG_LD_RELOAD_FLAG],
-[AC_CACHE_CHECK([for $LD option to reload object files],
-  lt_cv_ld_reload_flag,
-  [lt_cv_ld_reload_flag='-r'])
-reload_flag=$lt_cv_ld_reload_flag
-case $reload_flag in
-"" | " "*) ;;
-*) reload_flag=" $reload_flag" ;;
-esac
-reload_cmds='$LD$reload_flag -o $output$reload_objs'
-])# AC_PROG_LD_RELOAD_FLAG
-
-
-# AC_DEPLIBS_CHECK_METHOD
-# -----------------------
-# how to check for library dependencies
-#  -- PORTME fill in with the dynamic library characteristics
-AC_DEFUN([AC_DEPLIBS_CHECK_METHOD],
-[AC_CACHE_CHECK([how to recognise dependent libraries],
-lt_cv_deplibs_check_method,
-[lt_cv_file_magic_cmd='$MAGIC_CMD'
-lt_cv_file_magic_test_file=
-lt_cv_deplibs_check_method='unknown'
-# Need to set the preceding variable on all platforms that support
-# interlibrary dependencies.
-# 'none' -- dependencies not supported.
-# `unknown' -- same as none, but documents that we really don't know.
-# 'pass_all' -- all dependencies passed with no checks.
-# 'test_compile' -- check by making test program.
-# 'file_magic [[regex]]' -- check by looking for files in library path
-# which responds to the $file_magic_cmd with a given extended regex.
-# If you have `file' or equivalent on your system and you're not sure
-# whether `pass_all' will *always* work, you probably want this one.
-
-case $host_os in
-aix4* | aix5*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-beos*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-bsdi4*)
-  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
-  lt_cv_file_magic_cmd='/usr/bin/file -L'
-  lt_cv_file_magic_test_file=/shlib/libc.so
-  ;;
-
-cygwin*)
-  # func_win32_libid is a shell function defined in ltmain.sh
-  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
-  lt_cv_file_magic_cmd='func_win32_libid'
-  ;;
-
-mingw* | pw32*)
-  # Base MSYS/MinGW do not provide the 'file' command needed by
-  # func_win32_libid shell function, so use a weaker test based on 'objdump'.
-  lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
-  lt_cv_file_magic_cmd='$OBJDUMP -f'
-  ;;
-
-darwin* | rhapsody*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-freebsd* | kfreebsd*-gnu)
-  if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
-    case $host_cpu in
-    i*86 )
-      # Not sure whether the presence of OpenBSD here was a mistake.
-      # Let's accept both of them until this is cleared up.
-      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD)/i[[3-9]]86 (compact )?demand paged shared library'
-      lt_cv_file_magic_cmd=/usr/bin/file
-      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
-      ;;
-    esac
-  else
-    lt_cv_deplibs_check_method=pass_all
-  fi
-  ;;
-
-gnu*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-hpux10.20* | hpux11*)
-  lt_cv_file_magic_cmd=/usr/bin/file
-  case "$host_cpu" in
-  ia64*)
-    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
-    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
-    ;;
-  hppa*64*)
-    [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]']
-    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
-    ;;
-  *)
-    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library'
-    lt_cv_file_magic_test_file=/usr/lib/libc.sl
-    ;;
-  esac
-  ;;
-
-irix5* | irix6* | nonstopux*)
-  case $LD in
-  *-32|*"-32 ") libmagic=32-bit;;
-  *-n32|*"-n32 ") libmagic=N32;;
-  *-64|*"-64 ") libmagic=64-bit;;
-  *) libmagic=never-match;;
-  esac
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-# This must be Linux ELF.
-linux*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
-  if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
-    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
-  else
-    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
-  fi
-  ;;
-
-newos6*)
-  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
-  lt_cv_file_magic_cmd=/usr/bin/file
-  lt_cv_file_magic_test_file=/usr/lib/libnls.so
-  ;;
-
-nto-qnx*)
-  lt_cv_deplibs_check_method=unknown
-  ;;
-
-openbsd*)
-  lt_cv_file_magic_cmd=/usr/bin/file
-  lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
-  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
-    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB shared object'
-  else
-    lt_cv_deplibs_check_method='file_magic OpenBSD.* shared library'
-  fi
-  ;;
-
-osf3* | osf4* | osf5*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-sco3.2v5*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-solaris*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-
-sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
-  case $host_vendor in
-  motorola)
-    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
-    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
-    ;;
-  ncr)
-    lt_cv_deplibs_check_method=pass_all
-    ;;
-  sequent)
-    lt_cv_file_magic_cmd='/bin/file'
-    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
-    ;;
-  sni)
-    lt_cv_file_magic_cmd='/bin/file'
-    lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
-    lt_cv_file_magic_test_file=/lib/libc.so
-    ;;
-  siemens)
-    lt_cv_deplibs_check_method=pass_all
-    ;;
-  esac
-  ;;
-
-sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7* | sysv4*uw2*)
-  lt_cv_deplibs_check_method=pass_all
-  ;;
-esac
-])
-file_magic_cmd=$lt_cv_file_magic_cmd
-deplibs_check_method=$lt_cv_deplibs_check_method
-test -z "$deplibs_check_method" && deplibs_check_method=unknown
-])# AC_DEPLIBS_CHECK_METHOD
-
-
-# AC_PROG_NM
-# ----------
-# find the pathname to a BSD-compatible name lister
-AC_DEFUN([AC_PROG_NM],
-[AC_CACHE_CHECK([for BSD-compatible nm], lt_cv_path_NM,
-[if test -n "$NM"; then
-  # Let the user override the test.
-  lt_cv_path_NM="$NM"
-else
-  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
-  for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do
-    IFS="$lt_save_ifs"
-    test -z "$ac_dir" && ac_dir=.
-    tmp_nm="$ac_dir/${ac_tool_prefix}nm"
-    if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
-      # Check to see if the nm accepts a BSD-compat flag.
-      # Adding the `sed 1q' prevents false positives on HP-UX, which says:
-      #   nm: unknown option "B" ignored
-      # Tru64's nm complains that /dev/null is an invalid object file
-      case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
-      */dev/null* | *'Invalid file or object type'*)
-       lt_cv_path_NM="$tmp_nm -B"
-       break
-        ;;
-      *)
-       case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
-       */dev/null*)
-         lt_cv_path_NM="$tmp_nm -p"
-         break
-         ;;
-       *)
-         lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
-         continue # so that we can try to find one that supports BSD flags
-         ;;
-       esac
-      esac
-    fi
-  done
-  IFS="$lt_save_ifs"
-  test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm
-fi])
-NM="$lt_cv_path_NM"
-])# AC_PROG_NM
-
-
-# AC_CHECK_LIBM
-# -------------
-# check for math library
-AC_DEFUN([AC_CHECK_LIBM],
-[AC_REQUIRE([AC_CANONICAL_HOST])dnl
-LIBM=
-case $host in
-*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*)
-  # These system don't have libm, or don't need it
-  ;;
-*-ncr-sysv4.3*)
-  AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
-  AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
-  ;;
-*)
-  AC_CHECK_LIB(m, cos, LIBM="-lm")
-  ;;
-esac
-])# AC_CHECK_LIBM
-
-
-# AC_LIBLTDL_CONVENIENCE([DIRECTORY])
-# -----------------------------------
-# sets LIBLTDL to the link flags for the libltdl convenience library and
-# LTDLINCL to the include flags for the libltdl header and adds
-# --enable-ltdl-convenience to the configure arguments.  Note that LIBLTDL
-# and LTDLINCL are not AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called.  If
-# DIRECTORY is not provided, it is assumed to be `libltdl'.  LIBLTDL will
-# be prefixed with '${top_builddir}/' and LTDLINCL will be prefixed with
-# '${top_srcdir}/' (note the single quotes!).  If your package is not
-# flat and you're not using automake, define top_builddir and
-# top_srcdir appropriately in the Makefiles.
-AC_DEFUN([AC_LIBLTDL_CONVENIENCE],
-[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
-  case $enable_ltdl_convenience in
-  no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;;
-  "") enable_ltdl_convenience=yes
-      ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;;
-  esac
-  LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdlc.la
-  LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl'])
-  # For backwards non-gettext consistent compatibility...
-  INCLTDL="$LTDLINCL"
-])# AC_LIBLTDL_CONVENIENCE
-
-
-# AC_LIBLTDL_INSTALLABLE([DIRECTORY])
-# -----------------------------------
-# sets LIBLTDL to the link flags for the libltdl installable library and
-# LTDLINCL to the include flags for the libltdl header and adds
-# --enable-ltdl-install to the configure arguments.  Note that LIBLTDL
-# and LTDLINCL are not AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called.  If
-# DIRECTORY is not provided and an installed libltdl is not found, it is
-# assumed to be `libltdl'.  LIBLTDL will be prefixed with '${top_builddir}/'
-# and LTDLINCL will be prefixed with '${top_srcdir}/' (note the single
-# quotes!).  If your package is not flat and you're not using automake,
-# define top_builddir and top_srcdir appropriately in the Makefiles.
-# In the future, this macro may have to be called after AC_PROG_LIBTOOL.
-AC_DEFUN([AC_LIBLTDL_INSTALLABLE],
-[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
-  AC_CHECK_LIB(ltdl, lt_dlinit,
-  [test x"$enable_ltdl_install" != xyes && enable_ltdl_install=no],
-  [if test x"$enable_ltdl_install" = xno; then
-     AC_MSG_WARN([libltdl not installed, but installation disabled])
-   else
-     enable_ltdl_install=yes
-   fi
-  ])
-  if test x"$enable_ltdl_install" = x"yes"; then
-    ac_configure_args="$ac_configure_args --enable-ltdl-install"
-    LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdl.la
-    LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl'])
-  else
-    ac_configure_args="$ac_configure_args --enable-ltdl-install=no"
-    LIBLTDL="-lltdl"
-    LTDLINCL=
-  fi
-  # For backwards non-gettext consistent compatibility...
-  INCLTDL="$LTDLINCL"
-])# AC_LIBLTDL_INSTALLABLE
-
-
-# AC_LIBTOOL_CXX
-# --------------
-# enable support for C++ libraries
-AC_DEFUN([AC_LIBTOOL_CXX],
-[AC_REQUIRE([_LT_AC_LANG_CXX])
-])# AC_LIBTOOL_CXX
-
-
-# _LT_AC_LANG_CXX
-# ---------------
-AC_DEFUN([_LT_AC_LANG_CXX],
-[AC_REQUIRE([AC_PROG_CXX])
-AC_REQUIRE([AC_PROG_CXXCPP])
-_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}CXX])
-])# _LT_AC_LANG_CXX
-
-
-# AC_LIBTOOL_F77
-# --------------
-# enable support for Fortran 77 libraries
-AC_DEFUN([AC_LIBTOOL_F77],
-[AC_REQUIRE([_LT_AC_LANG_F77])
-])# AC_LIBTOOL_F77
-
-
-# _LT_AC_LANG_F77
-# ---------------
-AC_DEFUN([_LT_AC_LANG_F77],
-[AC_REQUIRE([AC_PROG_F77])
-_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}F77])
-])# _LT_AC_LANG_F77
-
-
-# AC_LIBTOOL_GCJ
-# --------------
-# enable support for GCJ libraries
-AC_DEFUN([AC_LIBTOOL_GCJ],
-[AC_REQUIRE([_LT_AC_LANG_GCJ])
-])# AC_LIBTOOL_GCJ
-
-
-# _LT_AC_LANG_GCJ
-# ---------------
-AC_DEFUN([_LT_AC_LANG_GCJ],
-[AC_PROVIDE_IFELSE([AC_PROG_GCJ],[],
-  [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],[],
-    [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ],[],
-      [ifdef([AC_PROG_GCJ],[AC_REQUIRE([AC_PROG_GCJ])],
-        [ifdef([A][M_PROG_GCJ],[AC_REQUIRE([A][M_PROG_GCJ])],
-          [AC_REQUIRE([A][C_PROG_GCJ_OR_A][M_PROG_GCJ])])])])])])
-_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}GCJ])
-])# _LT_AC_LANG_GCJ
-
-
-# AC_LIBTOOL_RC
-# --------------
-# enable support for Windows resource files
-AC_DEFUN([AC_LIBTOOL_RC],
-[AC_REQUIRE([LT_AC_PROG_RC])
-_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}RC])
-])# AC_LIBTOOL_RC
-
-
-# AC_LIBTOOL_LANG_C_CONFIG
-# ------------------------
-# Ensure that the configuration vars for the C compiler are
-# suitably defined.  Those variables are subsequently used by
-# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'.
-AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG], [_LT_AC_LANG_C_CONFIG])
-AC_DEFUN([_LT_AC_LANG_C_CONFIG],
-[lt_save_CC="$CC"
-AC_LANG_PUSH(C)
-
-# Source file extension for C test sources.
-ac_ext=c
-
-# Object file extension for compiled C test sources.
-objext=o
-_LT_AC_TAGVAR(objext, $1)=$objext
-
-# Code to be used in simple compile tests
-lt_simple_compile_test_code="int some_variable = 0;\n"
-
-# Code to be used in simple link tests
-lt_simple_link_test_code='int main(){return(0);}\n'
-
-_LT_AC_SYS_COMPILER
-
-#
-# Check for any special shared library compilation flags.
-#
-_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)=
-if test "$GCC" = no; then
-  case $host_os in
-  sco3.2v5*)
-    _LT_AC_TAGVAR(lt_prog_cc_shlib, $1)='-belf'
-    ;;
-  esac
-fi
-if test -n "$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)"; then
-  AC_MSG_WARN([`$CC' requires `$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)' to build shared libraries])
-  if echo "$old_CC $old_CFLAGS " | grep "[[    ]]$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)[[        ]]" >/dev/null; then :
-  else
-    AC_MSG_WARN([add `$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)' to the CC or CFLAGS env variable and reconfigure])
-    _LT_AC_TAGVAR(lt_cv_prog_cc_can_build_shared, $1)=no
-  fi
-fi
-
-
-#
-# Check to make sure the static flag actually works.
-#
-AC_LIBTOOL_LINKER_OPTION([if $compiler static flag $_LT_AC_TAGVAR(lt_prog_compiler_static, $1) works],
-  _LT_AC_TAGVAR(lt_prog_compiler_static_works, $1),
-  $_LT_AC_TAGVAR(lt_prog_compiler_static, $1),
-  [],
-  [_LT_AC_TAGVAR(lt_prog_compiler_static, $1)=])
-
-
-## CAVEAT EMPTOR:
-## There is no encapsulation within the following macros, do not change
-## the running order or otherwise move them around unless you know exactly
-## what you are doing...
-AC_LIBTOOL_PROG_COMPILER_NO_RTTI($1)
-AC_LIBTOOL_PROG_COMPILER_PIC($1)
-AC_LIBTOOL_PROG_CC_C_O($1)
-AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1)
-AC_LIBTOOL_PROG_LD_SHLIBS($1)
-AC_LIBTOOL_SYS_DYNAMIC_LINKER($1)
-AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1)
-AC_LIBTOOL_SYS_LIB_STRIP
-AC_LIBTOOL_DLOPEN_SELF($1)
-
-# Report which librarie types wil actually be built
-AC_MSG_CHECKING([if libtool supports shared libraries])
-AC_MSG_RESULT([$can_build_shared])
-
-AC_MSG_CHECKING([whether to build shared libraries])
-test "$can_build_shared" = "no" && enable_shared=no
-
-# On AIX, shared libraries and static libraries use the same namespace, and
-# are all built from PIC.
-case "$host_os" in
-aix3*)
-  test "$enable_shared" = yes && enable_static=no
-  if test -n "$RANLIB"; then
-    archive_cmds="$archive_cmds~\$RANLIB \$lib"
-    postinstall_cmds='$RANLIB $lib'
-  fi
-  ;;
-
-aix4* | aix5*)
-  if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
-    test "$enable_shared" = yes && enable_static=no
-  fi
-  ;;
-  darwin* | rhapsody*)
-  if test "$GCC" = yes; then
-    _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-    case "$host_os" in
-    rhapsody* | darwin1.[[012]])
-      _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined suppress'
-      ;;
-    *) # Darwin 1.3 on
-      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
-      else
-        case ${MACOSX_DEPLOYMENT_TARGET} in
-          10.[[012]])
-            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
-            ;;
-          10.*)
-            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined dynamic_lookup'
-            ;;
-        esac
-      fi
-      ;;
-    esac
-    output_verbose_link_cmd='echo'
-    _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs$compiler_flags -install_name $rpath/$soname $verstring'
-    _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
-    # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
-    _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag  -o $lib $libobjs $deplibs$compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
-    _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
-    _LT_AC_TAGVAR(hardcode_direct, $1)=no
-    _LT_AC_TAGVAR(hardcode_automatic, $1)=yes
-    _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
-    _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience'
-    _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
-  else
-    _LT_AC_TAGVAR(ld_shlibs, $1)=no
-  fi
-    ;;
-esac
-AC_MSG_RESULT([$enable_shared])
-
-AC_MSG_CHECKING([whether to build static libraries])
-# Make sure either enable_shared or enable_static is yes.
-test "$enable_shared" = yes || enable_static=yes
-AC_MSG_RESULT([$enable_static])
-
-AC_LIBTOOL_CONFIG($1)
-
-AC_LANG_POP
-CC="$lt_save_CC"
-])# AC_LIBTOOL_LANG_C_CONFIG
-
-
-# AC_LIBTOOL_LANG_CXX_CONFIG
-# --------------------------
-# Ensure that the configuration vars for the C compiler are
-# suitably defined.  Those variables are subsequently used by
-# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'.
-AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG], [_LT_AC_LANG_CXX_CONFIG(CXX)])
-AC_DEFUN([_LT_AC_LANG_CXX_CONFIG],
-[AC_LANG_PUSH(C++)
-AC_REQUIRE([AC_PROG_CXX])
-AC_REQUIRE([AC_PROG_CXXCPP])
-
-_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-_LT_AC_TAGVAR(allow_undefined_flag, $1)=
-_LT_AC_TAGVAR(always_export_symbols, $1)=no
-_LT_AC_TAGVAR(archive_expsym_cmds, $1)=
-_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=
-_LT_AC_TAGVAR(hardcode_direct, $1)=no
-_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=
-_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
-_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=
-_LT_AC_TAGVAR(hardcode_minus_L, $1)=no
-_LT_AC_TAGVAR(hardcode_automatic, $1)=no
-_LT_AC_TAGVAR(module_cmds, $1)=
-_LT_AC_TAGVAR(module_expsym_cmds, $1)=
-_LT_AC_TAGVAR(link_all_deplibs, $1)=unknown
-_LT_AC_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
-_LT_AC_TAGVAR(no_undefined_flag, $1)=
-_LT_AC_TAGVAR(whole_archive_flag_spec, $1)=
-_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no
-
-# Dependencies to place before and after the object being linked:
-_LT_AC_TAGVAR(predep_objects, $1)=
-_LT_AC_TAGVAR(postdep_objects, $1)=
-_LT_AC_TAGVAR(predeps, $1)=
-_LT_AC_TAGVAR(postdeps, $1)=
-_LT_AC_TAGVAR(compiler_lib_search_path, $1)=
-
-# Source file extension for C++ test sources.
-ac_ext=cc
-
-# Object file extension for compiled C++ test sources.
-objext=o
-_LT_AC_TAGVAR(objext, $1)=$objext
-
-# Code to be used in simple compile tests
-lt_simple_compile_test_code="int some_variable = 0;\n"
-
-# Code to be used in simple link tests
-lt_simple_link_test_code='int main(int, char *[]) { return(0); }\n'
-
-# ltmain only uses $CC for tagged configurations so make sure $CC is set.
-_LT_AC_SYS_COMPILER
-
-# Allow CC to be a program name with arguments.
-lt_save_CC=$CC
-lt_save_LD=$LD
-lt_save_GCC=$GCC
-GCC=$GXX
-lt_save_with_gnu_ld=$with_gnu_ld
-lt_save_path_LD=$lt_cv_path_LD
-if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
-  lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
-else
-  unset lt_cv_prog_gnu_ld
-fi
-if test -n "${lt_cv_path_LDCXX+set}"; then
-  lt_cv_path_LD=$lt_cv_path_LDCXX
-else
-  unset lt_cv_path_LD
-fi
-test -z "${LDCXX+set}" || LD=$LDCXX
-CC=${CXX-"c++"}
-compiler=$CC
-_LT_AC_TAGVAR(compiler, $1)=$CC
-cc_basename=`$echo X"$compiler" | $Xsed -e 's%^.*/%%'`
-
-# We don't want -fno-exception wen compiling C++ code, so set the
-# no_builtin_flag separately
-if test "$GXX" = yes; then
-  _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
-else
-  _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
-fi
-
-if test "$GXX" = yes; then
-  # Set up default GNU C++ configuration
-
-  AC_PROG_LD
-
-  # Check if GNU C++ uses GNU ld as the underlying linker, since the
-  # archiving commands below assume that GNU ld is being used.
-  if test "$with_gnu_ld" = yes; then
-    _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
-    _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-
-    _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
-    _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
-
-    # If archive_cmds runs LD, not CC, wlarc should be empty
-    # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
-    #     investigate it a little bit more. (MM)
-    wlarc='${wl}'
-
-    # ancient GNU ld didn't support --whole-archive et. al.
-    if eval "`$CC -print-prog-name=ld` --help 2>&1" | \
-       grep 'no-whole-archive' > /dev/null; then
-      _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
-    else
-      _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=
-    fi
-  else
-    with_gnu_ld=no
-    wlarc=
-
-    # A generic and very simple default shared library creation
-    # command for GNU C++ for the case where it uses the native
-    # linker, instead of GNU ld.  If possible, this setting should
-    # overridden to take advantage of the native linker features on
-    # the platform it is being used on.
-    _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
-  fi
-
-  # Commands to make compiler produce verbose output that lists
-  # what "hidden" libraries, object files and flags are used when
-  # linking a shared library.
-  output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"'
-
-else
-  GXX=no
-  with_gnu_ld=no
-  wlarc=
-fi
-
-# PORTME: fill in a description of your system's C++ link characteristics
-AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
-_LT_AC_TAGVAR(ld_shlibs, $1)=yes
-case $host_os in
-  aix3*)
-    # FIXME: insert proper C++ library support
-    _LT_AC_TAGVAR(ld_shlibs, $1)=no
-    ;;
-  aix4* | aix5*)
-    if test "$host_cpu" = ia64; then
-      # On IA64, the linker does run time linking by default, so we don't
-      # have to do anything special.
-      aix_use_runtimelinking=no
-      exp_sym_flag='-Bexport'
-      no_entry_flag=""
-    else
-      aix_use_runtimelinking=no
-
-      # Test if we are trying to use run time linking or normal
-      # AIX style linking. If -brtl is somewhere in LDFLAGS, we
-      # need to do runtime linking.
-      case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*)
-       for ld_flag in $LDFLAGS; do
-         case $ld_flag in
-         *-brtl*)
-           aix_use_runtimelinking=yes
-           break
-           ;;
-         esac
-       done
-      esac
-
-      exp_sym_flag='-bexport'
-      no_entry_flag='-bnoentry'
-    fi
-
-    # When large executables or shared objects are built, AIX ld can
-    # have problems creating the table of contents.  If linking a library
-    # or program results in "error TOC overflow" add -mminimal-toc to
-    # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
-    # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
-
-    _LT_AC_TAGVAR(archive_cmds, $1)=''
-    _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-    _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':'
-    _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
-
-    if test "$GXX" = yes; then
-      case $host_os in aix4.[012]|aix4.[012].*)
-      # We only want to do this on AIX 4.2 and lower, the check
-      # below for broken collect2 doesn't work under 4.3+
-       collect2name=`${CC} -print-prog-name=collect2`
-       if test -f "$collect2name" && \
-          strings "$collect2name" | grep resolve_lib_name >/dev/null
-       then
-         # We have reworked collect2
-         _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-       else
-         # We have old collect2
-         _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported
-         # It fails to find uninstalled libraries when the uninstalled
-         # path is not listed in the libpath.  Setting hardcode_minus_L
-         # to unsupported forces relinking
-         _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-         _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-         _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=
-       fi
-      esac
-      shared_flag='-shared'
-    else
-      # not using gcc
-      if test "$host_cpu" = ia64; then
-       # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
-       # chokes on -Wl,-G. The following line is correct:
-       shared_flag='-G'
-      else
-       if test "$aix_use_runtimelinking" = yes; then
-         shared_flag='${wl}-G'
-       else
-         shared_flag='${wl}-bM:SRE'
-       fi
-      fi
-    fi
-
-    # It seems that -bexpall does not export symbols beginning with
-    # underscore (_), so it is better to generate a list of symbols to export.
-    _LT_AC_TAGVAR(always_export_symbols, $1)=yes
-    if test "$aix_use_runtimelinking" = yes; then
-      # Warning - without using the other runtime loading flags (-brtl),
-      # -berok will link without error, but may produce a broken library.
-      _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok'
-      # Determine the default libpath from the value encoded in an empty executable.
-      _LT_AC_SYS_LIBPATH_AIX
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
-
-      _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag"
-     else
-      if test "$host_cpu" = ia64; then
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"
-      else
-       # Determine the default libpath from the value encoded in an empty executable.
-       _LT_AC_SYS_LIBPATH_AIX
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
-       # Warning - without using the other run time loading flags,
-       # -berok will link without error, but may produce a broken library.
-       _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
-       # -bexpall does not export symbols beginning with underscore (_)
-       _LT_AC_TAGVAR(always_export_symbols, $1)=yes
-       # Exported symbols can be pulled into shared objects from archives
-       _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=' '
-       _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes
-       # This is similar to how AIX traditionally builds it's shared libraries.
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
-      fi
-    fi
-    ;;
-  chorus*)
-    case $cc_basename in
-      *)
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-    esac
-    ;;
-
-  cygwin* | mingw* | pw32*)
-    # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
-    # as there is no search path for DLLs.
-    _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-    _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
-    _LT_AC_TAGVAR(always_export_symbols, $1)=no
-    _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
-
-    if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
-      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
-      # If the export-symbols file already is a .def file (1st line
-      # is EXPORTS), use it as is; otherwise, prepend...
-      _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
-       cp $export_symbols $output_objdir/$soname.def;
-      else
-       echo EXPORTS > $output_objdir/$soname.def;
-       cat $export_symbols >> $output_objdir/$soname.def;
-      fi~
-      $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
-    else
-      _LT_AC_TAGVAR(ld_shlibs, $1)=no
-    fi
-  ;;
-
-  darwin* | rhapsody*)
-  if test "$GXX" = yes; then
-    _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-    case "$host_os" in
-    rhapsody* | darwin1.[[012]])
-      _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined suppress'
-      ;;
-    *) # Darwin 1.3 on
-      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
-      else
-        case ${MACOSX_DEPLOYMENT_TARGET} in
-          10.[[012]])
-            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
-            ;;
-          10.*)
-            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined dynamic_lookup'
-            ;;
-        esac
-      fi
-      ;;
-    esac
-    lt_int_apple_cc_single_mod=no
-    output_verbose_link_cmd='echo'
-    if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then
-      lt_int_apple_cc_single_mod=yes
-    fi
-    if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
-      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
-    else
-      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
-    fi
-    _LT_AC_TAGVAR(module_cmds, $1)='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
-
-    # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
-    if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
-      _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
-    else
-      _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
-    fi
-    _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
-    _LT_AC_TAGVAR(hardcode_direct, $1)=no
-    _LT_AC_TAGVAR(hardcode_automatic, $1)=yes
-    _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
-    _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience'
-    _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
-  else
-    _LT_AC_TAGVAR(ld_shlibs, $1)=no
-  fi
-    ;;
-
-  dgux*)
-    case $cc_basename in
-      ec++)
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-      ghcx)
-       # Green Hills C++ Compiler
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-      *)
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-    esac
-    ;;
-  freebsd[12]*)
-    # C++ shared libraries reported to be fairly broken before switch to ELF
-    _LT_AC_TAGVAR(ld_shlibs, $1)=no
-    ;;
-  freebsd-elf*)
-    _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-    ;;
-  freebsd* | kfreebsd*-gnu)
-    # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
-    # conventions
-    _LT_AC_TAGVAR(ld_shlibs, $1)=yes
-    ;;
-  gnu*)
-    ;;
-  hpux9*)
-    _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-    _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-    _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-    _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-    _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
-                               # but as the default
-                               # location of the library.
-
-    case $cc_basename in
-    CC)
-      # FIXME: insert proper C++ library support
-      _LT_AC_TAGVAR(ld_shlibs, $1)=no
-      ;;
-    aCC)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
-      # Commands to make compiler produce verbose output that lists
-      # what "hidden" libraries, object files and flags are used when
-      # linking a shared library.
-      #
-      # There doesn't appear to be a way to prevent this compiler from
-      # explicitly linking system object files so we need to strip them
-      # from the output so that they don't get included in the library
-      # dependencies.
-      output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "[-]L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
-      ;;
-    *)
-      if test "$GXX" = yes; then
-        _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
-      else
-        # FIXME: insert proper C++ library support
-        _LT_AC_TAGVAR(ld_shlibs, $1)=no
-      fi
-      ;;
-    esac
-    ;;
-  hpux10*|hpux11*)
-    if test $with_gnu_ld = no; then
-      case "$host_cpu" in
-      hppa*64*)
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir'
-       _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-        ;;
-      ia64*)
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-        ;;
-      *)
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-       _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-       _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-        ;;
-      esac
-    fi
-    case "$host_cpu" in
-    hppa*64*)
-      _LT_AC_TAGVAR(hardcode_direct, $1)=no
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-    ia64*)
-      _LT_AC_TAGVAR(hardcode_direct, $1)=no
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
-                                             # but as the default
-                                             # location of the library.
-      ;;
-    *)
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
-                                             # but as the default
-                                             # location of the library.
-      ;;
-    esac
-
-    case $cc_basename in
-      CC)
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-      aCC)
-       case "$host_cpu" in
-       hppa*64*|ia64*)
-         _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs'
-         ;;
-       *)
-         _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-         ;;
-       esac
-       # Commands to make compiler produce verbose output that lists
-       # what "hidden" libraries, object files and flags are used when
-       # linking a shared library.
-       #
-       # There doesn't appear to be a way to prevent this compiler from
-       # explicitly linking system object files so we need to strip them
-       # from the output so that they don't get included in the library
-       # dependencies.
-       output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
-       ;;
-      *)
-       if test "$GXX" = yes; then
-         if test $with_gnu_ld = no; then
-           case "$host_cpu" in
-           ia64*|hppa*64*)
-             _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs'
-             ;;
-           *)
-             _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-             ;;
-           esac
-         fi
-       else
-         # FIXME: insert proper C++ library support
-         _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       fi
-       ;;
-    esac
-    ;;
-  irix5* | irix6*)
-    case $cc_basename in
-      CC)
-       # SGI C++
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
-
-       # Archives containing C++ object files must be created using
-       # "CC -ar", where "CC" is the IRIX C++ compiler.  This is
-       # necessary to make sure instantiated templates are included
-       # in the archive.
-       _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
-       ;;
-      *)
-       if test "$GXX" = yes; then
-         if test "$with_gnu_ld" = no; then
-           _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
-         else
-           _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` -o $lib'
-         fi
-       fi
-       _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
-       ;;
-    esac
-    _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-    _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-    ;;
-  linux*)
-    case $cc_basename in
-      KCC)
-       # Kuck and Associates, Inc. (KAI) C++ Compiler
-
-       # KCC will only create a shared library if the output file
-       # ends with ".so" (or ".sl" for HP-UX), so rename the library
-       # to its proper name (with version) after linking.
-       _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
-       # Commands to make compiler produce verbose output that lists
-       # what "hidden" libraries, object files and flags are used when
-       # linking a shared library.
-       #
-       # There doesn't appear to be a way to prevent this compiler from
-       # explicitly linking system object files so we need to strip them
-       # from the output so that they don't get included in the library
-       # dependencies.
-       output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | grep "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
-
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath,$libdir'
-       _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
-
-       # Archives containing C++ object files must be created using
-       # "CC -Bstatic", where "CC" is the KAI C++ compiler.
-       _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
-       ;;
-      icpc)
-       # Intel C++
-       with_gnu_ld=yes
-       _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-       _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
-       _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
-       ;;
-      cxx)
-       # Compaq C++
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname  -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
-
-       runpath_var=LD_RUN_PATH
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
-       _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-
-       # Commands to make compiler produce verbose output that lists
-       # what "hidden" libraries, object files and flags are used when
-       # linking a shared library.
-       #
-       # There doesn't appear to be a way to prevent this compiler from
-       # explicitly linking system object files so we need to strip them
-       # from the output so that they don't get included in the library
-       # dependencies.
-       output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
-       ;;
-    esac
-    ;;
-  lynxos*)
-    # FIXME: insert proper C++ library support
-    _LT_AC_TAGVAR(ld_shlibs, $1)=no
-    ;;
-  m88k*)
-    # FIXME: insert proper C++ library support
-    _LT_AC_TAGVAR(ld_shlibs, $1)=no
-    ;;
-  mvs*)
-    case $cc_basename in
-      cxx)
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-      *)
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-    esac
-    ;;
-  netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
-    if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
-      wlarc=
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-    fi
-    # Workaround some broken pre-1.5 toolchains
-    output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
-    ;;
-  osf3*)
-    case $cc_basename in
-      KCC)
-       # Kuck and Associates, Inc. (KAI) C++ Compiler
-
-       # KCC will only create a shared library if the output file
-       # ends with ".so" (or ".sl" for HP-UX), so rename the library
-       # to its proper name (with version) after linking.
-       _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
-
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-       _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-
-       # Archives containing C++ object files must be created using
-       # "CC -Bstatic", where "CC" is the KAI C++ compiler.
-       _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
-
-       ;;
-      RCC)
-       # Rational C++ 2.4.1
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-      cxx)
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && echo ${wl}-set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
-
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-       _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-
-       # Commands to make compiler produce verbose output that lists
-       # what "hidden" libraries, object files and flags are used when
-       # linking a shared library.
-       #
-       # There doesn't appear to be a way to prevent this compiler from
-       # explicitly linking system object files so we need to strip them
-       # from the output so that they don't get included in the library
-       # dependencies.
-       output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
-       ;;
-      *)
-       if test "$GXX" = yes && test "$with_gnu_ld" = no; then
-         _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
-         _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
-
-         _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-         _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-
-         # Commands to make compiler produce verbose output that lists
-         # what "hidden" libraries, object files and flags are used when
-         # linking a shared library.
-         output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"'
-
-       else
-         # FIXME: insert proper C++ library support
-         _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       fi
-       ;;
-    esac
-    ;;
-  osf4* | osf5*)
-    case $cc_basename in
-      KCC)
-       # Kuck and Associates, Inc. (KAI) C++ Compiler
-
-       # KCC will only create a shared library if the output file
-       # ends with ".so" (or ".sl" for HP-UX), so rename the library
-       # to its proper name (with version) after linking.
-       _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
-
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-       _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-
-       # Archives containing C++ object files must be created using
-       # the KAI C++ compiler.
-       _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs'
-       ;;
-      RCC)
-       # Rational C++ 2.4.1
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-      cxx)
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
-         echo "-hidden">> $lib.exp~
-         $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname -Wl,-input -Wl,$lib.exp  `test -n "$verstring" && echo -set_version $verstring` -update_registry $objdir/so_locations -o $lib~
-         $rm $lib.exp'
-
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
-       _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-
-       # Commands to make compiler produce verbose output that lists
-       # what "hidden" libraries, object files and flags are used when
-       # linking a shared library.
-       #
-       # There doesn't appear to be a way to prevent this compiler from
-       # explicitly linking system object files so we need to strip them
-       # from the output so that they don't get included in the library
-       # dependencies.
-       output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
-       ;;
-      *)
-       if test "$GXX" = yes && test "$with_gnu_ld" = no; then
-         _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
-        _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
-
-         _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-         _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-
-         # Commands to make compiler produce verbose output that lists
-         # what "hidden" libraries, object files and flags are used when
-         # linking a shared library.
-         output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"'
-
-       else
-         # FIXME: insert proper C++ library support
-         _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       fi
-       ;;
-    esac
-    ;;
-  psos*)
-    # FIXME: insert proper C++ library support
-    _LT_AC_TAGVAR(ld_shlibs, $1)=no
-    ;;
-  sco*)
-    _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-    case $cc_basename in
-      CC)
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-      *)
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-    esac
-    ;;
-  sunos4*)
-    case $cc_basename in
-      CC)
-       # Sun C++ 4.x
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-      lcc)
-       # Lucid
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-      *)
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-    esac
-    ;;
-  solaris*)
-    case $cc_basename in
-      CC)
-       # Sun C++ 4.2, 5.x and Centerline C++
-       _LT_AC_TAGVAR(no_undefined_flag, $1)=' -zdefs'
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -nolib -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
-       $CC -G${allow_undefined_flag} -nolib ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp'
-
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-       _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-       case $host_os in
-         solaris2.[0-5] | solaris2.[0-5].*) ;;
-         *)
-           # The C++ compiler is used as linker so we must use $wl
-           # flag to pass the commands to the underlying system
-           # linker.
-           # Supported since Solaris 2.6 (maybe 2.5.1?)
-           _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
-           ;;
-       esac
-       _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
-
-       # Commands to make compiler produce verbose output that lists
-       # what "hidden" libraries, object files and flags are used when
-       # linking a shared library.
-       #
-       # There doesn't appear to be a way to prevent this compiler from
-       # explicitly linking system object files so we need to strip them
-       # from the output so that they don't get included in the library
-       # dependencies.
-       output_verbose_link_cmd='templist=`$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep "\-[[LR]]"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
-
-       # Archives containing C++ object files must be created using
-       # "CC -xar", where "CC" is the Sun C++ compiler.  This is
-       # necessary to make sure instantiated templates are included
-       # in the archive.
-       _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
-       ;;
-      gcx)
-       # Green Hills C++ Compiler
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
-
-       # The C++ compiler must be used to create the archive.
-       _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
-       ;;
-      *)
-       # GNU C++ compiler with Solaris linker
-       if test "$GXX" = yes && test "$with_gnu_ld" = no; then
-         _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
-         if $CC --version | grep -v '^2\.7' > /dev/null; then
-           _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
-           _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
-               $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp'
-
-           # Commands to make compiler produce verbose output that lists
-           # what "hidden" libraries, object files and flags are used when
-           # linking a shared library.
-           output_verbose_link_cmd="$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\""
-         else
-           # g++ 2.7 appears to require `-G' NOT `-shared' on this
-           # platform.
-           _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
-           _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
-               $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp'
-
-           # Commands to make compiler produce verbose output that lists
-           # what "hidden" libraries, object files and flags are used when
-           # linking a shared library.
-           output_verbose_link_cmd="$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\""
-         fi
-
-         _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
-       fi
-       ;;
-    esac
-    ;;
-  sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7*)
-    _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-    ;;
-  tandem*)
-    case $cc_basename in
-      NCC)
-       # NonStop-UX NCC 3.20
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-      *)
-       # FIXME: insert proper C++ library support
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       ;;
-    esac
-    ;;
-  vxworks*)
-    # FIXME: insert proper C++ library support
-    _LT_AC_TAGVAR(ld_shlibs, $1)=no
-    ;;
-  *)
-    # FIXME: insert proper C++ library support
-    _LT_AC_TAGVAR(ld_shlibs, $1)=no
-    ;;
-esac
-AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)])
-test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
-
-_LT_AC_TAGVAR(GCC, $1)="$GXX"
-_LT_AC_TAGVAR(LD, $1)="$LD"
-
-## CAVEAT EMPTOR:
-## There is no encapsulation within the following macros, do not change
-## the running order or otherwise move them around unless you know exactly
-## what you are doing...
-AC_LIBTOOL_POSTDEP_PREDEP($1)
-AC_LIBTOOL_PROG_COMPILER_PIC($1)
-AC_LIBTOOL_PROG_CC_C_O($1)
-AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1)
-AC_LIBTOOL_PROG_LD_SHLIBS($1)
-AC_LIBTOOL_SYS_DYNAMIC_LINKER($1)
-AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1)
-AC_LIBTOOL_SYS_LIB_STRIP
-AC_LIBTOOL_DLOPEN_SELF($1)
-
-AC_LIBTOOL_CONFIG($1)
-
-AC_LANG_POP
-CC=$lt_save_CC
-LDCXX=$LD
-LD=$lt_save_LD
-GCC=$lt_save_GCC
-with_gnu_ldcxx=$with_gnu_ld
-with_gnu_ld=$lt_save_with_gnu_ld
-lt_cv_path_LDCXX=$lt_cv_path_LD
-lt_cv_path_LD=$lt_save_path_LD
-lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
-lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
-])# AC_LIBTOOL_LANG_CXX_CONFIG
-
-# AC_LIBTOOL_POSTDEP_PREDEP([TAGNAME])
-# ------------------------
-# Figure out "hidden" library dependencies from verbose
-# compiler output when linking a shared library.
-# Parse the compiler output and extract the necessary
-# objects, libraries and library flags.
-AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP],[
-dnl we can't use the lt_simple_compile_test_code here,
-dnl because it contains code intended for an executable,
-dnl not a library.  It's possible we should let each
-dnl tag define a new lt_????_link_test_code variable,
-dnl but it's only used here...
-ifelse([$1],[],[cat > conftest.$ac_ext <<EOF
-int a;
-void foo (void) { a = 0; }
-EOF
-],[$1],[CXX],[cat > conftest.$ac_ext <<EOF
-class Foo
-{
-public:
-  Foo (void) { a = 0; }
-private:
-  int a;
-};
-EOF
-],[$1],[F77],[cat > conftest.$ac_ext <<EOF
-      subroutine foo
-      implicit none
-      integer*4 a
-      a=0
-      return
-      end
-EOF
-],[$1],[GCJ],[cat > conftest.$ac_ext <<EOF
-public class foo {
-  private int a;
-  public void bar (void) {
-    a = 0;
-  }
-};
-EOF
-])
-dnl Parse the compiler output and extract the necessary
-dnl objects, libraries and library flags.
-if AC_TRY_EVAL(ac_compile); then
-  # Parse the compiler output and extract the necessary
-  # objects, libraries and library flags.
-
-  # Sentinel used to keep track of whether or not we are before
-  # the conftest object file.
-  pre_test_object_deps_done=no
-
-  # The `*' in the case matches for architectures that use `case' in
-  # $output_verbose_cmd can trigger glob expansion during the loop
-  # eval without this substitution.
-  output_verbose_link_cmd="`$echo \"X$output_verbose_link_cmd\" | $Xsed -e \"$no_glob_subst\"`"
-
-  for p in `eval $output_verbose_link_cmd`; do
-    case $p in
-
-    -L* | -R* | -l*)
-       # Some compilers place space between "-{L,R}" and the path.
-       # Remove the space.
-       if test $p = "-L" \
-         || test $p = "-R"; then
-        prev=$p
-        continue
-       else
-        prev=
-       fi
-
-       if test "$pre_test_object_deps_done" = no; then
-        case $p in
-        -L* | -R*)
-          # Internal compiler library paths should come after those
-          # provided the user.  The postdeps already come after the
-          # user supplied libs so there is no need to process them.
-          if test -z "$_LT_AC_TAGVAR(compiler_lib_search_path, $1)"; then
-            _LT_AC_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
-          else
-            _LT_AC_TAGVAR(compiler_lib_search_path, $1)="${_LT_AC_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
-          fi
-          ;;
-        # The "-l" case would never come before the object being
-        # linked, so don't bother handling this case.
-        esac
-       else
-        if test -z "$_LT_AC_TAGVAR(postdeps, $1)"; then
-          _LT_AC_TAGVAR(postdeps, $1)="${prev}${p}"
-        else
-          _LT_AC_TAGVAR(postdeps, $1)="${_LT_AC_TAGVAR(postdeps, $1)} ${prev}${p}"
-        fi
-       fi
-       ;;
-
-    *.$objext)
-       # This assumes that the test object file only shows up
-       # once in the compiler output.
-       if test "$p" = "conftest.$objext"; then
-        pre_test_object_deps_done=yes
-        continue
-       fi
-
-       if test "$pre_test_object_deps_done" = no; then
-        if test -z "$_LT_AC_TAGVAR(predep_objects, $1)"; then
-          _LT_AC_TAGVAR(predep_objects, $1)="$p"
-        else
-          _LT_AC_TAGVAR(predep_objects, $1)="$_LT_AC_TAGVAR(predep_objects, $1) $p"
-        fi
-       else
-        if test -z "$_LT_AC_TAGVAR(postdep_objects, $1)"; then
-          _LT_AC_TAGVAR(postdep_objects, $1)="$p"
-        else
-          _LT_AC_TAGVAR(postdep_objects, $1)="$_LT_AC_TAGVAR(postdep_objects, $1) $p"
-        fi
-       fi
-       ;;
-
-    *) ;; # Ignore the rest.
-
-    esac
-  done
-
-  # Clean up.
-  rm -f a.out a.exe
-else
-  echo "libtool.m4: error: problem compiling $1 test program"
-fi
-
-$rm -f confest.$objext
-
-case " $_LT_AC_TAGVAR(postdeps, $1) " in
-*" -lc "*) _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no ;;
-esac
-])# AC_LIBTOOL_POSTDEP_PREDEP
-
-# AC_LIBTOOL_LANG_F77_CONFIG
-# ------------------------
-# Ensure that the configuration vars for the C compiler are
-# suitably defined.  Those variables are subsequently used by
-# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'.
-AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG], [_LT_AC_LANG_F77_CONFIG(F77)])
-AC_DEFUN([_LT_AC_LANG_F77_CONFIG],
-[AC_REQUIRE([AC_PROG_F77])
-AC_LANG_PUSH(Fortran 77)
-
-_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-_LT_AC_TAGVAR(allow_undefined_flag, $1)=
-_LT_AC_TAGVAR(always_export_symbols, $1)=no
-_LT_AC_TAGVAR(archive_expsym_cmds, $1)=
-_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=
-_LT_AC_TAGVAR(hardcode_direct, $1)=no
-_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=
-_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
-_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=
-_LT_AC_TAGVAR(hardcode_minus_L, $1)=no
-_LT_AC_TAGVAR(hardcode_automatic, $1)=no
-_LT_AC_TAGVAR(module_cmds, $1)=
-_LT_AC_TAGVAR(module_expsym_cmds, $1)=
-_LT_AC_TAGVAR(link_all_deplibs, $1)=unknown
-_LT_AC_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
-_LT_AC_TAGVAR(no_undefined_flag, $1)=
-_LT_AC_TAGVAR(whole_archive_flag_spec, $1)=
-_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no
-
-# Source file extension for f77 test sources.
-ac_ext=f
-
-# Object file extension for compiled f77 test sources.
-objext=o
-_LT_AC_TAGVAR(objext, $1)=$objext
-
-# Code to be used in simple compile tests
-lt_simple_compile_test_code="      subroutine t\n      return\n      end\n"
-
-# Code to be used in simple link tests
-lt_simple_link_test_code="      program t\n      end\n"
-
-# ltmain only uses $CC for tagged configurations so make sure $CC is set.
-_LT_AC_SYS_COMPILER
-
-# Allow CC to be a program name with arguments.
-lt_save_CC="$CC"
-CC=${F77-"f77"}
-compiler=$CC
-_LT_AC_TAGVAR(compiler, $1)=$CC
-cc_basename=`$echo X"$compiler" | $Xsed -e 's%^.*/%%'`
-
-AC_MSG_CHECKING([if libtool supports shared libraries])
-AC_MSG_RESULT([$can_build_shared])
-
-AC_MSG_CHECKING([whether to build shared libraries])
-test "$can_build_shared" = "no" && enable_shared=no
-
-# On AIX, shared libraries and static libraries use the same namespace, and
-# are all built from PIC.
-case "$host_os" in
-aix3*)
-  test "$enable_shared" = yes && enable_static=no
-  if test -n "$RANLIB"; then
-    archive_cmds="$archive_cmds~\$RANLIB \$lib"
-    postinstall_cmds='$RANLIB $lib'
-  fi
-  ;;
-aix4* | aix5*)
-  test "$enable_shared" = yes && enable_static=no
-  ;;
-esac
-AC_MSG_RESULT([$enable_shared])
-
-AC_MSG_CHECKING([whether to build static libraries])
-# Make sure either enable_shared or enable_static is yes.
-test "$enable_shared" = yes || enable_static=yes
-AC_MSG_RESULT([$enable_static])
-
-test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
-
-_LT_AC_TAGVAR(GCC, $1)="$G77"
-_LT_AC_TAGVAR(LD, $1)="$LD"
-
-AC_LIBTOOL_PROG_COMPILER_PIC($1)
-AC_LIBTOOL_PROG_CC_C_O($1)
-AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1)
-AC_LIBTOOL_PROG_LD_SHLIBS($1)
-AC_LIBTOOL_SYS_DYNAMIC_LINKER($1)
-AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1)
-AC_LIBTOOL_SYS_LIB_STRIP
-
-
-AC_LIBTOOL_CONFIG($1)
-
-AC_LANG_POP
-CC="$lt_save_CC"
-])# AC_LIBTOOL_LANG_F77_CONFIG
-
-
-# AC_LIBTOOL_LANG_GCJ_CONFIG
-# --------------------------
-# Ensure that the configuration vars for the C compiler are
-# suitably defined.  Those variables are subsequently used by
-# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'.
-AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG], [_LT_AC_LANG_GCJ_CONFIG(GCJ)])
-AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG],
-[AC_LANG_SAVE
-
-# Source file extension for Java test sources.
-ac_ext=java
-
-# Object file extension for compiled Java test sources.
-objext=o
-_LT_AC_TAGVAR(objext, $1)=$objext
-
-# Code to be used in simple compile tests
-lt_simple_compile_test_code="class foo {}\n"
-
-# Code to be used in simple link tests
-lt_simple_link_test_code='public class conftest { public static void main(String[] argv) {}; }\n'
-
-# ltmain only uses $CC for tagged configurations so make sure $CC is set.
-_LT_AC_SYS_COMPILER
-
-# Allow CC to be a program name with arguments.
-lt_save_CC="$CC"
-CC=${GCJ-"gcj"}
-compiler=$CC
-_LT_AC_TAGVAR(compiler, $1)=$CC
-
-# GCJ did not exist at the time GCC didn't implicitly link libc in.
-_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-
-## CAVEAT EMPTOR:
-## There is no encapsulation within the following macros, do not change
-## the running order or otherwise move them around unless you know exactly
-## what you are doing...
-AC_LIBTOOL_PROG_COMPILER_NO_RTTI($1)
-AC_LIBTOOL_PROG_COMPILER_PIC($1)
-AC_LIBTOOL_PROG_CC_C_O($1)
-AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1)
-AC_LIBTOOL_PROG_LD_SHLIBS($1)
-AC_LIBTOOL_SYS_DYNAMIC_LINKER($1)
-AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1)
-AC_LIBTOOL_SYS_LIB_STRIP
-AC_LIBTOOL_DLOPEN_SELF($1)
-
-AC_LIBTOOL_CONFIG($1)
-
-AC_LANG_RESTORE
-CC="$lt_save_CC"
-])# AC_LIBTOOL_LANG_GCJ_CONFIG
-
-
-# AC_LIBTOOL_LANG_RC_CONFIG
-# --------------------------
-# Ensure that the configuration vars for the Windows resource compiler are
-# suitably defined.  Those variables are subsequently used by
-# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'.
-AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG], [_LT_AC_LANG_RC_CONFIG(RC)])
-AC_DEFUN([_LT_AC_LANG_RC_CONFIG],
-[AC_LANG_SAVE
-
-# Source file extension for RC test sources.
-ac_ext=rc
-
-# Object file extension for compiled RC test sources.
-objext=o
-_LT_AC_TAGVAR(objext, $1)=$objext
-
-# Code to be used in simple compile tests
-lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }\n'
-
-# Code to be used in simple link tests
-lt_simple_link_test_code="$lt_simple_compile_test_code"
-
-# ltmain only uses $CC for tagged configurations so make sure $CC is set.
-_LT_AC_SYS_COMPILER
-
-# Allow CC to be a program name with arguments.
-lt_save_CC="$CC"
-CC=${RC-"windres"}
-compiler=$CC
-_LT_AC_TAGVAR(compiler, $1)=$CC
-_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
-
-AC_LIBTOOL_CONFIG($1)
-
-AC_LANG_RESTORE
-CC="$lt_save_CC"
-])# AC_LIBTOOL_LANG_RC_CONFIG
-
-
-# AC_LIBTOOL_CONFIG([TAGNAME])
-# ----------------------------
-# If TAGNAME is not passed, then create an initial libtool script
-# with a default configuration from the untagged config vars.  Otherwise
-# add code to config.status for appending the configuration named by
-# TAGNAME from the matching tagged config vars.
-AC_DEFUN([AC_LIBTOOL_CONFIG],
-[# The else clause should only fire when bootstrapping the
-# libtool distribution, otherwise you forgot to ship ltmain.sh
-# with your package, and you will get complaints that there are
-# no rules to generate ltmain.sh.
-if test -f "$ltmain"; then
-  # See if we are running on zsh, and set the options which allow our commands through
-  # without removal of \ escapes.
-  if test -n "${ZSH_VERSION+set}" ; then
-    setopt NO_GLOB_SUBST
-  fi
-  # Now quote all the things that may contain metacharacters while being
-  # careful not to overquote the AC_SUBSTed values.  We take copies of the
-  # variables and quote the copies for generation of the libtool script.
-  for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \
-    SED SHELL STRIP \
-    libname_spec library_names_spec soname_spec extract_expsyms_cmds \
-    old_striplib striplib file_magic_cmd finish_cmds finish_eval \
-    deplibs_check_method reload_flag reload_cmds need_locks \
-    lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \
-    lt_cv_sys_global_symbol_to_c_name_address \
-    sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
-    old_postinstall_cmds old_postuninstall_cmds \
-    _LT_AC_TAGVAR(compiler, $1) \
-    _LT_AC_TAGVAR(CC, $1) \
-    _LT_AC_TAGVAR(LD, $1) \
-    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1) \
-    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1) \
-    _LT_AC_TAGVAR(lt_prog_compiler_static, $1) \
-    _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) \
-    _LT_AC_TAGVAR(export_dynamic_flag_spec, $1) \
-    _LT_AC_TAGVAR(thread_safe_flag_spec, $1) \
-    _LT_AC_TAGVAR(whole_archive_flag_spec, $1) \
-    _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1) \
-    _LT_AC_TAGVAR(old_archive_cmds, $1) \
-    _LT_AC_TAGVAR(old_archive_from_new_cmds, $1) \
-    _LT_AC_TAGVAR(predep_objects, $1) \
-    _LT_AC_TAGVAR(postdep_objects, $1) \
-    _LT_AC_TAGVAR(predeps, $1) \
-    _LT_AC_TAGVAR(postdeps, $1) \
-    _LT_AC_TAGVAR(compiler_lib_search_path, $1) \
-    _LT_AC_TAGVAR(archive_cmds, $1) \
-    _LT_AC_TAGVAR(archive_expsym_cmds, $1) \
-    _LT_AC_TAGVAR(postinstall_cmds, $1) \
-    _LT_AC_TAGVAR(postuninstall_cmds, $1) \
-    _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1) \
-    _LT_AC_TAGVAR(allow_undefined_flag, $1) \
-    _LT_AC_TAGVAR(no_undefined_flag, $1) \
-    _LT_AC_TAGVAR(export_symbols_cmds, $1) \
-    _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) \
-    _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1) \
-    _LT_AC_TAGVAR(hardcode_libdir_separator, $1) \
-    _LT_AC_TAGVAR(hardcode_automatic, $1) \
-    _LT_AC_TAGVAR(module_cmds, $1) \
-    _LT_AC_TAGVAR(module_expsym_cmds, $1) \
-    _LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1) \
-    _LT_AC_TAGVAR(exclude_expsyms, $1) \
-    _LT_AC_TAGVAR(include_expsyms, $1); do
-
-    case $var in
-    _LT_AC_TAGVAR(old_archive_cmds, $1) | \
-    _LT_AC_TAGVAR(old_archive_from_new_cmds, $1) | \
-    _LT_AC_TAGVAR(archive_cmds, $1) | \
-    _LT_AC_TAGVAR(archive_expsym_cmds, $1) | \
-    _LT_AC_TAGVAR(module_cmds, $1) | \
-    _LT_AC_TAGVAR(module_expsym_cmds, $1) | \
-    _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1) | \
-    _LT_AC_TAGVAR(export_symbols_cmds, $1) | \
-    extract_expsyms_cmds | reload_cmds | finish_cmds | \
-    postinstall_cmds | postuninstall_cmds | \
-    old_postinstall_cmds | old_postuninstall_cmds | \
-    sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
-      # Double-quote double-evaled strings.
-      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
-      ;;
-    *)
-      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
-      ;;
-    esac
-  done
-
-  case $lt_echo in
-  *'\[$]0 --fallback-echo"')
-    lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\[$]0 --fallback-echo"[$]/[$]0 --fallback-echo"/'`
-    ;;
-  esac
-
-ifelse([$1], [],
-  [cfgfile="${ofile}T"
-  trap "$rm \"$cfgfile\"; exit 1" 1 2 15
-  $rm -f "$cfgfile"
-  AC_MSG_NOTICE([creating $ofile])],
-  [cfgfile="$ofile"])
-
-  cat <<__EOF__ >> "$cfgfile"
-ifelse([$1], [],
-[#! $SHELL
-
-# `$echo "$cfgfile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
-# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP)
-# NOTE: Changes made to this file will be lost: look at ltmain.sh.
-#
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001
-# Free Software Foundation, Inc.
-#
-# This file is part of GNU Libtool:
-# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
-#
-# This program 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.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY 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 this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-# A sed program that does not truncate output.
-SED=$lt_SED
-
-# Sed that helps us avoid accidentally triggering echo(1) options like -n.
-Xsed="$SED -e s/^X//"
-
-# The HP-UX ksh and POSIX shell print the target directory to stdout
-# if CDPATH is set.
-if test "X\${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi
-
-# The names of the tagged configurations supported by this script.
-available_tags=
-
-# ### BEGIN LIBTOOL CONFIG],
-[# ### BEGIN LIBTOOL TAG CONFIG: $tagname])
-
-# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
-
-# Shell to use when invoking shell scripts.
-SHELL=$lt_SHELL
-
-# Whether or not to build shared libraries.
-build_libtool_libs=$enable_shared
-
-# Whether or not to build static libraries.
-build_old_libs=$enable_static
-
-# Whether or not to add -lc for building shared libraries.
-build_libtool_need_lc=$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)
-
-# Whether or not to disallow shared libs when runtime libs are static
-allow_libtool_libs_with_static_runtimes=$_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)
-
-# Whether or not to optimize for fast installation.
-fast_install=$enable_fast_install
-
-# The host system.
-host_alias=$host_alias
-host=$host
-
-# An echo program that does not interpret backslashes.
-echo=$lt_echo
-
-# The archiver.
-AR=$lt_AR
-AR_FLAGS=$lt_AR_FLAGS
-
-# A C compiler.
-LTCC=$lt_LTCC
-
-# A language-specific compiler.
-CC=$lt_[]_LT_AC_TAGVAR(compiler, $1)
-
-# Is the compiler the GNU C compiler?
-with_gcc=$_LT_AC_TAGVAR(GCC, $1)
-
-# An ERE matcher.
-EGREP=$lt_EGREP
-
-# The linker used to build libraries.
-LD=$lt_[]_LT_AC_TAGVAR(LD, $1)
-
-# Whether we need hard or soft links.
-LN_S=$lt_LN_S
-
-# A BSD-compatible nm program.
-NM=$lt_NM
-
-# A symbol stripping program
-STRIP=$lt_STRIP
-
-# Used to examine libraries when file_magic_cmd begins "file"
-MAGIC_CMD=$MAGIC_CMD
-
-# Used on cygwin: DLL creation program.
-DLLTOOL="$DLLTOOL"
-
-# Used on cygwin: object dumper.
-OBJDUMP="$OBJDUMP"
-
-# Used on cygwin: assembler.
-AS="$AS"
-
-# The name of the directory that contains temporary libtool files.
-objdir=$objdir
-
-# How to create reloadable object files.
-reload_flag=$lt_reload_flag
-reload_cmds=$lt_reload_cmds
-
-# How to pass a linker flag through the compiler.
-wl=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)
-
-# Object file suffix (normally "o").
-objext="$ac_objext"
-
-# Old archive suffix (normally "a").
-libext="$libext"
-
-# Shared library suffix (normally ".so").
-shrext_cmds='$shrext_cmds'
-
-# Executable file suffix (normally "").
-exeext="$exeext"
-
-# Additional compiler flags for building library objects.
-pic_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)
-pic_mode=$pic_mode
-
-# What is the maximum length of a command?
-max_cmd_len=$lt_cv_sys_max_cmd_len
-
-# Does compiler simultaneously support -c and -o options?
-compiler_c_o=$lt_[]_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)
-
-# Must we lock files when doing compilation ?
-need_locks=$lt_need_locks
-
-# Do we need the lib prefix for modules?
-need_lib_prefix=$need_lib_prefix
-
-# Do we need a version for libraries?
-need_version=$need_version
-
-# Whether dlopen is supported.
-dlopen_support=$enable_dlopen
-
-# Whether dlopen of programs is supported.
-dlopen_self=$enable_dlopen_self
-
-# Whether dlopen of statically linked programs is supported.
-dlopen_self_static=$enable_dlopen_self_static
-
-# Compiler flag to prevent dynamic linking.
-link_static_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_static, $1)
-
-# Compiler flag to turn off builtin functions.
-no_builtin_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)
-
-# Compiler flag to allow reflexive dlopens.
-export_dynamic_flag_spec=$lt_[]_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)
-
-# Compiler flag to generate shared objects directly from archives.
-whole_archive_flag_spec=$lt_[]_LT_AC_TAGVAR(whole_archive_flag_spec, $1)
-
-# Compiler flag to generate thread-safe objects.
-thread_safe_flag_spec=$lt_[]_LT_AC_TAGVAR(thread_safe_flag_spec, $1)
-
-# Library versioning type.
-version_type=$version_type
-
-# Format of library name prefix.
-libname_spec=$lt_libname_spec
-
-# List of archive names.  First name is the real one, the rest are links.
-# The last name is the one that the linker finds with -lNAME.
-library_names_spec=$lt_library_names_spec
-
-# The coded name of the library, if different from the real name.
-soname_spec=$lt_soname_spec
-
-# Commands used to build and install an old-style archive.
-RANLIB=$lt_RANLIB
-old_archive_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_cmds, $1)
-old_postinstall_cmds=$lt_old_postinstall_cmds
-old_postuninstall_cmds=$lt_old_postuninstall_cmds
-
-# Create an old-style archive from a shared archive.
-old_archive_from_new_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_new_cmds, $1)
-
-# Create a temporary old-style archive to link instead of a shared archive.
-old_archive_from_expsyms_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1)
-
-# Commands used to build and install a shared archive.
-archive_cmds=$lt_[]_LT_AC_TAGVAR(archive_cmds, $1)
-archive_expsym_cmds=$lt_[]_LT_AC_TAGVAR(archive_expsym_cmds, $1)
-postinstall_cmds=$lt_postinstall_cmds
-postuninstall_cmds=$lt_postuninstall_cmds
-
-# Commands used to build a loadable module (assumed same as above if empty)
-module_cmds=$lt_[]_LT_AC_TAGVAR(module_cmds, $1)
-module_expsym_cmds=$lt_[]_LT_AC_TAGVAR(module_expsym_cmds, $1)
-
-# Commands to strip libraries.
-old_striplib=$lt_old_striplib
-striplib=$lt_striplib
-
-# Dependencies to place before the objects being linked to create a
-# shared library.
-predep_objects=$lt_[]_LT_AC_TAGVAR(predep_objects, $1)
-
-# Dependencies to place after the objects being linked to create a
-# shared library.
-postdep_objects=$lt_[]_LT_AC_TAGVAR(postdep_objects, $1)
-
-# Dependencies to place before the objects being linked to create a
-# shared library.
-predeps=$lt_[]_LT_AC_TAGVAR(predeps, $1)
-
-# Dependencies to place after the objects being linked to create a
-# shared library.
-postdeps=$lt_[]_LT_AC_TAGVAR(postdeps, $1)
-
-# The library search path used internally by the compiler when linking
-# a shared library.
-compiler_lib_search_path=$lt_[]_LT_AC_TAGVAR(compiler_lib_search_path, $1)
-
-# Method to check whether dependent libraries are shared objects.
-deplibs_check_method=$lt_deplibs_check_method
-
-# Command to use when deplibs_check_method == file_magic.
-file_magic_cmd=$lt_file_magic_cmd
-
-# Flag that allows shared libraries with undefined symbols to be built.
-allow_undefined_flag=$lt_[]_LT_AC_TAGVAR(allow_undefined_flag, $1)
-
-# Flag that forces no undefined symbols.
-no_undefined_flag=$lt_[]_LT_AC_TAGVAR(no_undefined_flag, $1)
-
-# Commands used to finish a libtool library installation in a directory.
-finish_cmds=$lt_finish_cmds
-
-# Same as above, but a single script fragment to be evaled but not shown.
-finish_eval=$lt_finish_eval
-
-# Take the output of nm and produce a listing of raw symbols and C names.
-global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
-
-# Transform the output of nm in a proper C declaration
-global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
-
-# Transform the output of nm in a C name address pair
-global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
-
-# This is the shared library runtime path variable.
-runpath_var=$runpath_var
-
-# This is the shared library path variable.
-shlibpath_var=$shlibpath_var
-
-# Is shlibpath searched before the hard-coded library search path?
-shlibpath_overrides_runpath=$shlibpath_overrides_runpath
-
-# How to hardcode a shared library path into an executable.
-hardcode_action=$_LT_AC_TAGVAR(hardcode_action, $1)
-
-# Whether we should hardcode library paths into libraries.
-hardcode_into_libs=$hardcode_into_libs
-
-# Flag to hardcode \$libdir into a binary during linking.
-# This must work even if \$libdir does not exist.
-hardcode_libdir_flag_spec=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)
-
-# If ld is used when linking, flag to hardcode \$libdir into
-# a binary during linking. This must work even if \$libdir does
-# not exist.
-hardcode_libdir_flag_spec_ld=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)
-
-# Whether we need a single -rpath flag with a separated argument.
-hardcode_libdir_separator=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_separator, $1)
-
-# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the
-# resulting binary.
-hardcode_direct=$_LT_AC_TAGVAR(hardcode_direct, $1)
-
-# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
-# resulting binary.
-hardcode_minus_L=$_LT_AC_TAGVAR(hardcode_minus_L, $1)
-
-# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
-# the resulting binary.
-hardcode_shlibpath_var=$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)
-
-# Set to yes if building a shared library automatically hardcodes DIR into the library
-# and all subsequent libraries and executables linked against it.
-hardcode_automatic=$_LT_AC_TAGVAR(hardcode_automatic, $1)
-
-# Variables whose values should be saved in libtool wrapper scripts and
-# restored at relink time.
-variables_saved_for_relink="$variables_saved_for_relink"
-
-# Whether libtool must link a program against all its dependency libraries.
-link_all_deplibs=$_LT_AC_TAGVAR(link_all_deplibs, $1)
-
-# Compile-time system search path for libraries
-sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
-
-# Run-time system search path for libraries
-sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
-
-# Fix the shell variable \$srcfile for the compiler.
-fix_srcfile_path="$_LT_AC_TAGVAR(fix_srcfile_path, $1)"
-
-# Set to yes if exported symbols are required.
-always_export_symbols=$_LT_AC_TAGVAR(always_export_symbols, $1)
-
-# The commands to list exported symbols.
-export_symbols_cmds=$lt_[]_LT_AC_TAGVAR(export_symbols_cmds, $1)
-
-# The commands to extract the exported symbol list from a shared archive.
-extract_expsyms_cmds=$lt_extract_expsyms_cmds
-
-# Symbols that should not be listed in the preloaded symbols.
-exclude_expsyms=$lt_[]_LT_AC_TAGVAR(exclude_expsyms, $1)
-
-# Symbols that must always be exported.
-include_expsyms=$lt_[]_LT_AC_TAGVAR(include_expsyms, $1)
-
-ifelse([$1],[],
-[# ### END LIBTOOL CONFIG],
-[# ### END LIBTOOL TAG CONFIG: $tagname])
-
-__EOF__
-
-ifelse([$1],[], [
-  case $host_os in
-  aix3*)
-    cat <<\EOF >> "$cfgfile"
-
-# AIX sometimes has problems with the GCC collect2 program.  For some
-# reason, if we set the COLLECT_NAMES environment variable, the problems
-# vanish in a puff of smoke.
-if test "X${COLLECT_NAMES+set}" != Xset; then
-  COLLECT_NAMES=
-  export COLLECT_NAMES
-fi
-EOF
-    ;;
-  esac
-
-  # We use sed instead of cat because bash on DJGPP gets confused if
-  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
-  # text mode, it properly converts lines to CR/LF.  This bash problem
-  # is reportedly fixed, but why not run on old versions too?
-  sed '$q' "$ltmain" >> "$cfgfile" || (rm -f "$cfgfile"; exit 1)
-
-  mv -f "$cfgfile" "$ofile" || \
-    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
-  chmod +x "$ofile"
-])
-else
-  # If there is no Makefile yet, we rely on a make rule to execute
-  # `config.status --recheck' to rerun these tests and create the
-  # libtool script then.
-  ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'`
-  if test -f "$ltmain_in"; then
-    test -f Makefile && make "$ltmain"
-  fi
-fi
-])# AC_LIBTOOL_CONFIG
-
-
-# AC_LIBTOOL_PROG_COMPILER_NO_RTTI([TAGNAME])
-# -------------------------------------------
-AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI],
-[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl
-
-_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
-
-if test "$GCC" = yes; then
-  _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
-
-  AC_LIBTOOL_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
-    lt_cv_prog_compiler_rtti_exceptions,
-    [-fno-rtti -fno-exceptions], [],
-    [_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
-fi
-])# AC_LIBTOOL_PROG_COMPILER_NO_RTTI
-
-
-# AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE
-# ---------------------------------
-AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE],
-[AC_REQUIRE([AC_CANONICAL_HOST])
-AC_REQUIRE([AC_PROG_NM])
-AC_REQUIRE([AC_OBJEXT])
-# Check for command to grab the raw symbol name followed by C symbol from nm.
-AC_MSG_CHECKING([command to parse $NM output from $compiler object])
-AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
-[
-# These are sane defaults that work on at least a few old systems.
-# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
-
-# Character class describing NM global symbol codes.
-symcode='[[BCDEGRST]]'
-
-# Regexp to match symbols that can be accessed directly from C.
-sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
-
-# Transform the above into a raw symbol and a C symbol.
-symxfrm='\1 \2\3 \3'
-
-# Transform an extracted symbol line into a proper C declaration
-lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern int \1;/p'"
-
-# Transform an extracted symbol line into symbol name and symbol address
-lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/  {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (lt_ptr) \&\2},/p'"
-
-# Define system-specific variables.
-case $host_os in
-aix*)
-  symcode='[[BCDT]]'
-  ;;
-cygwin* | mingw* | pw32*)
-  symcode='[[ABCDGISTW]]'
-  ;;
-hpux*) # Its linker distinguishes data from code symbols
-  if test "$host_cpu" = ia64; then
-    symcode='[[ABCDEGRST]]'
-  fi
-  lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
-  lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/  {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (lt_ptr) \&\2},/p'"
-  ;;
-irix* | nonstopux*)
-  symcode='[[BCDEGRST]]'
-  ;;
-osf*)
-  symcode='[[BCDEGQRST]]'
-  ;;
-solaris* | sysv5*)
-  symcode='[[BDRT]]'
-  ;;
-sysv4)
-  symcode='[[DFNSTU]]'
-  ;;
-esac
-
-# Handle CRLF in mingw tool chain
-opt_cr=
-case $build_os in
-mingw*)
-  opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp
-  ;;
-esac
-
-# If we're using GNU nm, then use its standard symbol codes.
-case `$NM -V 2>&1` in
-*GNU* | *'with BFD'*)
-  symcode='[[ABCDGIRSTW]]' ;;
-esac
-
-# Try without a prefix undercore, then with it.
-for ac_symprfx in "" "_"; do
-
-  # Write the raw and C identifiers.
-  lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[     ]]\($symcode$symcode*\)[[       ]][[    ]]*\($ac_symprfx\)$sympat$opt_cr$/$symxfrm/p'"
-
-  # Check to see that the pipe works correctly.
-  pipe_works=no
-
-  rm -f conftest*
-  cat > conftest.$ac_ext <<EOF
-#ifdef __cplusplus
-extern "C" {
-#endif
-char nm_test_var;
-void nm_test_func(){}
-#ifdef __cplusplus
-}
-#endif
-int main(){nm_test_var='a';nm_test_func();return(0);}
-EOF
-
-  if AC_TRY_EVAL(ac_compile); then
-    # Now try to grab the symbols.
-    nlist=conftest.nm
-    if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then
-      # Try sorting and uniquifying the output.
-      if sort "$nlist" | uniq > "$nlist"T; then
-       mv -f "$nlist"T "$nlist"
-      else
-       rm -f "$nlist"T
-      fi
-
-      # Make sure that we snagged all the symbols we need.
-      if grep ' nm_test_var$' "$nlist" >/dev/null; then
-       if grep ' nm_test_func$' "$nlist" >/dev/null; then
-         cat <<EOF > conftest.$ac_ext
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-EOF
-         # Now generate the symbol file.
-         eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | grep -v main >> conftest.$ac_ext'
-
-         cat <<EOF >> conftest.$ac_ext
-#if defined (__STDC__) && __STDC__
-# define lt_ptr_t void *
-#else
-# define lt_ptr_t char *
-# define const
-#endif
-
-/* The mapping between symbol names and symbols. */
-const struct {
-  const char *name;
-  lt_ptr_t address;
-}
-lt_preloaded_symbols[[]] =
-{
-EOF
-         $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (lt_ptr_t) \&\2},/" < "$nlist" | grep -v main >> conftest.$ac_ext
-         cat <<\EOF >> conftest.$ac_ext
-  {0, (lt_ptr_t) 0}
-};
-
-#ifdef __cplusplus
-}
-#endif
-EOF
-         # Now try linking the two files.
-         mv conftest.$ac_objext conftstm.$ac_objext
-         lt_save_LIBS="$LIBS"
-         lt_save_CFLAGS="$CFLAGS"
-         LIBS="conftstm.$ac_objext"
-         CFLAGS="$CFLAGS$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
-         if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
-           pipe_works=yes
-         fi
-         LIBS="$lt_save_LIBS"
-         CFLAGS="$lt_save_CFLAGS"
-       else
-         echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
-       fi
-      else
-       echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
-      fi
-    else
-      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
-    fi
-  else
-    echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
-    cat conftest.$ac_ext >&5
-  fi
-  rm -f conftest* conftst*
-
-  # Do not use the global_symbol_pipe unless it works.
-  if test "$pipe_works" = yes; then
-    break
-  else
-    lt_cv_sys_global_symbol_pipe=
-  fi
-done
-])
-if test -z "$lt_cv_sys_global_symbol_pipe"; then
-  lt_cv_sys_global_symbol_to_cdecl=
-fi
-if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
-  AC_MSG_RESULT(failed)
-else
-  AC_MSG_RESULT(ok)
-fi
-]) # AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE
-
-
-# AC_LIBTOOL_PROG_COMPILER_PIC([TAGNAME])
-# ---------------------------------------
-AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC],
-[_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)=
-_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
-_LT_AC_TAGVAR(lt_prog_compiler_static, $1)=
-
-AC_MSG_CHECKING([for $compiler option to produce PIC])
- ifelse([$1],[CXX],[
-  # C++ specific cases for pic, static, wl, etc.
-  if test "$GXX" = yes; then
-    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static'
-
-    case $host_os in
-    aix*)
-      # All AIX code is PIC.
-      if test "$host_cpu" = ia64; then
-       # AIX 5 now supports IA64 processor
-       _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      fi
-      ;;
-    amigaos*)
-      # FIXME: we need at least 68020 code to build shared libraries, but
-      # adding the `-m68020' flag to GCC prevents building anything better,
-      # like `-m68040'.
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
-      ;;
-    beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
-      # PIC is the default for these OSes.
-      ;;
-    mingw* | os2* | pw32*)
-      # This hack is so that the source file can tell whether it is being
-      # built for inclusion in a dll (and should export symbols for example).
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'
-      ;;
-    darwin* | rhapsody*)
-      # PIC is the default on this platform
-      # Common symbols not allowed in MH_DYLIB files
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
-      ;;
-    *djgpp*)
-      # DJGPP does not support shared libraries at all
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
-      ;;
-    sysv4*MP*)
-      if test -d /usr/nec; then
-       _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
-      fi
-      ;;
-    hpux*)
-      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
-      # not for PA HP-UX.
-      case "$host_cpu" in
-      hppa*64*|ia64*)
-       ;;
-      *)
-       _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-       ;;
-      esac
-      ;;
-    *)
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-      ;;
-    esac
-  else
-    case $host_os in
-      aix4* | aix5*)
-       # All AIX code is PIC.
-       if test "$host_cpu" = ia64; then
-         # AIX 5 now supports IA64 processor
-         _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-       else
-         _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
-       fi
-       ;;
-      chorus*)
-       case $cc_basename in
-       cxch68)
-         # Green Hills C++ Compiler
-         # _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
-         ;;
-       esac
-       ;;
-      dgux*)
-       case $cc_basename in
-         ec++)
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-           ;;
-         ghcx)
-           # Green Hills C++ Compiler
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      freebsd* | kfreebsd*-gnu)
-       # FreeBSD uses GNU C++
-       ;;
-      hpux9* | hpux10* | hpux11*)
-       case $cc_basename in
-         CC)
-           _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive"
-           if test "$host_cpu" != ia64; then
-             _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
-           fi
-           ;;
-         aCC)
-           _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive"
-           case "$host_cpu" in
-           hppa*64*|ia64*)
-             # +Z the default
-             ;;
-           *)
-             _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
-             ;;
-           esac
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      irix5* | irix6* | nonstopux*)
-       case $cc_basename in
-         CC)
-           _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-           # CC pic flag -KPIC is the default.
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      linux*)
-       case $cc_basename in
-         KCC)
-           # KAI C++ Compiler
-           _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-           ;;
-         icpc)
-           # Intel C++
-           _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-           _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static'
-           ;;
-         cxx)
-           # Compaq C++
-           # Make sure the PIC flag is empty.  It appears that all Alpha
-           # Linux and Compaq Tru64 Unix objects are PIC.
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
-           _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      lynxos*)
-       ;;
-      m88k*)
-       ;;
-      mvs*)
-       case $cc_basename in
-         cxx)
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
-       ;;
-      osf3* | osf4* | osf5*)
-       case $cc_basename in
-         KCC)
-           _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
-           ;;
-         RCC)
-           # Rational C++ 2.4.1
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
-           ;;
-         cxx)
-           # Digital/Compaq C++
-           _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-           # Make sure the PIC flag is empty.  It appears that all Alpha
-           # Linux and Compaq Tru64 Unix objects are PIC.
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
-           _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      psos*)
-       ;;
-      sco*)
-       case $cc_basename in
-         CC)
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      solaris*)
-       case $cc_basename in
-         CC)
-           # Sun C++ 4.2, 5.x and Centerline C++
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-           _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-           _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
-           ;;
-         gcx)
-           # Green Hills C++ Compiler
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      sunos4*)
-       case $cc_basename in
-         CC)
-           # Sun C++ 4.x
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
-           _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-           ;;
-         lcc)
-           # Lucid
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      tandem*)
-       case $cc_basename in
-         NCC)
-           # NonStop-UX NCC 3.20
-           _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-           ;;
-         *)
-           ;;
-       esac
-       ;;
-      unixware*)
-       ;;
-      vxworks*)
-       ;;
-      *)
-       _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
-       ;;
-    esac
-  fi
-],
-[
-  if test "$GCC" = yes; then
-    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static'
-
-    case $host_os in
-      aix*)
-      # All AIX code is PIC.
-      if test "$host_cpu" = ia64; then
-       # AIX 5 now supports IA64 processor
-       _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      fi
-      ;;
-
-    amigaos*)
-      # FIXME: we need at least 68020 code to build shared libraries, but
-      # adding the `-m68020' flag to GCC prevents building anything better,
-      # like `-m68040'.
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
-      ;;
-
-    beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
-      # PIC is the default for these OSes.
-      ;;
-
-    mingw* | pw32* | os2*)
-      # This hack is so that the source file can tell whether it is being
-      # built for inclusion in a dll (and should export symbols for example).
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'
-      ;;
-
-    darwin* | rhapsody*)
-      # PIC is the default on this platform
-      # Common symbols not allowed in MH_DYLIB files
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
-      ;;
-
-    msdosdjgpp*)
-      # Just because we use GCC doesn't mean we suddenly get shared libraries
-      # on systems that don't support them.
-      _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
-      enable_shared=no
-      ;;
-
-    sysv4*MP*)
-      if test -d /usr/nec; then
-       _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
-      fi
-      ;;
-
-    hpux*)
-      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
-      # not for PA HP-UX.
-      case "$host_cpu" in
-      hppa*64*|ia64*)
-       # +Z the default
-       ;;
-      *)
-       _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-       ;;
-      esac
-      ;;
-
-    *)
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
-      ;;
-    esac
-  else
-    # PORTME Check for flag to pass linker flags through the system compiler.
-    case $host_os in
-    aix*)
-      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      if test "$host_cpu" = ia64; then
-       # AIX 5 now supports IA64 processor
-       _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      else
-       _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
-      fi
-      ;;
-
-    mingw* | pw32* | os2*)
-      # This hack is so that the source file can tell whether it is being
-      # built for inclusion in a dll (and should export symbols for example).
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'
-      ;;
-
-    hpux9* | hpux10* | hpux11*)
-      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
-      # not for PA HP-UX.
-      case "$host_cpu" in
-      hppa*64*|ia64*)
-       # +Z the default
-       ;;
-      *)
-       _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
-       ;;
-      esac
-      # Is there a better lt_prog_compiler_static that works with the bundled CC?
-      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
-      ;;
-
-    irix5* | irix6* | nonstopux*)
-      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      # PIC (with -KPIC) is the default.
-      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-      ;;
-
-    newsos6)
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      ;;
-
-    linux*)
-      case $CC in
-      icc* | ecc*)
-       _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-       _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-       _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static'
-        ;;
-      ccc*)
-        _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-        # All Alpha code is PIC.
-        _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-        ;;
-      esac
-      ;;
-
-    osf3* | osf4* | osf5*)
-      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      # All OSF/1 code is PIC.
-      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
-      ;;
-
-    sco3.2v5*)
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kpic'
-      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-dn'
-      ;;
-
-    solaris*)
-      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      ;;
-
-    sunos4*)
-      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
-      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      ;;
-
-    sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
-      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
-      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      ;;
-
-    sysv4*MP*)
-      if test -d /usr/nec ;then
-       _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
-       _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      fi
-      ;;
-
-    uts4*)
-      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
-      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
-      ;;
-
-    *)
-      _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
-      ;;
-    esac
-  fi
-])
-AC_MSG_RESULT([$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)])
-
-#
-# Check to make sure the PIC flag actually works.
-#
-if test -n "$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)"; then
-  AC_LIBTOOL_COMPILER_OPTION([if $compiler PIC flag $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) works],
-    _LT_AC_TAGVAR(lt_prog_compiler_pic_works, $1),
-    [$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])], [],
-    [case $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) in
-     "" | " "*) ;;
-     *) _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)" ;;
-     esac],
-    [_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
-     _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
-fi
-case "$host_os" in
-  # For platforms which do not support PIC, -DPIC is meaningless:
-  *djgpp*)
-    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
-    ;;
-  *)
-    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])"
-    ;;
-esac
-])
-
-
-# AC_LIBTOOL_PROG_LD_SHLIBS([TAGNAME])
-# ------------------------------------
-# See if the linker supports building shared libraries.
-AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS],
-[AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
-ifelse([$1],[CXX],[
-  _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
-  case $host_os in
-  aix4* | aix5*)
-    # If we're using GNU nm, then we don't want the "-C" option.
-    # -C means demangle to AIX nm, but means don't demangle with GNU nm
-    if $NM -V 2>&1 | grep 'GNU' > /dev/null; then
-      _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols'
-    else
-      _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols'
-    fi
-    ;;
-  pw32*)
-    _LT_AC_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
-  ;;
-  cygwin* | mingw*)
-    _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols'
-  ;;
-  linux*)
-    _LT_AC_TAGVAR(link_all_deplibs, $1)=no
-  ;;
-  *)
-    _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
-  ;;
-  esac
-],[
-  runpath_var=
-  _LT_AC_TAGVAR(allow_undefined_flag, $1)=
-  _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no
-  _LT_AC_TAGVAR(archive_cmds, $1)=
-  _LT_AC_TAGVAR(archive_expsym_cmds, $1)=
-  _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)=
-  _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1)=
-  _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=
-  _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=
-  _LT_AC_TAGVAR(thread_safe_flag_spec, $1)=
-  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=
-  _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
-  _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=
-  _LT_AC_TAGVAR(hardcode_direct, $1)=no
-  _LT_AC_TAGVAR(hardcode_minus_L, $1)=no
-  _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
-  _LT_AC_TAGVAR(link_all_deplibs, $1)=unknown
-  _LT_AC_TAGVAR(hardcode_automatic, $1)=no
-  _LT_AC_TAGVAR(module_cmds, $1)=
-  _LT_AC_TAGVAR(module_expsym_cmds, $1)=
-  _LT_AC_TAGVAR(always_export_symbols, $1)=no
-  _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
-  # include_expsyms should be a list of space-separated symbols to be *always*
-  # included in the symbol list
-  _LT_AC_TAGVAR(include_expsyms, $1)=
-  # exclude_expsyms can be an extended regexp of symbols to exclude
-  # it will be wrapped by ` (' and `)$', so one must not match beginning or
-  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
-  # as well as any symbol that contains `d'.
-  _LT_AC_TAGVAR(exclude_expsyms, $1)="_GLOBAL_OFFSET_TABLE_"
-  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
-  # platforms (ab)use it in PIC code, but their linkers get confused if
-  # the symbol is explicitly referenced.  Since portable code cannot
-  # rely on this symbol name, it's probably fine to never include it in
-  # preloaded symbol tables.
-  extract_expsyms_cmds=
-
-  case $host_os in
-  cygwin* | mingw* | pw32*)
-    # FIXME: the MSVC++ port hasn't been tested in a loooong time
-    # When not using gcc, we currently assume that we are using
-    # Microsoft Visual C++.
-    if test "$GCC" != yes; then
-      with_gnu_ld=no
-    fi
-    ;;
-  openbsd*)
-    with_gnu_ld=no
-    ;;
-  esac
-
-  _LT_AC_TAGVAR(ld_shlibs, $1)=yes
-  if test "$with_gnu_ld" = yes; then
-    # If archive_cmds runs LD, not CC, wlarc should be empty
-    wlarc='${wl}'
-
-    # See if GNU ld supports shared libraries.
-    case $host_os in
-    aix3* | aix4* | aix5*)
-      # On AIX/PPC, the GNU linker is very broken
-      if test "$host_cpu" != ia64; then
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       cat <<EOF 1>&2
-
-*** Warning: the GNU linker, at least up to release 2.9.1, is reported
-*** to be unable to reliably create shared libraries on AIX.
-*** Therefore, libtool is disabling shared libraries support.  If you
-*** really care for shared libraries, you may want to modify your PATH
-*** so that a non-GNU linker is found, and then restart.
-
-EOF
-      fi
-      ;;
-
-    amigaos*)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-
-      # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
-      # that the semantics of dynamic libraries on AmigaOS, at least up
-      # to version 4, is to share data among multiple programs linked
-      # with the same dynamic library.  Since this doesn't match the
-      # behavior of shared libraries on other platforms, we can't use
-      # them.
-      _LT_AC_TAGVAR(ld_shlibs, $1)=no
-      ;;
-
-    beos*)
-      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
-       # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
-       # support --undefined.  This deserves some investigation.  FIXME
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-      else
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-      fi
-      ;;
-
-    cygwin* | mingw* | pw32*)
-      # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
-      # as there is no search path for DLLs.
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
-      _LT_AC_TAGVAR(always_export_symbols, $1)=no
-      _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
-      _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols'
-
-      if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
-        _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
-       # If the export-symbols file already is a .def file (1st line
-       # is EXPORTS), use it as is; otherwise, prepend...
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
-         cp $export_symbols $output_objdir/$soname.def;
-       else
-         echo EXPORTS > $output_objdir/$soname.def;
-         cat $export_symbols >> $output_objdir/$soname.def;
-       fi~
-       $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000  ${wl}--out-implib,$lib'
-      else
-       ld_shlibs=no
-      fi
-      ;;
-
-    netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
-      if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
-       _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
-       wlarc=
-      else
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-      fi
-      ;;
-
-    solaris* | sysv5*)
-      if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-       cat <<EOF 1>&2
-
-*** Warning: The releases 2.8.* of the GNU linker cannot reliably
-*** create shared libraries on Solaris systems.  Therefore, libtool
-*** is disabling shared libraries support.  We urge you to upgrade GNU
-*** binutils to release 2.9.1 or newer.  Another option is to modify
-*** your PATH or compiler configuration so that the native linker is
-*** used, and then restart.
-
-EOF
-      elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-      else
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-      fi
-      ;;
-
-    sunos4*)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
-      wlarc=
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-  linux*)
-    if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
-        tmp_archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-       _LT_AC_TAGVAR(archive_cmds, $1)="$tmp_archive_cmds"
-      supports_anon_versioning=no
-      case `$LD -v 2>/dev/null` in
-        *\ [01].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
-        *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
-        *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
-        *\ 2.11.*) ;; # other 2.11 versions
-        *) supports_anon_versioning=yes ;;
-      esac
-      if test $supports_anon_versioning = yes; then
-        _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $output_objdir/$libname.ver~
-cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
-$echo "local: *; };" >> $output_objdir/$libname.ver~
-        $CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
-      else
-        _LT_AC_TAGVAR(archive_expsym_cmds, $1)="$tmp_archive_cmds"
-      fi
-      _LT_AC_TAGVAR(link_all_deplibs, $1)=no
-    else
-      _LT_AC_TAGVAR(ld_shlibs, $1)=no
-    fi
-    ;;
-
-    *)
-      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
-      else
-       _LT_AC_TAGVAR(ld_shlibs, $1)=no
-      fi
-      ;;
-    esac
-
-    if test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = yes; then
-      runpath_var=LD_RUN_PATH
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
-      _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
-      # ancient GNU ld didn't support --whole-archive et. al.
-      if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then
-       _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
-      else
-       _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=
-      fi
-    fi
-  else
-    # PORTME fill in a description of your system's linker (not GNU ld)
-    case $host_os in
-    aix3*)
-      _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
-      _LT_AC_TAGVAR(always_export_symbols, $1)=yes
-      _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
-      # Note: this linker hardcodes the directories in LIBPATH if there
-      # are no directories specified by -L.
-      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-      if test "$GCC" = yes && test -z "$link_static_flag"; then
-       # Neither direct hardcoding nor static linking is supported with a
-       # broken collect2.
-       _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported
-      fi
-      ;;
-
-    aix4* | aix5*)
-      if test "$host_cpu" = ia64; then
-       # On IA64, the linker does run time linking by default, so we don't
-       # have to do anything special.
-       aix_use_runtimelinking=no
-       exp_sym_flag='-Bexport'
-       no_entry_flag=""
-      else
-       # If we're using GNU nm, then we don't want the "-C" option.
-       # -C means demangle to AIX nm, but means don't demangle with GNU nm
-       if $NM -V 2>&1 | grep 'GNU' > /dev/null; then
-         _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols'
-       else
-         _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols'
-       fi
-       aix_use_runtimelinking=no
-
-       # Test if we are trying to use run time linking or normal
-       # AIX style linking. If -brtl is somewhere in LDFLAGS, we
-       # need to do runtime linking.
-       case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*)
-         for ld_flag in $LDFLAGS; do
-         if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
-           aix_use_runtimelinking=yes
-           break
-         fi
-         done
-       esac
-
-       exp_sym_flag='-bexport'
-       no_entry_flag='-bnoentry'
-      fi
-
-      # When large executables or shared objects are built, AIX ld can
-      # have problems creating the table of contents.  If linking a library
-      # or program results in "error TOC overflow" add -mminimal-toc to
-      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
-      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
-
-      _LT_AC_TAGVAR(archive_cmds, $1)=''
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':'
-      _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
-
-      if test "$GCC" = yes; then
-       case $host_os in aix4.[012]|aix4.[012].*)
-       # We only want to do this on AIX 4.2 and lower, the check
-       # below for broken collect2 doesn't work under 4.3+
-         collect2name=`${CC} -print-prog-name=collect2`
-         if test -f "$collect2name" && \
-          strings "$collect2name" | grep resolve_lib_name >/dev/null
-         then
-         # We have reworked collect2
-         _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-         else
-         # We have old collect2
-         _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported
-         # It fails to find uninstalled libraries when the uninstalled
-         # path is not listed in the libpath.  Setting hardcode_minus_L
-         # to unsupported forces relinking
-         _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-         _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-         _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=
-         fi
-       esac
-       shared_flag='-shared'
-      else
-       # not using gcc
-       if test "$host_cpu" = ia64; then
-       # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
-       # chokes on -Wl,-G. The following line is correct:
-         shared_flag='-G'
-       else
-       if test "$aix_use_runtimelinking" = yes; then
-           shared_flag='${wl}-G'
-         else
-           shared_flag='${wl}-bM:SRE'
-       fi
-       fi
-      fi
-
-      # It seems that -bexpall does not export symbols beginning with
-      # underscore (_), so it is better to generate a list of symbols to export.
-      _LT_AC_TAGVAR(always_export_symbols, $1)=yes
-      if test "$aix_use_runtimelinking" = yes; then
-       # Warning - without using the other runtime loading flags (-brtl),
-       # -berok will link without error, but may produce a broken library.
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok'
-       # Determine the default libpath from the value encoded in an empty executable.
-       _LT_AC_SYS_LIBPATH_AIX
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag"
-       else
-       if test "$host_cpu" = ia64; then
-         _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
-         _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
-         _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"
-       else
-        # Determine the default libpath from the value encoded in an empty executable.
-        _LT_AC_SYS_LIBPATH_AIX
-        _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
-         # Warning - without using the other run time loading flags,
-         # -berok will link without error, but may produce a broken library.
-         _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
-         _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
-         # -bexpall does not export symbols beginning with underscore (_)
-         _LT_AC_TAGVAR(always_export_symbols, $1)=yes
-         # Exported symbols can be pulled into shared objects from archives
-         _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=' '
-         _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes
-         # This is similar to how AIX traditionally builds it's shared libraries.
-         _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
-       fi
-      fi
-      ;;
-
-    amigaos*)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-      # see comment about different semantics on the GNU ld section
-      _LT_AC_TAGVAR(ld_shlibs, $1)=no
-      ;;
-
-    bsdi4*)
-      _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
-      ;;
-
-    cygwin* | mingw* | pw32*)
-      # When not using gcc, we currently assume that we are using
-      # Microsoft Visual C++.
-      # hardcode_libdir_flag_spec is actually meaningless, as there is
-      # no search path for DLLs.
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
-      _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
-      # Tell ltmain to make .lib files, not .a files.
-      libext=lib
-      # Tell ltmain to make .dll files, not .so files.
-      shrext_cmds=".dll"
-      # FIXME: Setting linknames here is a bad hack.
-      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames='
-      # The linker will automatically build a .lib file if we build a DLL.
-      _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='true'
-      # FIXME: Should let the user specify the lib program.
-      _LT_AC_TAGVAR(old_archive_cmds, $1)='lib /OUT:$oldlib$oldobjs$old_deplibs'
-      fix_srcfile_path='`cygpath -w "$srcfile"`'
-      _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
-      ;;
-
-    darwin* | rhapsody*)
-    if test "$GXX" = yes ; then
-      _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-      case "$host_os" in
-      rhapsody* | darwin1.[[012]])
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined suppress'
-       ;;
-      *) # Darwin 1.3 on
-      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
-      else
-        case ${MACOSX_DEPLOYMENT_TARGET} in
-          10.[[012]])
-            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
-            ;;
-          10.*)
-            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined dynamic_lookup'
-            ;;
-        esac
-      fi
-       ;;
-      esac
-       lt_int_apple_cc_single_mod=no
-       output_verbose_link_cmd='echo'
-       if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then
-         lt_int_apple_cc_single_mod=yes
-       fi
-       if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
-         _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
-       else
-        _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
-      fi
-      _LT_AC_TAGVAR(module_cmds, $1)='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
-      # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
-        if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
-          _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
-        else
-          _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
-        fi
-          _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
-      _LT_AC_TAGVAR(hardcode_direct, $1)=no
-      _LT_AC_TAGVAR(hardcode_automatic, $1)=yes
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
-      _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience'
-      _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
-    else
-      _LT_AC_TAGVAR(ld_shlibs, $1)=no
-    fi
-      ;;
-
-    dgux*)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    freebsd1*)
-      _LT_AC_TAGVAR(ld_shlibs, $1)=no
-      ;;
-
-    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
-    # support.  Future versions do this automatically, but an explicit c++rt0.o
-    # does not break anything, and helps significantly (at the cost of a little
-    # extra space).
-    freebsd2.2*)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
-    freebsd2*)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
-    freebsd* | kfreebsd*-gnu)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    hpux9*)
-      if test "$GCC" = yes; then
-       _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
-      else
-       _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
-      fi
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-
-      # hardcode_minus_L: Not really in the search PATH,
-      # but as the default location of the library.
-      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-      _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-      ;;
-
-    hpux10* | hpux11*)
-      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
-       case "$host_cpu" in
-       hppa*64*|ia64*)
-         _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
-         ;;
-       *)
-         _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
-         ;;
-       esac
-      else
-       case "$host_cpu" in
-       hppa*64*|ia64*)
-         _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags'
-         ;;
-       *)
-         _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
-         ;;
-       esac
-      fi
-      if test "$with_gnu_ld" = no; then
-       case "$host_cpu" in
-       hppa*64*)
-         _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-         _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir'
-         _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-         _LT_AC_TAGVAR(hardcode_direct, $1)=no
-         _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-         ;;
-       ia64*)
-         _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-         _LT_AC_TAGVAR(hardcode_direct, $1)=no
-         _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-
-         # hardcode_minus_L: Not really in the search PATH,
-         # but as the default location of the library.
-         _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-         ;;
-       *)
-         _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
-         _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-         _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-         _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-
-         # hardcode_minus_L: Not really in the search PATH,
-         # but as the default location of the library.
-         _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-         ;;
-       esac
-      fi
-      ;;
-
-    irix5* | irix6* | nonstopux*)
-      if test "$GCC" = yes; then
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
-      else
-       _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir'
-      fi
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-      _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
-      ;;
-
-    netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
-      if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
-       _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
-      else
-       _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
-      fi
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    newsos6)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    openbsd*)
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-       _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
-      else
-       case $host_os in
-        openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
-          _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
-          _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-          ;;
-        *)
-          _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
-          _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
-          ;;
-       esac
-      fi
-      ;;
-
-    os2*)
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-      _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
-      _LT_AC_TAGVAR(archive_cmds, $1)='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
-      _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
-      ;;
-
-    osf3*)
-      if test "$GCC" = yes; then
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
-      else
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
-       _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
-      fi
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-      ;;
-
-    osf4* | osf5*)     # as osf3* with the addition of -msym flag
-      if test "$GCC" = yes; then
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
-      else
-       _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
-       _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~
-       $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp'
-
-       # Both c and cxx compiler support -rpath directly
-       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
-      fi
-      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
-      ;;
-
-    sco3.2v5*)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
-      runpath_var=LD_RUN_PATH
-      hardcode_runpath_var=yes
-      ;;
-
-    solaris*)
-      _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text'
-      if test "$GCC" = yes; then
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
-         $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp'
-      else
-       _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
-       _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
-       $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
-      fi
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      case $host_os in
-      solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
-      *) # Supported since Solaris 2.6 (maybe 2.5.1?)
-       _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;;
-      esac
-      _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
-      ;;
-
-    sunos4*)
-      if test "x$host_vendor" = xsequent; then
-       # Use $CC to link under sequent, because it throws in some extra .o
-       # files that make .init and .fini sections work.
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
-      else
-       _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
-      fi
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    sysv4)
-      case $host_vendor in
-       sni)
-         _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-         _LT_AC_TAGVAR(hardcode_direct, $1)=yes # is this really true???
-       ;;
-       siemens)
-         ## LD is ld it makes a PLAMLIB
-         ## CC just makes a GrossModule.
-         _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
-         _LT_AC_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
-         _LT_AC_TAGVAR(hardcode_direct, $1)=no
-        ;;
-       motorola)
-         _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-         _LT_AC_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
-       ;;
-      esac
-      runpath_var='LD_RUN_PATH'
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    sysv4.3*)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
-      ;;
-
-    sysv4*MP*)
-      if test -d /usr/nec; then
-       _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-       _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-       runpath_var=LD_RUN_PATH
-       hardcode_runpath_var=yes
-       _LT_AC_TAGVAR(ld_shlibs, $1)=yes
-      fi
-      ;;
-
-    sysv4.2uw2*)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
-      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
-      _LT_AC_TAGVAR(hardcode_minus_L, $1)=no
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      hardcode_runpath_var=yes
-      runpath_var=LD_RUN_PATH
-      ;;
-
-   sysv5OpenUNIX8* | sysv5UnixWare7* |  sysv5uw[[78]]* | unixware7*)
-      _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z ${wl}text'
-      if test "$GCC" = yes; then
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
-      else
-       _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
-      fi
-      runpath_var='LD_RUN_PATH'
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    sysv5*)
-      _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text'
-      # $CC -shared without GNU ld will not create a library from C++
-      # object files and a static libstdc++, better avoid it by now
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
-      _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
-               $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      runpath_var='LD_RUN_PATH'
-      ;;
-
-    uts4*)
-      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
-      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
-      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
-      ;;
-
-    *)
-      _LT_AC_TAGVAR(ld_shlibs, $1)=no
-      ;;
-    esac
-  fi
-])
-AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)])
-test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
-
-variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
-if test "$GCC" = yes; then
-  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
-fi
-
-#
-# Do we need to explicitly link libc?
-#
-case "x$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)" in
-x|xyes)
-  # Assume -lc should be added
-  _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes
-
-  if test "$enable_shared" = yes && test "$GCC" = yes; then
-    case $_LT_AC_TAGVAR(archive_cmds, $1) in
-    *'~'*)
-      # FIXME: we may have to deal with multi-command sequences.
-      ;;
-    '$CC '*)
-      # Test whether the compiler implicitly links with -lc since on some
-      # systems, -lgcc has to come before -lc. If gcc already passes -lc
-      # to ld, don't add -lc before -lgcc.
-      AC_MSG_CHECKING([whether -lc should be explicitly linked in])
-      $rm conftest*
-      printf "$lt_simple_compile_test_code" > conftest.$ac_ext
-
-      if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
-        soname=conftest
-        lib=conftest
-        libobjs=conftest.$ac_objext
-        deplibs=
-        wl=$_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)
-        compiler_flags=-v
-        linker_flags=-v
-        verstring=
-        output_objdir=.
-        libname=conftest
-        lt_save_allow_undefined_flag=$_LT_AC_TAGVAR(allow_undefined_flag, $1)
-        _LT_AC_TAGVAR(allow_undefined_flag, $1)=
-        if AC_TRY_EVAL(_LT_AC_TAGVAR(archive_cmds, $1) 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1)
-        then
-         _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
-        else
-         _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes
-        fi
-        _LT_AC_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
-      else
-        cat conftest.err 1>&5
-      fi
-      $rm conftest*
-      AC_MSG_RESULT([$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)])
-      ;;
-    esac
-  fi
-  ;;
-esac
-])# AC_LIBTOOL_PROG_LD_SHLIBS
-
-
-# _LT_AC_FILE_LTDLL_C
-# -------------------
-# Be careful that the start marker always follows a newline.
-AC_DEFUN([_LT_AC_FILE_LTDLL_C], [
-# /* ltdll.c starts here */
-# #define WIN32_LEAN_AND_MEAN
-# #include <windows.h>
-# #undef WIN32_LEAN_AND_MEAN
-# #include <stdio.h>
-#
-# #ifndef __CYGWIN__
-# #  ifdef __CYGWIN32__
-# #    define __CYGWIN__ __CYGWIN32__
-# #  endif
-# #endif
-#
-# #ifdef __cplusplus
-# extern "C" {
-# #endif
-# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved);
-# #ifdef __cplusplus
-# }
-# #endif
-#
-# #ifdef __CYGWIN__
-# #include <cygwin/cygwin_dll.h>
-# DECLARE_CYGWIN_DLL( DllMain );
-# #endif
-# HINSTANCE __hDllInstance_base;
-#
-# BOOL APIENTRY
-# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved)
-# {
-#   __hDllInstance_base = hInst;
-#   return TRUE;
-# }
-# /* ltdll.c ends here */
-])# _LT_AC_FILE_LTDLL_C
-
-
-# _LT_AC_TAGVAR(VARNAME, [TAGNAME])
-# ---------------------------------
-AC_DEFUN([_LT_AC_TAGVAR], [ifelse([$2], [], [$1], [$1_$2])])
-
-
-# old names
-AC_DEFUN([AM_PROG_LIBTOOL],   [AC_PROG_LIBTOOL])
-AC_DEFUN([AM_ENABLE_SHARED],  [AC_ENABLE_SHARED($@)])
-AC_DEFUN([AM_ENABLE_STATIC],  [AC_ENABLE_STATIC($@)])
-AC_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
-AC_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
-AC_DEFUN([AM_PROG_LD],        [AC_PROG_LD])
-AC_DEFUN([AM_PROG_NM],        [AC_PROG_NM])
-
-# This is just to silence aclocal about the macro not being used
-ifelse([AC_DISABLE_FAST_INSTALL])
-
-AC_DEFUN([LT_AC_PROG_GCJ],
-[AC_CHECK_TOOL(GCJ, gcj, no)
-  test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
-  AC_SUBST(GCJFLAGS)
-])
-
-AC_DEFUN([LT_AC_PROG_RC],
-[AC_CHECK_TOOL(RC, windres, no)
-])
-
-############################################################
-# NOTE: This macro has been submitted for inclusion into   #
-#  GNU Autoconf as AC_PROG_SED.  When it is available in   #
-#  a released version of Autoconf we should remove this    #
-#  macro and use it instead.                               #
-############################################################
-# LT_AC_PROG_SED
-# --------------
-# Check for a fully-functional sed program, that truncates
-# as few characters as possible.  Prefer GNU sed if found.
-AC_DEFUN([LT_AC_PROG_SED],
-[AC_MSG_CHECKING([for a sed that does not truncate output])
-AC_CACHE_VAL(lt_cv_path_SED,
-[# Loop through the user's path and test for sed and gsed.
-# Then use that list of sed's as ones to test for truncation.
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for lt_ac_prog in sed gsed; do
-    for ac_exec_ext in '' $ac_executable_extensions; do
-      if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
-        lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
-      fi
-    done
-  done
-done
-lt_ac_max=0
-lt_ac_count=0
-# Add /usr/xpg4/bin/sed as it is typically found on Solaris
-# along with /bin/sed that truncates output.
-for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
-  test ! -f $lt_ac_sed && break
-  cat /dev/null > conftest.in
-  lt_ac_count=0
-  echo $ECHO_N "0123456789$ECHO_C" >conftest.in
-  # Check for GNU sed and select it if it is found.
-  if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
-    lt_cv_path_SED=$lt_ac_sed
-    break
-  fi
-  while true; do
-    cat conftest.in conftest.in >conftest.tmp
-    mv conftest.tmp conftest.in
-    cp conftest.in conftest.nl
-    echo >>conftest.nl
-    $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
-    cmp -s conftest.out conftest.nl || break
-    # 10000 chars as input seems more than enough
-    test $lt_ac_count -gt 10 && break
-    lt_ac_count=`expr $lt_ac_count + 1`
-    if test $lt_ac_count -gt $lt_ac_max; then
-      lt_ac_max=$lt_ac_count
-      lt_cv_path_SED=$lt_ac_sed
-    fi
-  done
-done
-SED=$lt_cv_path_SED
-])
-AC_MSG_RESULT([$SED])
-])
diff --git a/ltdl.m4 b/ltdl.m4
deleted file mode 100644 (file)
index aa6a0d1..0000000
--- a/ltdl.m4
+++ /dev/null
@@ -1,431 +0,0 @@
-## ltdl.m4 - Configure ltdl for the target system. -*-Autoconf-*-
-## Copyright (C) 1999-2000 Free Software Foundation, Inc.
-##
-## This program 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.
-##
-## This program is distributed in the hope that it will be useful, but
-## WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY 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 this program; if not, write to the Free Software
-## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-##
-## As a special exception to the GNU General Public License, if you
-## distribute this file as part of a program that contains a
-## configuration script generated by Autoconf, you may include it under
-## the same distribution terms that you use for the rest of that program.
-
-# serial 6 AC_LIB_LTDL
-# Debian $Rev: 214 $
-
-# AC_WITH_LTDL
-# ------------
-# Clients of libltdl can use this macro to allow the installer to
-# choose between a shipped copy of the ltdl sources or a preinstalled
-# version of the library.
-AC_DEFUN([AC_WITH_LTDL],
-[AC_REQUIRE([AC_LIB_LTDL])
-AC_SUBST([LIBLTDL])
-AC_SUBST([INCLTDL])
-
-# Unless the user asks us to check, assume no installed ltdl exists.
-use_installed_libltdl=no
-
-AC_ARG_WITH([included_ltdl],
-    [  --with-included-ltdl    use the GNU ltdl sources included here])
-
-if test "x$with_included_ltdl" != xyes; then
-  # We are not being forced to use the included libltdl sources, so
-  # decide whether there is a useful installed version we can use.
-  AC_CHECK_HEADER([ltdl.h],
-      [AC_CHECK_LIB([ltdl], [lt_dlcaller_register],
-          [with_included_ltdl=no],
-          [with_included_ltdl=yes])
-  ])
-fi
-
-if test "x$enable_ltdl_install" != xyes; then
-  # If the user did not specify an installable libltdl, then default
-  # to a convenience lib.
-  AC_LIBLTDL_CONVENIENCE
-fi
-
-if test "x$with_included_ltdl" = xno; then
-  # If the included ltdl is not to be used. then Use the
-  # preinstalled libltdl we found.
-  AC_DEFINE([HAVE_LTDL], 1,
-    [Define this if a modern libltdl is already installed])
-  LIBLTDL=-lltdl
-fi
-
-# Report our decision...
-AC_MSG_CHECKING([whether to use included libltdl])
-AC_MSG_RESULT([$with_included_ltdl])
-
-AC_CONFIG_SUBDIRS([libltdl])
-])# AC_WITH_LTDL
-
-
-# AC_LIB_LTDL
-# -----------
-# Perform all the checks necessary for compilation of the ltdl objects
-#  -- including compiler checks and header checks.
-AC_DEFUN([AC_LIB_LTDL],
-[AC_PREREQ(2.50)
-AC_REQUIRE([AC_PROG_CC])
-AC_REQUIRE([AC_C_CONST])
-AC_REQUIRE([AC_HEADER_STDC])
-AC_REQUIRE([AC_HEADER_DIRENT])
-AC_REQUIRE([_LT_AC_CHECK_DLFCN])
-AC_REQUIRE([AC_LTDL_ENABLE_INSTALL])
-AC_REQUIRE([AC_LTDL_SHLIBEXT])
-AC_REQUIRE([AC_LTDL_SHLIBPATH])
-AC_REQUIRE([AC_LTDL_SYSSEARCHPATH])
-AC_REQUIRE([AC_LTDL_OBJDIR])
-AC_REQUIRE([AC_LTDL_DLPREOPEN])
-AC_REQUIRE([AC_LTDL_DLLIB])
-AC_REQUIRE([AC_LTDL_SYMBOL_USCORE])
-AC_REQUIRE([AC_LTDL_DLSYM_USCORE])
-AC_REQUIRE([AC_LTDL_SYS_DLOPEN_DEPLIBS])
-AC_REQUIRE([AC_LTDL_FUNC_ARGZ])
-
-AC_CHECK_HEADERS([assert.h ctype.h errno.h malloc.h memory.h stdlib.h \
-                 stdio.h unistd.h])
-AC_CHECK_HEADERS([dl.h sys/dl.h dld.h mach-o/dyld.h])
-AC_CHECK_HEADERS([string.h strings.h], [break])
-
-AC_CHECK_FUNCS([strchr index], [break])
-AC_CHECK_FUNCS([strrchr rindex], [break])
-AC_CHECK_FUNCS([memcpy bcopy], [break])
-AC_CHECK_FUNCS([memmove strcmp])
-AC_CHECK_FUNCS([closedir opendir readdir])
-])# AC_LIB_LTDL
-
-
-# AC_LTDL_ENABLE_INSTALL
-# ----------------------
-AC_DEFUN([AC_LTDL_ENABLE_INSTALL],
-[AC_ARG_ENABLE([ltdl-install],
-    [AC_HELP_STRING([--enable-ltdl-install], [install libltdl])])
-
-AM_CONDITIONAL(INSTALL_LTDL, test x"${enable_ltdl_install-no}" != xno)
-AM_CONDITIONAL(CONVENIENCE_LTDL, test x"${enable_ltdl_convenience-no}" != xno)
-])])# AC_LTDL_ENABLE_INSTALL
-
-
-# AC_LTDL_SYS_DLOPEN_DEPLIBS
-# --------------------------
-AC_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS],
-[AC_REQUIRE([AC_CANONICAL_HOST])
-AC_CACHE_CHECK([whether deplibs are loaded by dlopen],
-  [libltdl_cv_sys_dlopen_deplibs],
-  [# PORTME does your system automatically load deplibs for dlopen?
-  # or its logical equivalent (e.g. shl_load for HP-UX < 11)
-  # For now, we just catch OSes we know something about -- in the
-  # future, we'll try test this programmatically.
-  libltdl_cv_sys_dlopen_deplibs=unknown
-  case "$host_os" in
-  aix3*|aix4.1.*|aix4.2.*)
-    # Unknown whether this is true for these versions of AIX, but
-    # we want this `case' here to explicitly catch those versions.
-    libltdl_cv_sys_dlopen_deplibs=unknown
-    ;;
-  aix[[45]]*)
-    libltdl_cv_sys_dlopen_deplibs=yes
-    ;;
-  darwin*)
-    # Assuming the user has installed a libdl from somewhere, this is true
-    # If you are looking for one http://www.opendarwin.org/projects/dlcompat
-    libltdl_cv_sys_dlopen_deplibs=yes
-    ;;   
-  gnu* | linux* | kfreebsd*-gnu | knetbsd*-gnu)
-    # GNU and its variants, using gnu ld.so (Glibc)
-    libltdl_cv_sys_dlopen_deplibs=yes
-    ;;
-  hpux10*|hpux11*)
-    libltdl_cv_sys_dlopen_deplibs=yes
-    ;;
-  irix[[12345]]*|irix6.[[01]]*)
-    # Catch all versions of IRIX before 6.2, and indicate that we don't
-    # know how it worked for any of those versions.
-    libltdl_cv_sys_dlopen_deplibs=unknown
-    ;;
-  irix*)
-    # The case above catches anything before 6.2, and it's known that
-    # at 6.2 and later dlopen does load deplibs.
-    libltdl_cv_sys_dlopen_deplibs=yes
-    ;;
-  netbsd* | netbsdelf*-gnu)
-    libltdl_cv_sys_dlopen_deplibs=yes
-    ;;
-  openbsd*)
-    libltdl_cv_sys_dlopen_deplibs=yes
-    ;;
-  osf[[1234]]*)
-    # dlopen did load deplibs (at least at 4.x), but until the 5.x series,
-    # it did *not* use an RPATH in a shared library to find objects the
-    # library depends on, so we explictly say `no'.
-    libltdl_cv_sys_dlopen_deplibs=no
-    ;;
-  osf5.0|osf5.0a|osf5.1)
-    # dlopen *does* load deplibs and with the right loader patch applied
-    # it even uses RPATH in a shared library to search for shared objects
-    # that the library depends on, but there's no easy way to know if that
-    # patch is installed.  Since this is the case, all we can really
-    # say is unknown -- it depends on the patch being installed.  If
-    # it is, this changes to `yes'.  Without it, it would be `no'.
-    libltdl_cv_sys_dlopen_deplibs=unknown
-    ;;
-  osf*)
-    # the two cases above should catch all versions of osf <= 5.1.  Read
-    # the comments above for what we know about them.
-    # At > 5.1, deplibs are loaded *and* any RPATH in a shared library
-    # is used to find them so we can finally say `yes'.
-    libltdl_cv_sys_dlopen_deplibs=yes
-    ;;
-  solaris*)
-    libltdl_cv_sys_dlopen_deplibs=yes
-    ;;
-  esac
-  ])
-if test "$libltdl_cv_sys_dlopen_deplibs" != yes; then
- AC_DEFINE([LTDL_DLOPEN_DEPLIBS], [1],
-    [Define if the OS needs help to load dependent libraries for dlopen().])
-fi
-])# AC_LTDL_SYS_DLOPEN_DEPLIBS
-
-
-# AC_LTDL_SHLIBEXT
-# ----------------
-AC_DEFUN([AC_LTDL_SHLIBEXT],
-[AC_REQUIRE([AC_LIBTOOL_SYS_DYNAMIC_LINKER])
-AC_CACHE_CHECK([which extension is used for loadable modules],
-  [libltdl_cv_shlibext],
-[
-module=yes
-eval libltdl_cv_shlibext=$shrext_cmds
-  ])
-if test -n "$libltdl_cv_shlibext"; then
-  AC_DEFINE_UNQUOTED(LTDL_SHLIB_EXT, "$libltdl_cv_shlibext",
-    [Define to the extension used for shared libraries, say, ".so".])
-fi
-])# AC_LTDL_SHLIBEXT
-
-
-# AC_LTDL_SHLIBPATH
-# -----------------
-AC_DEFUN([AC_LTDL_SHLIBPATH],
-[AC_REQUIRE([AC_LIBTOOL_SYS_DYNAMIC_LINKER])
-AC_CACHE_CHECK([which variable specifies run-time library path],
-  [libltdl_cv_shlibpath_var], [libltdl_cv_shlibpath_var="$shlibpath_var"])
-if test -n "$libltdl_cv_shlibpath_var"; then
-  AC_DEFINE_UNQUOTED(LTDL_SHLIBPATH_VAR, "$libltdl_cv_shlibpath_var",
-    [Define to the name of the environment variable that determines the dynamic library search path.])
-fi
-])# AC_LTDL_SHLIBPATH
-
-
-# AC_LTDL_SYSSEARCHPATH
-# ---------------------
-AC_DEFUN([AC_LTDL_SYSSEARCHPATH],
-[AC_REQUIRE([AC_LIBTOOL_SYS_DYNAMIC_LINKER])
-AC_CACHE_CHECK([for the default library search path],
-  [libltdl_cv_sys_search_path],
-  [libltdl_cv_sys_search_path="$sys_lib_dlsearch_path_spec"])
-if test -n "$libltdl_cv_sys_search_path"; then
-  sys_search_path=
-  for dir in $libltdl_cv_sys_search_path; do
-    if test -z "$sys_search_path"; then
-      sys_search_path="$dir"
-    else
-      sys_search_path="$sys_search_path$PATH_SEPARATOR$dir"
-    fi
-  done
-  AC_DEFINE_UNQUOTED(LTDL_SYSSEARCHPATH, "$sys_search_path",
-    [Define to the system default library search path.])
-fi
-])# AC_LTDL_SYSSEARCHPATH
-
-
-# AC_LTDL_OBJDIR
-# --------------
-AC_DEFUN([AC_LTDL_OBJDIR],
-[AC_CACHE_CHECK([for objdir],
-  [libltdl_cv_objdir],
-  [libltdl_cv_objdir="$objdir"
-  if test -n "$objdir"; then
-    :
-  else
-    rm -f .libs 2>/dev/null
-    mkdir .libs 2>/dev/null
-    if test -d .libs; then
-      libltdl_cv_objdir=.libs
-    else
-      # MS-DOS does not allow filenames that begin with a dot.
-      libltdl_cv_objdir=_libs
-    fi
-  rmdir .libs 2>/dev/null
-  fi
-  ])
-AC_DEFINE_UNQUOTED(LTDL_OBJDIR, "$libltdl_cv_objdir/",
-  [Define to the sub-directory in which libtool stores uninstalled libraries.])
-])# AC_LTDL_OBJDIR
-
-
-# AC_LTDL_DLPREOPEN
-# -----------------
-AC_DEFUN([AC_LTDL_DLPREOPEN],
-[AC_REQUIRE([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])
-AC_CACHE_CHECK([whether libtool supports -dlopen/-dlpreopen],
-  [libltdl_cv_preloaded_symbols],
-  [if test -n "$lt_cv_sys_global_symbol_pipe"; then
-    libltdl_cv_preloaded_symbols=yes
-  else
-    libltdl_cv_preloaded_symbols=no
-  fi
-  ])
-if test x"$libltdl_cv_preloaded_symbols" = xyes; then
-  AC_DEFINE(HAVE_PRELOADED_SYMBOLS, 1,
-    [Define if libtool can extract symbol lists from object files.])
-fi
-])# AC_LTDL_DLPREOPEN
-
-
-# AC_LTDL_DLLIB
-# -------------
-AC_DEFUN([AC_LTDL_DLLIB],
-[LIBADD_DL=
-AC_SUBST(LIBADD_DL)
-AC_LANG_PUSH([C])
-
-AC_CHECK_FUNC([shl_load],
-      [AC_DEFINE([HAVE_SHL_LOAD], [1],
-                [Define if you have the shl_load function.])],
-  [AC_CHECK_LIB([dld], [shl_load],
-       [AC_DEFINE([HAVE_SHL_LOAD], [1],
-                  [Define if you have the shl_load function.])
-       LIBADD_DL="$LIBADD_DL -ldld"],
-    [AC_CHECK_LIB([dl], [dlopen],
-         [AC_DEFINE([HAVE_LIBDL], [1],
-                    [Define if you have the libdl library or equivalent.])
-               LIBADD_DL="-ldl" libltdl_cv_lib_dl_dlopen="yes"],
-      [AC_TRY_LINK([#if HAVE_DLFCN_H
-#  include <dlfcn.h>
-#endif
-      ],
-       [dlopen(0, 0);],
-           [AC_DEFINE([HAVE_LIBDL], [1],
-                            [Define if you have the libdl library or equivalent.]) libltdl_cv_func_dlopen="yes"],
-       [AC_CHECK_LIB([svld], [dlopen],
-             [AC_DEFINE([HAVE_LIBDL], [1],
-                        [Define if you have the libdl library or equivalent.])
-                   LIBADD_DL="-lsvld" libltdl_cv_func_dlopen="yes"],
-         [AC_CHECK_LIB([dld], [dld_link],
-               [AC_DEFINE([HAVE_DLD], [1],
-                          [Define if you have the GNU dld library.])
-               LIBADD_DL="$LIBADD_DL -ldld"],
-               [AC_CHECK_FUNC([_dyld_func_lookup],
-                      [AC_DEFINE([HAVE_DYLD], [1],
-                         [Define if you have the _dyld_func_lookup function.])])
-          ])
-        ])
-      ])
-    ])
-  ])
-])
-
-if test x"$libltdl_cv_func_dlopen" = xyes || test x"$libltdl_cv_lib_dl_dlopen" = xyes
-then
-  lt_save_LIBS="$LIBS"
-  LIBS="$LIBS $LIBADD_DL"
-  AC_CHECK_FUNCS([dlerror])
-  LIBS="$lt_save_LIBS"
-fi
-AC_LANG_POP
-])# AC_LTDL_DLLIB
-
-
-# AC_LTDL_SYMBOL_USCORE
-# ---------------------
-# does the compiler prefix global symbols with an underscore?
-AC_DEFUN([AC_LTDL_SYMBOL_USCORE],
-[AC_REQUIRE([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])
-AC_CACHE_CHECK([for _ prefix in compiled symbols],
-  [ac_cv_sys_symbol_underscore],
-  [ac_cv_sys_symbol_underscore=no
-  cat > conftest.$ac_ext <<EOF
-void nm_test_func(){}
-int main(){nm_test_func;return 0;}
-EOF
-  if AC_TRY_EVAL(ac_compile); then
-    # Now try to grab the symbols.
-    ac_nlist=conftest.nm
-    if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $ac_nlist) && test -s "$ac_nlist"; then
-      # See whether the symbols have a leading underscore.
-      if grep '^. _nm_test_func' "$ac_nlist" >/dev/null; then
-        ac_cv_sys_symbol_underscore=yes
-      else
-        if grep '^. nm_test_func ' "$ac_nlist" >/dev/null; then
-         :
-        else
-         echo "configure: cannot find nm_test_func in $ac_nlist" >&AC_FD_CC
-        fi
-      fi
-    else
-      echo "configure: cannot run $lt_cv_sys_global_symbol_pipe" >&AC_FD_CC
-    fi
-  else
-    echo "configure: failed program was:" >&AC_FD_CC
-    cat conftest.c >&AC_FD_CC
-  fi
-  rm -rf conftest*
-  ])
-])# AC_LTDL_SYMBOL_USCORE
-
-
-# AC_LTDL_DLSYM_USCORE
-# --------------------
-AC_DEFUN([AC_LTDL_DLSYM_USCORE],
-[AC_REQUIRE([AC_LTDL_SYMBOL_USCORE])
-if test x"$ac_cv_sys_symbol_underscore" = xyes; then
-  if test x"$libltdl_cv_func_dlopen" = xyes ||
-     test x"$libltdl_cv_lib_dl_dlopen" = xyes ; then
-       AC_CACHE_CHECK([whether we have to add an underscore for dlsym],
-         [libltdl_cv_need_uscore],
-         [libltdl_cv_need_uscore=unknown
-          save_LIBS="$LIBS"
-          LIBS="$LIBS $LIBADD_DL"
-         _LT_AC_TRY_DLOPEN_SELF(
-           [libltdl_cv_need_uscore=no], [libltdl_cv_need_uscore=yes],
-           [],                          [libltdl_cv_need_uscore=cross])
-         LIBS="$save_LIBS"
-       ])
-  fi
-fi
-
-if test x"$libltdl_cv_need_uscore" = xyes; then
-  AC_DEFINE(NEED_USCORE, 1,
-    [Define if dlsym() requires a leading underscore in symbol names.])
-fi
-])# AC_LTDL_DLSYM_USCORE
-
-# AC_LTDL_FUNC_ARGZ
-# -----------------
-AC_DEFUN([AC_LTDL_FUNC_ARGZ],
-[AC_CHECK_HEADERS([argz.h])
-
-AC_CHECK_TYPES([error_t],
-  [],
-  [AC_DEFINE([error_t], [int],
-    [Define to a type to use for `error_t' if it is not otherwise available.])],
-  [#if HAVE_ARGZ_H
-#  include <argz.h>
-#endif])
-
-AC_CHECK_FUNCS([argz_append argz_create_sep argz_insert argz_next argz_stringify])
-])# AC_LTDL_FUNC_ARGZ
diff --git a/man/.gitignore b/man/.gitignore
new file mode 100644 (file)
index 0000000..c213238
--- /dev/null
@@ -0,0 +1,4 @@
+*.1
+*.1.xml
+*.5
+*.5.xml
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644 (file)
index 0000000..31ac69c
--- /dev/null
@@ -0,0 +1,190 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+pulseconfdir=$(sysconfdir)/pulse
+
+if BUILD_MANPAGES
+
+man_MANS = \
+       pulseaudio.1 \
+       esdcompat.1 \
+       pax11publish.1 \
+       paplay.1 \
+       pacat.1 \
+       pacmd.1 \
+       pactl.1 \
+       pasuspender.1 \
+       padsp.1 \
+       pabrowse.1 \
+       pulse-daemon.conf.5 \
+       pulse-client.conf.5 \
+       default.pa.5
+
+noinst_DATA = \
+       pulseaudio.1.xml \
+       esdcompat.1.xml \
+       pax11publish.1.xml \
+       paplay.1.xml \
+       pacat.1.xml \
+       pacmd.1.xml \
+       pactl.1.xml \
+       pasuspender.1.xml \
+       padsp.1.xml \
+       pabrowse.1.xml \
+       pulse-daemon.conf.5.xml \
+       pulse-client.conf.5.xml \
+       default.pa.5.xml
+
+CLEANFILES = \
+       $(noinst_DATA)
+
+pulseaudio.1.xml: pulseaudio.1.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+esdcompat.1.xml: esdcompat.1.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+pax11publish.1.xml: pax11publish.1.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+paplay.1.xml: paplay.1.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+pacat.1.xml: pacat.1.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+pacmd.1.xml: pacmd.1.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+pactl.1.xml: pactl.1.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+pasuspender.1.xml: pasuspender.1.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+padsp.1.xml: padsp.1.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+pabrowse.1.xml: pabrowse.1.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+pulse-daemon.conf.5.xml: pulse-daemon.conf.5.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+pulse-client.conf.5.xml: pulse-client.conf.5.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+default.pa.5.xml: default.pa.5.xml.in Makefile
+       sed -e 's,@pulseconfdir\@,$(pulseconfdir),g' \
+           -e 's,@PACKAGE_BUGREPORT\@,$(PACKAGE_BUGREPORT),g' \
+            -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' $< > $@
+
+if USE_XMLTOMAN
+
+CLEANFILES += \
+       $(man_MANS)
+
+pulseaudio.1: pulseaudio.1.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+esdcompat.1: esdcompat.1.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+pax11publish.1: pax11publish.1.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+paplay.1: paplay.1.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+pacat.1: pacat.1.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+pacmd.1: pacmd.1.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+pactl.1: pactl.1.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+pasuspender.1: pasuspender.1.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+padsp.1: padsp.1.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+pabrowse.1: pabrowse.1.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+pulse-daemon.conf.5: pulse-daemon.conf.5.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+pulse-client.conf.5: pulse-client.conf.5.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+default.pa.5: default.pa.5.xml Makefile
+       xmltoman $< > $@ || rm -f $@
+
+xmllint: $(noinst_DATA)
+       for f in $(noinst_DATA) ; do \
+                       xmllint --noout --valid "$$f" || exit 1 ; \
+       done
+
+endif
+
+endif
+
+EXTRA_DIST = \
+       $(man_MANS) \
+       pulseaudio.1.xml.in \
+       esdcompat.1.xml.in \
+       pax11publish.1.xml.in \
+       paplay.1.xml.in \
+       pacat.1.xml.in \
+       pacmd.1.xml.in \
+       pactl.1.xml.in \
+       pasuspender.1.xml.in \
+       padsp.1.xml.in \
+       pabrowse.1.xml.in \
+       pulse-daemon.conf.5.xml.in \
+       pulse-client.conf.5.xml.in \
+       default.pa.5.xml.in \
+       xmltoman.css \
+       xmltoman.xsl \
+       xmltoman.dtd
diff --git a/man/default.pa.5.xml.in b/man/default.pa.5.xml.in
new file mode 100644 (file)
index 0000000..4caad7c
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="default.pa" section="5" desc="PulseAudio Sound Server Startup Script">
+
+  <synopsis>
+    <p><file>~/.pulse/default.pa</file></p>
+
+    <p><file>@pulseconfdir@/default.pa</file></p>
+  </synopsis>
+
+  <description>
+    <p>The PulseAudio sound server interprets the file
+    <file>~/.pulse/default.pa</file> on startup, and when that file
+    doesn't exist <file>@pulseconfdir@/default.pa</file>. It
+    should contain directives in the PulseAudio CLI languages, as
+    documented on <url href="http://pulseaudio.org/wiki/CLI"/>.</p>
+
+    <p>The same commands can also be entered during runtime in the <manref name="pacmd"
+      section="1"/> tool, allowing flexible runtime reconfiguration.</p>
+  </description>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;;
+    PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulse-daemon.conf" section="5"/>, <manref
+      name="pulseaudio" section="1"/>, <manref name="pacmd"
+      section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/esdcompat.1.xml.in b/man/esdcompat.1.xml.in
new file mode 100644 (file)
index 0000000..61fefa3
--- /dev/null
@@ -0,0 +1,89 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="esdcompat" section="1" desc="PulseAudio ESD wrapper script">
+
+  <synopsis>
+    <cmd>esdcompat [<arg>options</arg>]</cmd>
+    <cmd>esdcompat <opt>--help</opt></cmd>
+    <cmd>esdcompat <opt>--version</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>esdcompat</file> is a compatiblity script that takes the
+    same arguments as the ESD sound daemon <manref name="esd"
+    section="1"/>, but uses them to start a the PulseAudio sound server with the appropriate parameters. It is
+    required to make PulseAudio a drop-in replacement for esd, i.e. it
+    can be used to make <manref name="gnome-session" section="1"/>
+    start up PulseAudio instead of esd.</p>
+
+    <p>It is recommended to make <file>esd</file> a symbolic link to this script.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-tcp | -promiscuous | -d | -b | -r | -as | -unix | -public | -terminate | -nobeeps | -trust | -port | -bind</opt></p>
+
+      <optdesc><p>These options understood by the original
+      <file>esd</file> are ignored by
+      <file>esdcompat</file>.</p></optdesc>
+
+    </option>
+
+
+    <option>
+      <p><opt>-spawnpid | -spawnfd</opt></p>
+
+      <optdesc><p>These internally used options understood by the
+      original <file>esd</file> are properly handled by
+      <file>esdcompat</file>, however are not to be used
+      manually.</p></optdesc>
+
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="esd" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pabrowse.1.xml.in b/man/pabrowse.1.xml.in
new file mode 100644 (file)
index 0000000..33f071b
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="pabrowse" section="1" desc="List PulseAudio sound servers on the network">
+
+  <synopsis>
+    <cmd>pabrowse</cmd>
+  </synopsis>
+
+  <description>
+    <p><file>pabrowse</file> lists all PulseAudio sound servers on the
+    local network that are being announced with Zeroconf/Avahi.</p>
+
+    <p>This program takes no command line arguments.</p>
+  </description>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="avahi-daemon" section="8"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pacat.1.xml.in b/man/pacat.1.xml.in
new file mode 100644 (file)
index 0000000..7b0d72b
--- /dev/null
@@ -0,0 +1,183 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="pacat" section="1" desc="Play back or record raw audio streams on a PulseAudio sound server">
+
+  <synopsis>
+    <cmd>pacat [<arg>options</arg>] [<arg>FILE</arg>]</cmd>
+    <cmd>parec [<arg>options</arg>] [<arg>FILE</arg>]</cmd>
+    <cmd>paplay <opt>--help</opt></cmd>
+    <cmd>paplay <opt>--version</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>pacat</file> is a simple tool for playing back or
+    capturing raw audio files on a PulseAudio sound server.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-r | --record</opt></p>
+
+      <optdesc><p>Capture raw audio data and write it to the specified file or to STDOUT if none is specified. If the tool is called under the name <file>parec</file> this is the default.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-p | --playback</opt></p>
+
+      <optdesc><p>Read raw audio data from the specified file or STDIN if none is specified, and play it back. If the tool is called under the name <file>pacat</file> this is the default.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-v | --verbose</opt></p>
+
+      <optdesc><p>Enable verbose operation. Dumps the current playback time to STDERR during playback/capturing.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-s | --server</opt><arg>=SERVER</arg></p>
+
+      <optdesc><p>Choose the server to connect to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-d | --device</opt><arg>=SINKORSOURCE</arg></p>
+
+      <optdesc><p>Specify the symbolic name of the sink/source to play/record this stream on/from.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-n | --client-name</opt><arg>=NAME</arg></p>
+
+      <optdesc><p>Specify the client name <file>paplay</file> shall pass to the server when connecting.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--stream-name</opt><arg>=NAME</arg></p>
+
+      <optdesc><p>Specify the stream name <file>paplay</file> shall pass to the server when creating the stream.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--volume</opt><arg>=VOLUME</arg></p>
+
+      <optdesc><p>Specify the initial playback volume to use. Choose a value between 0 (silent) and 65536 (100% volume).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--rate</opt><arg>=SAMPLERATE</arg></p>
+
+      <optdesc><p>Capture or play back audio with the specified sample rate. Defaults to 44100 Hz.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--format</opt><arg>=FORMAT</arg></p>
+
+      <optdesc><p>Capture or play back audio with the specified sample
+      format. Specify one of <opt>u8</opt>, <opt>s16le</opt>,
+      <opt>s16be</opt>, <opt>float32le</opt>, <opt>float32be</opt>,
+      <opt>ulaw</opt>, <opt>alaw</opt>. Depending on the endianess of
+      the CPU the formats <opt>s16ne</opt>, <opt>s16re</opt>,
+      <opt>float32ne</opt>, <opt>float32re</opt> (for native,
+      resp. reverse endian) are available as aliases. Defaults to
+      s16ne.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--channels</opt><arg>=CHANNELS</arg></p>
+
+      <optdesc><p>Capture or play back audio with the specified number
+      of channels. If more than two channels are used it is
+      recommended to use the <opt>--channel-map</opt> option
+      below. Defaults to 2.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--channel-map</opt><arg>=CHANNELMAP</arg></p>
+
+      <optdesc><p>Explicitly choose a channel map when playing back
+      this stream. The argument should be a comma separated list of
+      channel names: <opt>front-left</opt>, <opt>front-right</opt>,
+      <opt>mono</opt>, <opt>front-center</opt>, <opt>rear-left</opt>,
+      <opt>rear-right</opt>, <opt>rear-center</opt>, <opt>lfe</opt>,
+      <opt>front-left-of-center</opt>,
+      <opt>front-right-of-center</opt>, <opt>side-left</opt>,
+      <opt>side-right</opt>, <opt>top-center</opt>,
+      <opt>top-front-center</opt>, <opt>top-front-left</opt>,
+      <opt>top-front-right</opt>, <opt>top-rear-left</opt>,
+      <opt>top-rear-right</opt>, <opt>top-rear-center</opt>, or any of
+      the 32 auxiliary channel names <opt>aux0</opt> to
+      <opt>aux31</opt>.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--fix-format</opt></p>
+      <optdesc><p>If passed, the sample format of the stream is changed to the native format of the sink the stream is connected to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--fix-rate</opt></p>
+      <optdesc><p>If passed, the sampling rate of the stream is changed to the native rate of the sink the stream is connected to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--fix-channels</opt></p>
+      <optdesc><p>If passed, the number of channels and the channel map of the stream is changed to the native number of channels and the native channel map of the sink the stream is connected to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--no-remix</opt></p>
+      <optdesc><p>Never upmix or downmix channels.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--no-remap</opt></p>
+      <optdesc><p>Never remap channels. Instead of mapping channels by their name this will match them solely by their index/order.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="paplay" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pacmd.1.xml.in b/man/pacmd.1.xml.in
new file mode 100644 (file)
index 0000000..c20c016
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="pacmd" section="1" desc="Reconfigure a PulseAudio sound server during runtime">
+
+  <synopsis>
+    <cmd>pacmd</cmd>
+  </synopsis>
+
+  <description>
+    <p>This tool can be used to introspect or reconfigure a running
+    PulseAudio sound server during runtime. It connects to the sound
+    server and offers a simple live shell that can be used to enter
+    the commands also understood in the <file>default.pa</file>
+    configuration scripts.</p>
+
+    <p>This program takes no command line options.</p>
+  </description>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="pactl" section="1"/>, <manref name="default.pa" section="5"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
new file mode 100644 (file)
index 0000000..8d5bf1d
--- /dev/null
@@ -0,0 +1,189 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="pactl" section="1" desc="Control a running PulseAudio sound server">
+
+  <synopsis>
+    <cmd>pactl [<arg>options</arg>] stat</cmd>
+    <cmd>pactl [<arg>options</arg>] list</cmd>
+    <cmd>pactl [<arg>options</arg>] exit</cmd>
+    <cmd>pactl [<arg>options</arg>] upload-sample <arg>FILENAME</arg> [<arg>NAME</arg>]</cmd>
+    <cmd>pactl [<arg>options</arg>] play-sample <arg>NAME</arg> [<arg>SINK</arg>]</cmd>
+    <cmd>pactl [<arg>options</arg>] remove-sample <arg>NAME</arg></cmd>
+    <cmd>pactl [<arg>options</arg>] move-sink-input <arg>ID</arg> <arg>SINK</arg></cmd>
+    <cmd>pactl [<arg>options</arg>] move-source-input <arg>ID</arg> <arg>SOURCE</arg></cmd>
+    <cmd>pactl [<arg>options</arg>] load-module <arg>NAME</arg> [<arg>ARGUMENTS ...</arg>]</cmd>
+    <cmd>pactl [<arg>options</arg>] unload-module <arg>ID</arg></cmd>
+    <cmd>pactl [<arg>options</arg>] suspend-sink [<arg>SINK</arg>] <arg>1|0</arg></cmd>
+    <cmd>pactl [<arg>options</arg>] suspend-source [<arg>SOURCE</arg>] <arg>1|0</arg></cmd>
+    <cmd>pactl <opt>--help</opt></cmd>
+    <cmd>pactl <opt>--version</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>pactl</file> can be used to issue control commands to the PulseAudio sound server.</p>
+
+    <p><file>pactl</file> only exposes a subset of the available operations. For the full set use the <manref name="pacmd" section="1"/>.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-s | --server</opt><arg>=SERVER</arg></p>
+
+      <optdesc><p>Choose the server to connect to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-n | --client-name</opt><arg>=NAME</arg></p>
+
+      <optdesc><p>Specify the client name <file>pactl</file> shall pass to the server when connecting.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>stat</opt></p>
+
+      <optdesc><p>Dump a few statistics about the PulseAudio daemon.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>list</opt></p>
+
+      <optdesc><p>Dump all currently loaded modules, available sinks, sources, streams and clients.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>exit</opt></p>
+
+      <optdesc><p>Asks the PulseAudio server to terminate.</p></optdesc>
+    </option>
+
+
+    <option>
+      <p><opt>upload-sample</opt> <arg>FILENAME</arg> [<arg>NAME</arg>]</p>
+
+      <optdesc><p>Upload a sound from the specified audio file into
+      the sample cache. The file types supported are those understood
+      by <file>libsndfile</file>. The sample in the cache is named
+      after the audio file, unless the name is explicitly
+      specified.</p></optdesc>
+
+    </option>
+
+    <option>
+      <p><opt>play-sample</opt> <arg>NAME</arg> [<arg>SINK</arg>]</p>
+
+      <optdesc><p>Play the specified sample from the sample cache. It
+      is played on the default sink, unless the symbolic name or the
+      numerical index of the sink to play it on is
+      specified.</p></optdesc>
+
+    </option>
+
+    <option>
+      <p><opt>remove-sample</opt> <arg>NAME</arg></p>
+
+      <optdesc><p>Remove the specified sample from the sample cache.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>move-sink-input</opt> <arg>ID</arg> <arg>SINK</arg></p>
+
+      <optdesc><p>Move the specified playback stream (identified by its numerical index) to the specified sink (identified by its symbolic name or numerical index).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>move-source-output</opt> <arg>ID</arg> <arg>SOURCE</arg></p>
+
+      <optdesc><p>Move the specified recording stream (identified by its numerical index) to the specified source (identified by its symbolic name or numerical index).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>load-module</opt> <arg>NAME</arg> [<arg>ARGUMENTS ...</arg>]</p>
+
+      <optdesc><p>Load the specified module with the specified arguments into the running sound server. Prints the numeric index of the module just loaded to STDOUT. You can use it to unload the module later.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>unload-module</opt> <arg>ID</arg></p>
+
+      <optdesc><p>Unload the module instance identified by the specified numeric index.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>unload-module</opt> <arg>ID</arg></p>
+
+      <optdesc><p>Unload the module instance identified by the specified numeric index.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>suspend-sink</opt> <arg>SINK</arg> <arg>1|0</arg></p>
+
+      <optdesc><p>Suspend or resume the specified sink (which my be
+      specified either by its symbolic name, or by its numeric index),
+      depending whether 1 (suspend) or 0 (resume) is passed as last
+      argument. Suspending a sink will pause all playback. Depending
+      on the module implementing the sink this might have the effect
+      that the underlying device is closed, making it available for
+      other applications to use. The exact behaviour depends on the
+      module.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>suspend-source</opt> <arg>SOURCE</arg> <arg>1|0</arg></p>
+
+      <optdesc><p>Suspend or resume the specified source (which my be
+      specified either by its symbolic name, or by its numeric index),
+      depending whether 1 (suspend) or 0 (resume) is passed as last
+      argument. Suspending a source will pause all
+      capturing. Depending on the module implementing the source this
+      might have the effect that the underlying device is closed,
+      making it available for other applications to use. The exact
+      behaviour depends on the module.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="pacmd" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/padsp.1.xml.in b/man/padsp.1.xml.in
new file mode 100644 (file)
index 0000000..9bbe3d1
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="padsp" section="1" desc="PulseAudio OSS Wrapper">
+
+  <synopsis>
+    <cmd>padsp [<arg>options</arg>] <arg>PROGRAM</arg> [<arg>ARGUMENTS ...</arg>]</cmd>
+    <cmd>padsp <opt>-h</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>padsp</file> starts the specified program and
+    redirects its access to OSS compatible audio devices
+    (<file>/dev/dsp</file> and auxiliary devices) to a PulseAudio
+    sound server.</p>
+
+    <p><file>padsp</file> uses the $LD_PRELOAD environment variable
+    that is interpreted by <manref name="ld.so" section="8"/> and thus
+    does not work for SUID binaries and statically built
+    executables.</p>
+
+    <p>Equivalent to using <file>padsp</file> is starting an
+    application with $LD_PRELOAD set to
+    <file>libpulsedsp.so</file></p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+
+    <option>
+      <p><opt>-s</opt> <arg>SERVER</arg></p>
+
+      <optdesc><p>Set the PulseAudio server to connect to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-n</opt> <arg>NAME</arg></p>
+
+      <optdesc><p>The client application name that shall be passed to the server when connecting.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-m</opt> <arg>NAME</arg></p>
+
+      <optdesc><p>The stream name that shall be passed to the server when creating a stream.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-M</opt></p>
+
+      <optdesc><p>Disable <file>/dev/mixer</file> emulation.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-S</opt></p>
+
+      <optdesc><p>Disable <file>/dev/sndstat</file> emulation.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-D</opt></p>
+
+      <optdesc><p>Disable <file>/dev/dsp</file> emulation.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-d</opt></p>
+
+      <optdesc><p>Enable debug output.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="pasuspender" section="1"/>, <manref name="ld.so" section="8"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/paplay.1.xml.in b/man/paplay.1.xml.in
new file mode 100644 (file)
index 0000000..843b172
--- /dev/null
@@ -0,0 +1,127 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="paplay" section="1" desc="Play back audio files on a PulseAudio sound server">
+
+  <synopsis>
+    <cmd>paplay [<arg>options</arg>] <arg>FILE</arg></cmd>
+    <cmd>paplay <opt>--help</opt></cmd>
+    <cmd>paplay <opt>--version</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>paplay</file> is a simple tool for playing back audio
+    files on a PulseAudio sound server. It understands all audio file
+    formats supported by <file>libsndfile</file>.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-v | --verbose</opt></p>
+
+      <optdesc><p>Enable verbose operation. Dumps the current playback time to STDERR during playback.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-s | --server</opt><arg>=SERVER</arg></p>
+
+      <optdesc><p>Choose the server to connect to.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-d | --device</opt><arg>=SINK</arg></p>
+
+      <optdesc><p>Specify the symbolic name of the sink to play this file on.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-n | --client-name</opt><arg>=NAME</arg></p>
+
+      <optdesc><p>Specify the client name <file>paplay</file> shall pass to the server when connecting.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--stream-name</opt><arg>=NAME</arg></p>
+
+      <optdesc><p>Specify the stream name <file>paplay</file> shall pass to the server when creating the stream.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--volume</opt><arg>=VOLUME</arg></p>
+
+      <optdesc><p>Specify the initial playback volume to use. Choose a value between 0 (silent) and 65536 (100% volume).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--channel-map</opt><arg>=CHANNELMAP</arg></p>
+
+      <optdesc><p>Explicitly choose a channel map when playing back
+      this stream. The argument should be a comma separated list of
+      channel names: <arg>front-left</arg>, <arg>front-right</arg>,
+      <arg>mono</arg>, <arg>front-center</arg>, <arg>rear-left</arg>,
+      <arg>rear-right</arg>, <arg>rear-center</arg>, <arg>lfe</arg>,
+      <arg>front-left-of-center</arg>,
+      <arg>front-right-of-center</arg>, <arg>side-left</arg>,
+      <arg>side-right</arg>, <arg>top-center</arg>,
+      <arg>top-front-center</arg>, <arg>top-front-left</arg>,
+      <arg>top-front-right</arg>, <arg>top-rear-left</arg>,
+      <arg>top-rear-right</arg>, <arg>top-rear-center</arg>, or any of
+      the 32 auxiliary channel names <arg>aux0</arg> to
+      <arg>aux31</arg>.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Limitations">
+
+    <p>Due to a limitation in <file>libsndfile</file>
+    <file>paplay</file> currently does not always set the correct channel
+    mapping for playback of multichannel (i.e. surround) audio files, even if the channel mapping information is
+    available in the audio file.</p>
+
+  </section>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="pacat" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pasuspender.1.xml.in b/man/pasuspender.1.xml.in
new file mode 100644 (file)
index 0000000..52deae6
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="pasuspender" section="1" desc="Temporarily suspend PulseAudio">
+
+  <synopsis>
+    <cmd>pasuspender [<arg>options</arg>] -- <arg>PROGRAM</arg> [<arg>ARGUMENTS ...</arg>]</cmd>
+    <cmd>pasuspender <opt>--help</opt></cmd>
+    <cmd>pasuspender <opt>--version</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p><file>pasuspender</file> is a tool that can be used to tell a
+    local PulseAudio sound server to temporarily suspend access to the
+    audio devices, to allow other
+    applications access them directly. <file>pasuspender</file> will
+    suspend access to the audio devices, fork a child process, and
+    when the child process terminates, resume access again.</p>
+
+    <p>Make sure to include <opt>--</opt> in
+    your <file>pasuspender</file> command line before passing the
+    subprocess command line (as shown
+    above). Otherwise <file>pasuspender</file> itself might end up
+    interpreting the command line switches and options you intended to
+    pass to the subprocess.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-s | --server=</opt><arg>SERVER</arg></p>
+
+      <optdesc><p>Specify the sound server to connect to.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="padsp" section="1"/>, <manref name="pacmd" section="1"/>, <manref name="pactl" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pax11publish.1.xml.in b/man/pax11publish.1.xml.in
new file mode 100644 (file)
index 0000000..3b40b97
--- /dev/null
@@ -0,0 +1,151 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="pax11publish" section="1" desc="PulseAudio X11 Credential Utility">
+
+  <synopsis>
+    <cmd>pax11publish <opt>-h</opt></cmd>
+    <cmd>pax11publish [<arg>options</arg>] [<opt>-d</opt>]</cmd>
+    <cmd>pax11publish [<arg>options</arg>] <opt>-e</opt></cmd>
+    <cmd>pax11publish [<arg>options</arg>] <opt>-i</opt></cmd>
+    <cmd>pax11publish [<arg>options</arg>] <opt>-r</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p>The <file>pax11publish</file> utility can be used to dump or
+    manipulate the PulseAudio server credentials that can be stored as
+    properties on the X11 root window.</p>
+
+    <p>Please note that the loadable module
+    <file>module-x11-publish</file> exports the same information
+    directly from the PulseAudio sound server, and should in most
+    cases be used in preference over this tool.</p>
+
+    <p>Use the following command to dump the raw
+    PulseAudio-specific data that is stored in your X11 root
+    window:</p>
+
+    <p>xprop -root | grep ^PULSE_</p>
+
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-d</opt></p>
+
+      <optdesc><p>Read the PulseAudio server credentials currently set
+      on the X11 root window and dump them in a human readable form. This reads the
+      PULSE_SERVER, PULSE_SINK, PULSE_SOURCE and PULSE_COOKIE
+      properties.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-i</opt></p>
+      <optdesc><p>Similar to <opt>-d</opt>, however dumps them in a
+      Bourne shell compatible format so they may be used together with
+      the <file>eval</file> shell command to set the $PULSE_SERVER,
+      $PULSE_SINK, $PULSE_SOURCE environment variables. Also reads the
+      authentication cookie from the root window and stores it in
+      <file>~/.pulse-cookie</file>. </p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-e</opt></p>
+
+      <optdesc><p>Export the currently locally used sound server,
+      sink, source configuration to the X11 root window. This takes
+      the data from the $PULSE_SERVER, $PULSE_SINK, $PULSE_SOURCE
+      environment variables and combines them with the data from
+      <file>~/.pulse/client.conf</file> (or
+      <file>@pulseconfdir@/client.conf</file> if that file does not
+      exist). If specific options are passed on the command line
+      (<opt>-S</opt>, <opt>-O</opt>, <opt>-I</opt>, <opt>-c</opt>, see
+      below), they take precedence. Also uploads the local
+      authentication cookie <file>~/.pulse-cookie</file> to the X11
+      server.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-r</opt></p>
+
+      <optdesc><p>Removes the configured PulseAudio configuration from the X11 root window.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-D</opt> <arg>DISPLAY</arg></p>
+
+      <optdesc><p>Connect to the specified X11 display, instead of the default one configured in $DISPLAY.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-S</opt> <arg>SERVER</arg></p>
+
+      <optdesc><p>Only valid for <opt>-e</opt>: export the specified
+      PulseAudio server as default to the X11 display instead of the
+      one configured via local configuration.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-O</opt> <arg>SINK</arg></p>
+
+      <optdesc><p>Only valid for <opt>-e</opt>: export the specified
+      sink as default sink to the X11 display instead of the one
+      configured via local configuration.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-I</opt> <arg>SOURCE</arg></p>
+
+      <optdesc><p>Only valid for <opt>-e</opt>: export the specified
+      source as default to the X11 display instead of the one
+      configured via local configuration.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-c</opt> <arg>FILE</arg></p>
+
+      <optdesc><p>Only valid for <opt>-e</opt>: export the PulseAudio
+      authentication cookie stored in the specified file to the X11
+      display instead of the one stored in <file>~/.pulse-cookie</file>.</p></optdesc>
+    </option>
+
+  </options>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulseaudio" section="1"/>, <manref name="xprop" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pulse-client.conf.5.xml.in b/man/pulse-client.conf.5.xml.in
new file mode 100644 (file)
index 0000000..ae8de1f
--- /dev/null
@@ -0,0 +1,113 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="pulse-client.conf" section="5" desc="PulseAudio client configuration file">
+
+  <synopsis>
+    <p><file>~/.pulse/client.conf</file></p>
+
+    <p><file>@pulseconfdir@/client.conf</file></p>
+  </synopsis>
+
+  <description>
+    <p>The PulseAudio client library reads configuration directives from
+    a file <file>~/.pulse/client.conf</file> on startup, and when that
+    file doesn't exist from
+    <file>@pulseconfdir@/client.conf</file>.</p>
+
+    <p>The configuration file is a simple collection of variable
+    declarations. If the configuration file parser encounters either ;
+    or # for it ignores the rest of the line until its end.</p>
+
+    <p>For the settings that take a boolean argument, the values
+    <opt>true</opt>, <opt>yes</opt>, <opt>on</opt> and <opt>1</opt>
+    are equivalent, resp. <opt>false</opt>, <opt>no</opt>,
+    <opt>off</opt>, <opt>0</opt>.</p>
+
+  </description>
+
+  <section name="Directives">
+
+    <option>
+      <p><opt>default-sink=</opt> The default sink to connect to. If
+      specified overwrites the setting in the daemon. The environment
+      variable <opt>$PULSE_SINK</opt> however takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>default-source=</opt> The default source to connect
+      to. If specified overwrites the setting in the daemon. The
+      environment variable <opt>$PULSE_SOURCE</opt> however takes
+      precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>default-server=</opt> The default sever to connect
+      to. The environment variable <opt>$PULSE_SERVER</opt> takes
+      precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>autospawn=</opt> Autospawn a PulseAudio daemon when
+      needed. Takes a boolean value, defaults to "no".</p>
+    </option>
+
+    <option>
+      <p><opt>daemon-binary=</opt> Path to the PulseAudio daemon to
+      run when autospawning. Defaults to a path configured at compile
+      time.</p>
+    </option>
+
+    <option>
+      <p><opt>extra-arguments=</opt> Extra arguments to pass to the
+      PulseAudio daemon when autospawning. Defaults to
+      <opt>--log-target=syslog --exit-idle-time=5</opt>
+      </p>
+    </option>
+
+    <option>
+      <p><opt>cookie-file=</opt> Specify the path to the PulseAudio
+      authentication cookie. Defaults to
+      <file>~/.pulse-cookie</file>.</p>
+    </option>
+
+    <option>
+      <p><opt>disable-shm=</opt> Disable data transfer via POSIX
+      shared memory. Takes a boolean argument, defaults to
+      <opt>no</opt>.</p>
+    </option>
+
+  </section>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;;
+    PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulse-daemon.conf" section="5"/>, <manref name="pulseaudio" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pulse-daemon.conf.5.xml.in b/man/pulse-daemon.conf.5.xml.in
new file mode 100644 (file)
index 0000000..50e2455
--- /dev/null
@@ -0,0 +1,370 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="pulse-daemon.conf" section="5" desc="PulseAudio daemon configuration file">
+
+  <synopsis>
+    <p><file>~/.pulse/daemon.conf</file></p>
+
+    <p><file>@pulseconfdir@/daemon.conf</file></p>
+  </synopsis>
+
+  <description>
+    <p>The PulseAudio sound server reads configuration directives from
+    a file <file>~/.pulse/daemon.conf</file> on startup, and when that
+    file doesn't exist from
+    <file>@pulseconfdir@/daemon.conf</file>. Please note that the
+    server also reads a configuration script on startup
+    <file>default.pa</file> which also contains runtime configuration
+    directives.</p>
+
+    <p>The configuration file is a simple collection of variable
+    declarations. If the configuration file parser encounters either ;
+    or # for it ignores the rest of the line until its end.</p>
+
+    <p>For the settings that take a boolean argument, the values
+    <opt>true</opt>, <opt>yes</opt>, <opt>on</opt> and <opt>1</opt>
+    are equivalent, resp. <opt>false</opt>, <opt>no</opt>,
+    <opt>off</opt>, <opt>0</opt>.</p>
+
+  </description>
+
+  <section name="General Directives">
+
+    <option>
+      <p><opt>daemonize= </opt> Daemonize after startup. Takes a
+      boolean value, defaults to "no". The <opt>--daemonize</opt>
+      command line option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>fail=</opt> Fail to start up if any of the directives
+      in the configuration script <file>default.pa</file>
+      fail. Takes a boolean argument, defaults to "yes". The <opt>--fail</opt> command line
+      option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>disallow-module-loading=</opt> Disallow module loading
+      after startup. This is a security feature that makes sure that
+      no further modules may be loaded into the PulseAudio server
+      after startup completed. It is recommended to enable this when
+      <opt>system-instance</opt> is enabled. Please note that certain
+      features like automatic hot-plug support will not work if this
+      option is enabled. Takes a boolean argument, defaults to
+      <opt>no</opt>. The <opt>--disallow-module-loading</opt> command line
+      option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>resample-method=</opt> The resampling algorithm to
+      use. Use one of <opt>src-sinc-best-quality</opt>,
+      <opt>src-sinc-medium-quality</opt>, <opt>src-sinc-fastest</opt>,
+      <opt>src-zero-order-hold</opt>, <opt>src-linear</opt>,
+      <opt>trivial</opt>, <opt>speex-float-N</opt>,
+      <opt>speex-fixed-N</opt>, <opt>ffmpeg</opt>. See the
+      documentation of libsamplerate for an explanation for the
+      different src- methods. The method <opt>trivial</opt> is the most basic
+      algorithm implemented. If you're tight on CPU consider using
+      this. On the other hand it has the worst quality of them
+      all. The Speex resamplers take an integer quality setting in the
+      range 0..9 (bad...good). They exist in two flavours: <opt>fixed</opt> and
+      <opt>float</opt>. The former uses fixed point numbers, the latter relies on
+      floating point numbers. On most desktop CPUs the float point
+      resmampler is a lot faster, and it also offers slightly better
+      quality. See the output of <opt>dump-resample-methods</opt> for
+      a complete list of all available resamplers. Defaults to
+      <opt>speex-float-3</opt>. The <opt>--resample-method</opt>
+      command line option takes precedence. Note that some modules
+      overwrite or allow overwriting of the resampler to use.</p>
+    </option>
+
+    <option>
+      <p><opt>disable-remixing=</opt> Never upmix or downmix channels
+      to different channel maps. Instead, do a simple name-based
+      matching only.</p>
+    </option>
+
+    <option>
+      <p><opt>use-pid-file=</opt> Create a PID file in
+      <file>/tmp/pulse-$USER/pid</file>. Of this is enabled you may
+      use commands like <opt>--kill</opt> or <opt>--check</opt>. If
+      you are planning to start more than one PulseAudio process per
+      user, you better disable this option since it effectively
+      disables multiple instances. Takes a boolean argument, defaults
+      to <opt>yes</opt>. The <opt>--no-cpu-limit</opt> command line
+      option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>no-cpu-limit=</opt> Do not install the CPU load limiter,
+      even on platforms where it is supported. This option is useful
+      when debugging/profiling PulseAudio to disable disturbing
+      SIGXCPU signals. Takes a boolean argument, defaults to <opt>no</opt>. The
+      <opt>--no-cpu-limit</opt> command line argument takes
+      precedence.</p>
+    </option>
+
+
+    <option>
+      <p><opt>system-instance=</opt> Run the daemon as system-wide
+      instance, requires root priviliges. Takes a boolean argument,
+      defaults to <opt>no</opt>. The <opt>--system</opt> command line
+      argument takes precedence.</p>
+    </option>
+
+
+    <option>
+      <p><opt>disable-shm=</opt> Disable data transfer via POSIX
+      shared memory. Takes a boolean argument, defaults to
+      <opt>no</opt>. The <opt>--disable-shm</opt> command line
+      argument takes precedence.</p>
+    </option>
+
+  </section>
+
+  <section name="Scheduling">
+
+    <option>
+      <p><opt>high-priority=</opt> Renice the daemon after startup to
+      become a high-priority process. This a good idea if you
+      experience drop-outs during playback. However, this is a certain
+      security issue, since it works when called SUID root only, or
+      RLIMIT_NICE is used. root is dropped immediately after gaining
+      the nice level on startup, thus it is presumably safe. See
+      <manref section="1" name="pulseaudio"/> for more
+      information. Takes a boolean argument, defaults to "yes". The <opt>--high-priority</opt>
+      command line option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>realtime-scheduling=</opt> Try to acquire SCHED_FIFO
+      scheduling for the IO threads. The same security concerns as
+      mentioned above apply. However, if PA enters an endless loop,
+      realtime scheduling causes a system lockup. Thus, realtime
+      scheduling should only be enabled on trusted machines for
+      now. Please not that only the IO threads of PulseAudio are made
+      real-time. The controlling thread is left a normally scheduled
+      thread. Thus enabling the high-priority option is orthogonal.
+      See <manref section="1" name="pulseaudio"/> for more
+      information. Takes a boolean argument, defaults to "no". The
+      <opt>--realtime</opt> command line option takes precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>realtime-priority=</opt> The realtime priority to
+      acquire, if <opt>realtime-scheduling</opt> is enabled. Note: JACK uses 10
+      by default, 9 for clients. Thus it is recommended to choose the
+      PulseAudio real-time priorities lower. Some PulseAudio threads
+      might choose a priority a little lower or higher than the
+      specified value. Defaults to "5".</p>
+    </option>
+
+    <option>
+      <p><opt>nice-level=</opt> The nice level to acquire for the
+      daemon, if <opt>high-priority</opt> is enabled. Note: on some
+      distributions X11 uses -10 by default. Defaults to -11.</p>
+    </option>
+
+  </section>
+
+  <section name="Idle Times">
+
+    <option>
+      <p><opt>exit-idle-time=</opt> Terminate the daemon after the
+      last client quit and this time in seconds passed. Use a negative value to
+      disable this feature. Defaults to -1. The
+      <opt>--exit-idle-time</opt> command line option takes
+      precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>module-idle-time=</opt> Unload autoloaded modules after
+      being idle for this time in seconds. Defaults to 20. The
+      <opt>--module-idle-time</opt> command line option takes
+      precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>scache-idle-time=</opt> Unload autoloaded sample cache
+      entries after being idle for this time in seconds. Defaults to
+      20. The <opt>--scache-idle-time</opt> command line option takes
+      precedence.</p>
+    </option>
+
+  </section>
+
+  <section name="Paths">
+
+    <option>
+      <p><opt>dl-search-path=</opt> The path were to look for dynamic
+      shared objects (DSOs/plugins). You may specify more than one
+      path seperated by colons. The default path depends on compile
+      time settings. The <opt>--dl-search-path</opt> command line
+      option takes precedence. </p>
+    </option>
+
+    <option>
+      <p><opt>default-script-file=</opt> The default configuration
+      script file to load. Specify an empty string for not loading a
+      default script file. The default behaviour is to load
+      <file>~/.pulse/default.pa</file>, and if that file does not
+      exist fall back to the system wide installed version
+      <file>@pulseconfdir@/default.pa</file>. If <opt>-n</opt> is
+      passed on the command line the default configuration script is
+      ignored.</p>
+    </option>
+
+  </section>
+
+  <section name="Logging">
+
+    <option>
+      <p><opt>log-target=</opt> The default log target. Use either
+      <opt>stderr</opt>, <opt>syslog</opt> or <opt>auto</opt>. The
+      latter is equivalent to <opt>sylog</opt> in case
+      <opt>daemonize</opt> is enabled, otherwise to
+      <opt>stderr</opt>. Defaults to <opt>auto</opt>. The
+      <opt>--log-target</opt> command line option takes
+      precedence.</p>
+    </option>
+
+    <option>
+      <p><opt>log-level=</opt> Log level, one of <opt>debug</opt>,
+      <opt>info</opt>, <opt>notice</opt>, <opt>warning</opt>,
+      <opt>error</opt>. Log messages with a lower log level than
+      specified here are not logged. Defaults to
+      <opt>notice</opt>. The <opt>--log-level</opt> command line
+      option takes precedence. The <opt>-v</opt> command line option
+      might alter this setting.</p>
+    </option>
+
+  </section>
+
+  <section name="Resource Limits">
+
+    <p>See <manref name="getrlimit" section="2"/> for
+    more information. Set to -1 if PulseAudio shall not touch the resource
+    limit. Not all resource limits are available on all operating
+    systems.</p>
+
+    <option>
+      <p><opt>rlimit-as</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-core</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-data</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-fsize</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-nofile</opt> Defaults to 256.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-stack</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-nproc</opt> Defaults to -1.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-memlock</opt> Defaults to 16 KiB. Please note
+      that the JACK client libraries may require more locked
+      memory.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-nice</opt> Defaults to 31. Please make sure that
+      the default nice level as configured with <opt>nice-level</opt>
+      fits in this resource limit, if <opt>high-priority</opt> is
+      enabled.</p>
+    </option>
+    <option>
+      <p><opt>rlimit-rtprio</opt> Defaults to 9. Please make sure that
+      the default real-time priority level as configured with
+      <opt>realtime-priority=</opt> fits in this resource limit, if
+      <opt>realtime-scheduling</opt> is enabled. The JACK client
+      libraries require a real-time prority of 9 by default. </p>
+    </option>
+
+  </section>
+
+  <section name="Default Device Settings">
+
+    <p>Most drivers try to open the audio device with these settings
+    and then fall back to lower settings. The default settings are CD
+    quality: 16bit native endian, 2 channels, 44100 Hz sampling.</p>
+
+    <option>
+      <p><opt>default-sample-format=</opt> The default sampling
+      format. Specify one of <opt>u8</opt>, <opt>s16le</opt>,
+      <opt>s16be</opt>, <opt>float32le</opt>, <opt>float32be</opt>,
+      <opt>ulaw</opt>, <opt>alaw</opt>. Depending on the endianess of
+      the CPU the formats <opt>s16ne</opt>, <opt>s16re</opt>,
+      <opt>float32ne</opt>, <opt>float32re</opt> (for native,
+      resp. reverse endian) are available as aliases.</p>
+    </option>
+
+    <option>
+      <p><opt>default-sample-rate=</opt> The default sample frequency.</p>
+    </option>
+
+    <option>
+      <p><opt>default-sample-channels</opt> The default number of channels.</p>
+    </option>
+
+  </section>
+
+  <section name="Default Fragment Settings">
+
+    <p>Some hardware drivers require the hardware playback buffer to
+    be subdivided into several fragments. It is possible to change
+    these buffer metrics for machines with high scheduling
+    latencies. Not all possible values that may be configured here are
+    available in all hardware. The driver will to find the nearest
+    setting supported.</p>
+
+    <option>
+      <p><opt>default-fragments=</opt> The default number of
+      fragments. Defaults to 4.</p>
+    </option>
+    <option>
+      <p><opt>default-fragment-size-msec=</opt>The duration of a
+      single fragment. Defaults to 25ms (i.e. the total buffer is thus
+      100ms long).</p>
+    </option>
+
+  </section>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulse-client.conf" section="5"/>, <manref name="default.pa" section="5"/>, <manref name="pulseaudio" section="1"/>, <manref name="pacmd" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/pulseaudio.1.xml.in b/man/pulseaudio.1.xml.in
new file mode 100644 (file)
index 0000000..9ce66f8
--- /dev/null
@@ -0,0 +1,455 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE manpage SYSTEM "xmltoman.dtd">
+<?xml-stylesheet type="text/xsl" href="xmltoman.xsl" ?>
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<manpage name="pulseaudio" section="1" desc="The PulseAudio Sound System">
+
+  <synopsis>
+    <cmd>pulseaudio [<arg>options</arg>]</cmd>
+    <cmd>pulseaudio <opt>--help</opt></cmd>
+    <cmd>pulseaudio <opt>--version</opt></cmd>
+    <cmd>pulseaudio <opt>--dump-conf</opt></cmd>
+    <cmd>pulseaudio <opt>--dump-modules</opt></cmd>
+    <cmd>pulseaudio <opt>--dump-resample-methods</opt></cmd>
+    <cmd>pulseaudio <opt>--cleanup-shm</opt></cmd>
+    <cmd>pulseaudio <opt>--start</opt></cmd>
+    <cmd>pulseaudio <opt>--kill</opt></cmd>
+    <cmd>pulseaudio <opt>--check</opt></cmd>
+  </synopsis>
+
+  <description>
+    <p>PulseAudio is a networked low-latency sound server for Linux, POSIX and Windows systems.</p>
+  </description>
+
+  <options>
+
+    <option>
+      <p><opt>-h | --help</opt></p>
+
+      <optdesc><p>Show help.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--version</opt></p>
+
+      <optdesc><p>Show version information.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--dump-conf</opt></p>
+
+      <optdesc><p>Load the daemon configuration file
+      <file>daemon.conf</file> (see below), parse remaining
+      configuration options on the command line and dump the resulting
+      daemon configuration, in a format that is compatible with
+      <file>daemon.conf</file>.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--dump-modules</opt></p>
+
+      <optdesc><p>List available loadable modules. Combine with
+      <opt>-v</opt> for a more elaborate listing.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--dump-resampe-methods</opt></p>
+      <optdesc><p>List available audio resamplers.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--cleanup-shm</opt></p>
+
+      <optdesc><p>Identify stale PulseAudio POSIX shared memory
+      segments in <file>/dev/shm</file> and remove them if
+      possible. This is done implicitly whenever a new daemon starts
+      up or a client tries to connect to a daemon. It should normally
+      not be necessary to issue this command by hand. Only available
+      on systems with POSIX shared memory segments implemented via a
+      virtual file system mounted to <file>/dev/shm</file>
+      (e.g. Linux).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--start</opt></p>
+
+      <optdesc><p>Start PulseAudio if it is not running yet. This is
+      different from starting PulseAudio without <opt>--start</opt>
+      which would fail if PA is already running. PulseAudio is
+      guaranteed to be fully initialized when this call
+      returns. Implies <opt>--daemon</opt>.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-k | --kill</opt></p>
+
+      <optdesc><p>Kill an already running PulseAudio daemon of the
+      calling user (Equivalent to sending a SIGTERM).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--check</opt></p>
+
+      <optdesc><p>Return 0 as return code when the PulseAudio daemon
+      is already running for the calling user.</p></optdesc>
+    </option>
+
+
+    <option>
+      <p><opt>--system</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Run as system-wide instance instead of
+      per-user. Please not that this disables certain features of
+      PulseAudio and is generally not recommended unless the system
+      knows no local users (e.g. is a thin client). This feature needs
+      special configuration and a dedicated UNIX user set up. It is
+      highly recommended to combine this with
+      <opt>--disallow-module-loading</opt> (see below).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-D | --daemon</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Daemonize after startup, i.e. detach from the
+      terminal.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--fail</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Fail startup when any of the commands specified in
+      the startup script <file>default.pa</file> (see below)
+      fails.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--high-priority</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Try to acquire a high Unix nice level. This will
+      only succeed if the calling user has a non-zero RLIMIT_NICE
+      resource limit set (on systems that support this), or we're
+      called SUID root (see below), or we are configure to be run as
+      system daemon (see <arg>--system</arg> above). It is recommended
+      to enable this, since it is only a negligible security risk (see
+      below).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--realtime</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Try to acquire a real-time scheduling for
+      PulseAudio's I/O threads. This will only succeed if the calling
+      user has a non-zero RLIMIT_RTPRIO resource limit set (on systems
+      that support this), or we're called SUID root (see below), or we
+      are configure to be run as system daemon (see
+      <arg>--system</arg> above). It is recommended to enable this
+      only for trusted users, since it is a major security risk (see
+      below).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--disallow-module-loading</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Disallow module loading after startup. This is a
+      security feature since it disallows additional module loading
+      during runtime and on user request. It is highly recommended
+      when <arg>--system</arg> is used (see above). Note however, that
+      this breaks certain features like automatic module loading on hot
+      plug.</p></optdesc>
+
+    </option>
+
+    <option>
+      <p><opt>--exit-idle-time</opt><arg>=SECS</arg></p>
+
+      <optdesc><p>Terminate the daemon when idle and the specified
+      number of seconds passed.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--module-idle-time</opt><arg>=SECS</arg></p>
+
+      <optdesc><p>Unload autoloaded modules when idle and the
+      specified number of seconds passed.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--scache-idle-time</opt><arg>=SECS</arg></p>
+
+      <optdesc><p>Unload autoloaded samples from the cache when the
+      haven't been used for the specified number of
+      seconds.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--log-level</opt><arg>[=LEVEL]</arg></p>
+
+      <optdesc><p>If an argument is passed, set the log level to the
+      specified value, otherwise increase the configured verbosity
+      level by one. The log levels are numerical from 0 to 4,
+      corresponding to <arg>error</arg>, <arg>warn</arg>,
+      <arg>notice</arg>, <arg>info</arg>, <arg>debug</arg>. Default
+      log level is <arg>notice</arg>, i.e. all log messages with lower
+      log levels are printed: <arg>error</arg>, <arg>warn</arg>,
+      <arg>notice</arg>.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-v</opt></p>
+
+      <optdesc><p>Increase the configured verbosity level by one (see
+      <opt>--log-level</opt> above). Specify multiple times to
+      increase log level multiple times.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--log-target</opt><arg>={auto,syslog,stderr}</arg></p>
+
+      <optdesc><p>Specify the log target. If set to <arg>auto</arg>
+      (which is the default), then logging is directed to syslog when
+      <opt>--daemonize</opt> is passed, otherwise to
+      STDERR.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--p | --dl-search-path</opt><arg>=PATH</arg></p>
+
+      <optdesc><p>Set the search path for dynamic shared objects
+      (plugins).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--resample-method</opt><arg>=METHOD</arg></p>
+
+      <optdesc><p>Use the specified resampler by default (See
+      <opt>--dump-resample-methods</opt> above for possible
+      values).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--use-pid-file</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Create a PID file. If this options is disabled it is possible to run multiple sound servers per user.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--no-cpu-limit</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>Do not install CPU load limiter on platforms that
+      support it. By default, PulseAudio will terminate itself when it
+      notices that it takes up too much CPU time. This is useful as a
+      protection against system lockups when real-time scheduling is
+      used (see below). Disabling this meachnism is useful when
+      debugging PulseAudio with tools like <manref name="valgrind"
+      section="1"/> which slow down execution.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>--disable-shm</opt><arg>[=BOOL]</arg></p>
+
+      <optdesc><p>PulseAudio clients and the server can exchange audio
+      data via POSIX shared memory segments (on systems that support
+      this). If disabled PulseAudio will communicate exclusively over
+      sockets. Please note that data transfer via shared memory
+      segments is always disabled when PulseAudio is running with
+      <opt>--system</opt> enabled (see above).</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-L | --load</opt><arg>="MODULE ARGUMENTS"</arg></p>
+
+      <optdesc><p>Load the specified plugin module with the specified
+      arguments.</p></optdesc>
+    </option>
+
+    <option>
+      <p><opt>-F | --file</opt><arg>=FILENAME</arg></p>
+
+      <optdesc><p>Run the specified script on startup. May be
+      specified multiple times to specify multiple scripts to be run
+      in order. Combine with <opt>-n</opt> to disable loading of the
+      default script <file>default.pa</file> (see below).</p></optdesc>
+    </option>
+    <option>
+      <p><opt>-C</opt></p>
+
+      <optdesc><p>Open a command interpreter on STDIN/STDOUT after
+      startup. This may be used to configure PulseAudio dynamically
+      during runtime. Equivalent to
+      <opt>--load</opt><arg>=module-cli</arg>.</p></optdesc>
+    </option>
+    <option>
+      <p><opt>-n</opt></p>
+
+      <optdesc><p>Don't load default script file
+      <file>default.pa</file> (see below) on startup. Useful in
+      conjunction with <opt>-C</opt> or
+      <opt>--file</opt>.</p></optdesc>
+    </option>
+
+
+  </options>
+
+  <section name="Files">
+
+    <p><file>~/.pulse/daemon.conf</file>,
+    <file>@pulseconfdir@/daemon.conf</file>: configuration settings
+    for the PulseAudio daemon. If the version in the user's home
+    directory does not exist the global configuration file is
+    loaded. See <manref name="pulse-daemon.conf" section="5"/> for
+    more information.</p>
+
+    <p><file>~/.pulse/default.pa</file>,
+    <file>@pulseconfdir@/default.pa</file>: the default configuration
+    script to execute when the PulseAudio daemon is started. If the
+    version in the user's home directory does not exist the global
+    configuration script is loaded. See <manref name="default.pa"
+    section="5"/> for more information.</p>
+
+    <p><file>~/.pulse/client.conf</file>,
+    <file>@pulseconfdir@/client.conf</file>: configuration settings
+    for PulseAudio client applications. If the version in the user's
+    home directory does not exist the global configuration file is
+    loaded.  See <manref name="pulse-client.conf" section="5"/> for
+    more information.</p>
+
+  </section>
+
+  <section name="Signals">
+
+    <p><arg>SIGINT, SIGTERM</arg>: the PulseAudio daemon will shut
+    down (Same as <opt>--kill</opt>).</p>
+
+    <p><arg>SIGHUP</arg>: dump a long status report to STDOUT or
+    syslog, depending on the configuration.</p>
+
+    <p><arg>SIGUSR1</arg>: load module-cli, allowing runtime
+    reconfiguration via STDIN/STDOUT.</p>
+
+    <p><arg>SIGUSR2</arg>: load module-cli-protocol-unix, allowing
+    runtime reconfiguration via a AF_UNIX socket. See <manref
+    name="pacmd" section="1"/> for more information.</p>
+
+  </section>
+
+  <section name="UNIX Groups and users">
+
+    <p>Group <arg>pulse-rt</arg>: if the PulseAudio binary is marked
+    SUID root, then membership of the calling user in this group
+    decides whether real-time and/or high-priority scheduling is
+    enabled. Please note that enabling real-time scheduling is a
+    security risk (see below).</p>
+
+    <p>Group <arg>pulse-access</arg>: if PulseAudio is running as a system
+    daemon (see <opt>--system</opt> above) access is granted to
+    members of this group when they connect via AF_UNIX sockets. If
+    PulseAudio is running as a user daemon this group has no
+    meaning.</p>
+
+    <p>User <arg>pulse</arg>, group <arg>pulse</arg>: if PulseAudio is running as a system
+    daemon (see <opt>--system</opt> above) and is started as root the
+    daemon will drop priviliges and become a normal user process using
+    this user and group. If PulseAudio is running as a user daemon
+    this user and group has no meaning.</p>
+  </section>
+
+  <section name="Real-time and high-priority scheduling">
+    <p>To minimize the risk of drop-outs during playback it is
+    recommended to run PulseAudio with real-time scheduling if the
+    underlying platform supports it. This decouples the scheduling
+    latency of the PulseAudio daemon from the system load and is thus
+    the best way to make sure that PulseAudio always gets CPU time
+    when it needs it to refill the hardware playback
+    buffers. Unfortunately this is a security risk on most systems,
+    since PulseAudio runs as user process, and giving realtime
+    scheduling priviliges to a user process always comes with the risk
+    that the user misuses it to lock up the system -- which is
+    possible since making a process real-time effectively disables
+    preemption.</p>
+
+    <p>To minimize the risk PulseAudio by default does not enable
+    real-time scheduling. It is however recommended to enable it
+    on trusted systems. To do that start PulseAudio with
+    <opt>--realtime</opt> (see above) or enabled the appropriate option in
+    <file>daemon.conf</file>. Since acquiring realtime scheduling is a
+    priviliged operation on most systems, some special changes to the
+    system configuration need to be made to allow them to the calling
+    user. Two options are available:</p>
+
+    <p>On newer Linux systems the system resource limit RLIMIT_RTPRIO
+    (see <manref name="setrlimit" section="2"/> for more information)
+    can be used to allow specific users to acquire real-time
+    scheduling. This can be configured in
+    <file>/etc/security/limits.conf</file>, a resource limit of 9 is recommended.</p>
+
+    <p>Alternatively, the SUID root bit can be set for the PulseAudio
+    binary. Then, the daemon will drop root priviliges immediately on
+    startup, however retain the CAP_NICE capability (on systems that
+    support it), but only if the calling user is a member of the
+    <arg>pulse-rt</arg> group (see above). For all other users all
+    capababilities are dropped immediately. The advantage of this
+    solution is that the real-time priviliges are only granted to the
+    PulseAudio daemon -- not to all the user's processes.</p>
+
+    <p>Alternatively, if the risk of locking up the machine is
+    considered too big to enable real-time scheduling, high-priority
+    scheduling can be enabled instead (i.e. negative nice level). This
+    can be enabled by passing <opt>--high-priority</opt> (see above)
+    when starting PulseAudio and may also be enabled with the
+    approriate option in <file>daemon.conf</file>. Negative nice
+    levels can only be enabled when the appropriate resource limit
+    RLIMIT_NICE is set (see <manref name="setrlimit" section="2"/> for
+    more information), possibly configured in
+    <file>/etc/security/limits.conf</file>. A resource limit of 31
+    (corresponding with nice level -11) is recommended.</p>
+  </section>
+
+  <section name="Environment variables">
+
+    <p>The PulseAudio client libraries check for the existance of the
+    following environment variables and change their local configuration accordingly:</p>
+
+    <p><arg>$PULSE_SERVER</arg>: the server string specifying the server to connect to when a client asks for a sound server connection and doesn't explicitly ask for a specific server.</p>
+
+    <p><arg>$PULSE_SINK</arg>: the symbolic name of the sink to connect to when a client creates a playback stream and doesn't explicitly ask for a specific sink.</p>
+
+    <p><arg>$PULSE_SOURCE</arg>: the symbolic name of the source to connect to when a client creates a record stream and doesn't explicitly ask for a specific source.</p>
+
+    <p><arg>$PULSE_BINARY</arg>: path of PulseAudio executable to run when server auto-spawning is used.</p>
+
+    <p><arg>$PULSE_CLIENTCONFIG</arg>: path of file that shall be read instead of <file>client.conf</file> (see above) for client configuration.</p>
+
+    <p>These environment settings take precedence -- if set -- over the configuration settings from <file>client.conf</file> (see above).</p>
+
+  </section>
+
+  <section name="Authors">
+    <p>The PulseAudio Developers &lt;@PACKAGE_BUGREPORT@&gt;; PulseAudio is available from <url href="@PACKAGE_URL@"/></p>
+  </section>
+
+  <section name="See also">
+    <p>
+      <manref name="pulse-daemon.conf" section="5"/>, <manref name="default.pa" section="5"/>, <manref name="pulse-client.conf" section="5"/>, <manref name="pacmd" section="1"/>
+    </p>
+  </section>
+
+</manpage>
diff --git a/man/xmltoman.css b/man/xmltoman.css
new file mode 100644 (file)
index 0000000..113aeec
--- /dev/null
@@ -0,0 +1,28 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio 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.
+
+  PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 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 PulseAudio; if not, write to the Free Software Foundation,
+  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 
+***/
+
+body { color: black; background-color: white; } 
+a:link, a:visited { color: #900000; }       
+h1 { text-transform:uppercase; font-size: 18pt; } 
+p { margin-left:1cm; margin-right:1cm; } 
+.cmd { font-family:monospace; }
+.file { font-family:monospace; }
+.arg { text-transform:uppercase; font-family:monospace; font-style: italic; }
+.opt { font-family:monospace; font-weight: bold;  }
+.manref { font-family:monospace; }
+.option .optdesc { margin-left:2cm; }
diff --git a/man/xmltoman.dtd b/man/xmltoman.dtd
new file mode 100644 (file)
index 0000000..4760638
--- /dev/null
@@ -0,0 +1,37 @@
+<!--
+  This file is part of PulseAudio.
+
+  PulseAudio 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.
+
+  PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 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 PulseAudio; if not, write to the Free Software Foundation,
+  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+
+<!ELEMENT manpage (synopsis | description | section | options | seealso)*>
+<!ATTLIST manpage name CDATA #REQUIRED section CDATA #REQUIRED desc CDATA #IMPLIED>
+<!ELEMENT arg (#PCDATA)>
+<!ELEMENT p (#PCDATA | arg | url | manref | opt | file )*>
+<!ELEMENT synopsis (cmd | p)+>
+<!ELEMENT description (p)+>
+<!ELEMENT section (p | option)*>
+<!ATTLIST section name CDATA #REQUIRED>
+<!ELEMENT option (#PCDATA | p | optdesc)*>
+<!ELEMENT optdesc (#PCDATA | p )*>
+<!ELEMENT cmd (#PCDATA | arg | opt)*>
+<!ELEMENT options (p | option)*>
+<!ELEMENT seealso (p)*>
+<!ELEMENT opt (#PCDATA)>
+<!ELEMENT file (#PCDATA)>
+<!ELEMENT manref EMPTY>
+<!ATTLIST manref name CDATA #REQUIRED section CDATA #REQUIRED href CDATA #IMPLIED>
+<!ELEMENT url EMPTY>
+<!ATTLIST url href CDATA #REQUIRED>
diff --git a/man/xmltoman.xsl b/man/xmltoman.xsl
new file mode 100644 (file)
index 0000000..766ab25
--- /dev/null
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="iso-8859-15"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
+
+<!--
+  This file is part of PulseAudio.
+
+  PulseAudio 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.
+
+  PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 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 PulseAudio; if not, write to the Free Software Foundation,
+  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+
+<xsl:output method="xml" version="1.0" encoding="iso-8859-15" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes"/>
+
+<xsl:template match="/manpage">
+
+    <html>
+
+    <head>
+      <title><xsl:value-of select="@name"/>(<xsl:value-of select="@section"/>)</title>
+      <style type="text/css">
+        body { color: black; background-color: white; }
+        a:link, a:visited { color: #900000; }
+        h1 { text-transform:uppercase; font-size: 18pt; }
+        p { margin-left:1cm; margin-right:1cm; }
+        .cmd { font-family:monospace; }
+        .file { font-family:monospace; }
+        .arg { text-transform:uppercase; font-family:monospace; font-style: italic; }
+        .opt { font-family:monospace; font-weight: bold;  }
+        .manref { font-family:monospace; }
+        .option .optdesc { margin-left:2cm; }
+      </style>
+    </head>
+    <body>
+      <h1>Name</h1>
+      <p><xsl:value-of select="@name"/>
+        <xsl:if test="string-length(@desc) &gt; 0"> - <xsl:value-of select="@desc"/></xsl:if>
+      </p>
+      <xsl:apply-templates />
+    </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="p">
+ <p>
+  <xsl:apply-templates/>
+ </p>
+</xsl:template>
+
+<xsl:template match="cmd">
+ <p class="cmd">
+  <xsl:apply-templates/>
+ </p>
+</xsl:template>
+
+<xsl:template match="arg">
+  <span class="arg"><xsl:apply-templates/></span>
+</xsl:template>
+
+<xsl:template match="opt">
+  <span class="opt"><xsl:apply-templates/></span>
+</xsl:template>
+
+<xsl:template match="file">
+  <span class="file"><xsl:apply-templates/></span>
+</xsl:template>
+
+<xsl:template match="optdesc">
+  <div class="optdesc">
+    <xsl:apply-templates/>
+  </div>
+</xsl:template>
+
+<xsl:template match="synopsis">
+  <h1>Synopsis</h1>
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="seealso">
+  <h1>Synopsis</h1>
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="description">
+  <h1>Description</h1>
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="options">
+  <h1>Options</h1>
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="section">
+  <h1><xsl:value-of select="@name"/></h1>
+  <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="option">
+  <div class="option"><xsl:apply-templates/></div>
+</xsl:template>
+
+<xsl:template match="manref">
+  <xsl:choose>
+    <xsl:when test="string-length(@href) &gt; 0">
+    <a class="manref"><xsl:attribute name="href"><xsl:value-of select="@href"/></xsl:attribute><xsl:value-of select="@name"/>(<xsl:value-of select="@section"/>)</a>
+    </xsl:when>
+    <xsl:otherwise>
+    <span class="manref"><xsl:value-of select="@name"/>(<xsl:value-of select="@section"/>)</span>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+<xsl:template match="url">
+  <a class="url"><xsl:attribute name="href"><xsl:value-of select="@href"/></xsl:attribute><xsl:value-of select="@href"/></a>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/polyp/Makefile.am b/polyp/Makefile.am
deleted file mode 100644 (file)
index 18e1a2a..0000000
+++ /dev/null
@@ -1,850 +0,0 @@
-# $Id$
-#
-# This file is part of polypaudio.
-#
-# polypaudio is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# polypaudio is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA.
-
-polypincludedir=$(includedir)/polyp
-polypconfdir=$(sysconfdir)/polypaudio
-
-modlibdir=$(libdir)/polypaudio-@PA_MAJORMINOR@
-
-AM_CFLAGS=-D_GNU_SOURCE  -I$(top_srcdir) $(PTHREAD_CFLAGS)
-AM_CFLAGS+=-DDLSEARCHPATH=\"$(modlibdir)\"
-AM_CFLAGS+=-DDEFAULT_CONFIG_DIR=\"$(polypconfdir)\"
-AM_CFLAGS+=-DPOLYPAUDIO_BINARY=\"$(bindir)/polypaudio\"
-
-# This cool debug trap works on i386/gcc only
-AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")'
-
-AM_LIBADD=$(PTHREAD_LIBS) -lm
-AM_LDADD=$(PTHREAD_LIBS) -lm
-
-EXTRA_DIST = default.pa.in daemon.conf.in client.conf.in depmod.py esdcompat.sh.in module-defs.h.m4
-bin_PROGRAMS = \
-               polypaudio \
-               pacat \
-               pactl \
-               paplay \
-               pacmd
-
-bin_SCRIPTS = esdcompat.sh
-
-noinst_PROGRAMS = \
-               mainloop-test \
-               pacat-simple \
-               parec-simple \
-               cpulimit-test \
-               cpulimit-test2 \
-               voltest \
-               strlist-test \
-               mcalign-test
-
-polypconf_DATA=default.pa daemon.conf client.conf
-
-BUILT_SOURCES=polyplib-version.h
-
-polypinclude_HEADERS= \
-               polyplib.h \
-               polyplib-def.h \
-               polyplib-simple.h \
-               polyplib-error.h \
-               polyplib-stream.h \
-               polyplib-context.h \
-               polyplib-introspect.h \
-               polyplib-subscribe.h \
-               polyplib-operation.h \
-               polyplib-scache.h \
-               polyplib-version.h \
-               cdecl.h \
-               mainloop-api.h \
-               mainloop.h \
-               mainloop-signal.h \
-               sample.h \
-               glib-mainloop.h \
-               typeid.h
-
-### Warning! Due to an obscure bug in libtool/automake it is required
-### that the libraries in modlib_LTLIBRARIES are specified in-order,
-### i.e. libraries near the end of the list depend on libraries near
-### the head, and not the other way!
-
-modlib_LTLIBRARIES= \
-               libsocket-util.la \
-               libiochannel.la \
-               libsocket-server.la \
-               libsocket-client.la \
-               libparseaddr.la \
-               libpacket.la \
-               libpstream.la \
-               liboss-util.la \
-               libioline.la \
-               libcli.la \
-               libprotocol-cli.la \
-               libtagstruct.la \
-               libpstream-util.la \
-               libpdispatch.la \
-               libauthkey.la \
-               libauthkey-prop.la \
-               libstrlist.la \
-               libprotocol-simple.la \
-               libprotocol-esound.la \
-               libprotocol-native.la \
-               libprotocol-http.la \
-               module-cli.la \
-               module-cli-protocol-tcp.la \
-               module-cli-protocol-tcp6.la \
-               module-cli-protocol-unix.la \
-               module-pipe-sink.la \
-               module-pipe-source.la \
-               module-oss.la \
-               module-oss-mmap.la \
-               module-simple-protocol-tcp.la \
-               module-simple-protocol-tcp6.la \
-               module-simple-protocol-unix.la \
-               module-esound-protocol-tcp.la \
-               module-esound-protocol-tcp6.la \
-               module-esound-protocol-unix.la \
-               module-native-protocol-tcp.la \
-               module-native-protocol-tcp6.la \
-               module-native-protocol-unix.la \
-               module-native-protocol-fd.la \
-               module-sine.la \
-               module-combine.la \
-               module-esound-compat-spawnfd.la \
-               module-esound-compat-spawnpid.la \
-               module-match.la \
-               module-tunnel-sink.la \
-               module-tunnel-source.la \
-               module-null-sink.la \
-               module-esound-sink.la \
-               module-http-protocol-tcp.la \
-               module-http-protocol-tcp6.la \
-               module-http-protocol-unix.la
-
-SYMDEF_FILES= \
-               module-cli-symdef.h \
-               module-cli-protocol-tcp-symdef.h \
-               module-cli-protocol-tcp6-symdef.h \
-               module-cli-protocol-unix-symdef.h \
-               module-pipe-sink-symdef.h \
-               module-pipe-source-symdef.h \
-               module-oss-symdef.h \
-               module-oss-mmap-symdef.h \
-               module-simple-protocol-tcp-symdef.h \
-               module-simple-protocol-tcp6-symdef.h \
-               module-simple-protocol-unix-symdef.h \
-               module-esound-protocol-tcp-symdef.h \
-               module-esound-protocol-tcp6-symdef.h \
-               module-esound-protocol-unix-symdef.h \
-               module-native-protocol-tcp-symdef.h \
-               module-native-protocol-tcp6-symdef.h \
-               module-native-protocol-unix-symdef.h \
-               module-native-protocol-fd-symdef.h \
-               module-sine-symdef.h \
-               module-combine-symdef.h \
-               module-esound-compat-spawnfd-symdef.h \
-               module-esound-compat-spawnpid-symdef.h \
-               module-match-symdef.h \
-               module-tunnel-sink-symdef.h \
-               module-tunnel-source-symdef.h \
-               module-null-sink-symdef.h \
-               module-esound-sink-symdef.h \
-               module-zeroconf-publish-symdef.h \
-               module-lirc-symdef.h \
-               module-mmkbd-evdev-symdef.h \
-               module-http-protocol-tcp-symdef.h \
-               module-http-protocol-tcp6-symdef.h \
-               module-http-protocol-unix-symdef.h
-
-EXTRA_DIST+=$(SYMDEF_FILES)
-BUILT_SOURCES+=$(SYMDEF_FILES)
-
-lib_LTLIBRARIES= \
-               libpolyp-@PA_MAJORMINOR@.la \
-               libpolyp-error-@PA_MAJORMINOR@.la \
-               libpolyp-mainloop-@PA_MAJORMINOR@.la \
-               libpolyp-simple-@PA_MAJORMINOR@.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 \
-               cli-text.c cli-text.h \
-               tokenizer.c tokenizer.h \
-               dynarray.c dynarray.h \
-               scache.c scache.h \
-               sound-file.c sound-file.h \
-               play-memchunk.c play-memchunk.h \
-               autoload.c autoload.h \
-               xmalloc.c xmalloc.h \
-               subscribe.h subscribe.c \
-               sound-file-stream.c sound-file-stream.h \
-               cpulimit.c cpulimit.h \
-               log.c log.h \
-               gcc-printf.h \
-               modinfo.c modinfo.h \
-               daemon-conf.c daemon-conf.h \
-               dumpmodules.c dumpmodules.h \
-               conf-parser.h conf-parser.c \
-               caps.h caps.c \
-               props.h props.c \
-               mcalign.c mcalign.h \
-               g711.c g711.h \
-               pid.c pid.h \
-               random.c random.h \
-               typeid.c typeid.h
-
-polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS)
-polypaudio_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL)
-polypaudio_LDADD = $(AM_LDADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS)
-polypaudio_LDFLAGS= $(AM_LDFLAGS) -export-dynamic -dlopen force 
-#q-static $(foreach f,$(modlib_LTLIBRARIES),-dlpreopen $(f))
-
-libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h
-libprotocol_simple_la_LDFLAGS = -avoid-version
-libprotocol_simple_la_LIBADD = $(AM_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 = $(AM_LIBADD) libiochannel.la libsocket-util.la $(LIBWRAP_LIBS)
-
-libsocket_client_la_SOURCES = socket-client.c socket-client.h
-libsocket_client_la_LDFLAGS = -avoid-version
-libsocket_client_la_LIBADD = $(AM_LIBADD) libiochannel.la libsocket-util.la libparseaddr.la $(LIBASYNCNS_LIBS)
-libsocket_client_la_CFLAGS = $(AM_CFLAGS) $(LIBASYNCNS_CFLAGS)
-
-libparseaddr_la_SOURCES = parseaddr.c parseaddr.h
-libparseaddr_la_LDFLAGS = -avoid-version
-
-libpstream_la_SOURCES = pstream.c pstream.h
-libpstream_la_LDFLAGS = -avoid-version
-libpstream_la_LIBADD = $(AM_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 = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la
-
-libpdispatch_la_SOURCES = pdispatch.c pdispatch.h
-libpdispatch_la_LDFLAGS = -avoid-version
-libpdispatch_la_LIBADD = $(AM_LIBADD) libtagstruct.la
-
-libiochannel_la_SOURCES = iochannel.c iochannel.h
-libiochannel_la_LDFLAGS = -avoid-version
-libiochannel_la_LIBADD = $(AM_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
-
-libioline_la_SOURCES = ioline.c ioline.h
-libioline_la_LDFLAGS = -avoid-version
-libioline_la_LIBADD = $(AM_LIBADD) libiochannel.la
-
-libcli_la_SOURCES = cli.c cli.h
-libcli_la_LDFLAGS = -avoid-version
-libcli_la_LIBADD = $(AM_LIBADD) libiochannel.la libioline.la
-
-libstrlist_la_SOURCES = strlist.c strlist.h
-libstrlist_la_LDFLAGS = -avoid-version
-libstrlist_la_LIBADD = $(AM_LIBADD)
-
-libprotocol_cli_la_SOURCES = protocol-cli.c protocol-cli.h
-libprotocol_cli_la_LDFLAGS = -avoid-version
-libprotocol_cli_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libcli.la
-
-libprotocol_http_la_SOURCES = protocol-http.c protocol-http.h
-libprotocol_http_la_LDFLAGS = -avoid-version
-libprotocol_http_la_LIBADD = $(AM_LIBADD) libsocket-server.la libioline.la
-
-libprotocol_native_la_SOURCES = protocol-native.c protocol-native.h native-common.h
-libprotocol_native_la_LDFLAGS = -avoid-version
-libprotocol_native_la_LIBADD = $(AM_LIBADD) libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libstrlist.la
-
-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 = $(AM_LIBADD) libsocket-server.la libiochannel.la libauthkey.la
-
-libauthkey_la_SOURCES = authkey.c authkey.h
-libauthkey_la_LDFLAGS = -avoid-version
-
-libauthkey_prop_la_SOURCES = authkey-prop.c authkey-prop.h
-libauthkey_prop_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 = $(AM_LIBADD) libprotocol-simple.la libsocket-server.la
-
-module_simple_protocol_tcp6_la_SOURCES = module-protocol-stub.c
-module_simple_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
-module_simple_protocol_tcp6_la_LDFLAGS = -module -avoid-version
-module_simple_protocol_tcp6_la_LIBADD = $(AM_LIBADD) 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 = $(AM_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 = $(AM_LIBADD) libprotocol-cli.la libsocket-server.la
-
-module_cli_protocol_tcp6_la_SOURCES = module-protocol-stub.c
-module_cli_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
-module_cli_protocol_tcp6_la_LDFLAGS = -module -avoid-version
-module_cli_protocol_tcp6_la_LIBADD = $(AM_LIBADD) 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 = $(AM_LIBADD) libprotocol-cli.la libsocket-server.la libsocket-util.la
-
-module_http_protocol_tcp_la_SOURCES = module-protocol-stub.c
-module_http_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
-module_http_protocol_tcp_la_LDFLAGS = -module -avoid-version
-module_http_protocol_tcp_la_LIBADD = $(AM_LIBADD) libprotocol-http.la libsocket-server.la
-
-module_http_protocol_tcp6_la_SOURCES = module-protocol-stub.c
-module_http_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
-module_http_protocol_tcp6_la_LDFLAGS = -module -avoid-version
-module_http_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libprotocol-http.la libsocket-server.la
-
-module_http_protocol_unix_la_SOURCES = module-protocol-stub.c
-module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
-module_http_protocol_unix_la_LDFLAGS = -module -avoid-version
-module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libprotocol-http.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 = $(AM_LIBADD) libprotocol-native.la libsocket-server.la
-
-module_native_protocol_tcp6_la_SOURCES = module-protocol-stub.c
-module_native_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
-module_native_protocol_tcp6_la_LDFLAGS = -module -avoid-version
-module_native_protocol_tcp6_la_LIBADD = $(AM_LIBADD) 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 = $(AM_LIBADD) libprotocol-native.la libsocket-server.la libsocket-util.la
-
-module_native_protocol_fd_la_SOURCES = module-native-protocol-fd.c
-module_native_protocol_fd_la_CFLAGS = $(AM_CFLAGS)
-module_native_protocol_fd_la_LDFLAGS = -module -avoid-version
-module_native_protocol_fd_la_LIBADD = $(AM_LIBADD) 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 = $(AM_LIBADD) libprotocol-esound.la libsocket-server.la
-
-module_esound_protocol_tcp6_la_SOURCES = module-protocol-stub.c
-module_esound_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
-module_esound_protocol_tcp6_la_LDFLAGS = -module -avoid-version
-module_esound_protocol_tcp6_la_LIBADD = $(AM_LIBADD) 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 = $(AM_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 = $(AM_LIBADD) libiochannel.la
-
-module_pipe_source_la_SOURCES = module-pipe-source.c
-module_pipe_source_la_LDFLAGS = -module -avoid-version
-module_pipe_source_la_LIBADD = $(AM_LIBADD) libiochannel.la
-
-module_oss_la_SOURCES = module-oss.c
-module_oss_la_LDFLAGS = -module -avoid-version
-module_oss_la_LIBADD = $(AM_LIBADD) libiochannel.la liboss-util.la
-
-module_oss_mmap_la_SOURCES = module-oss-mmap.c
-module_oss_mmap_la_LDFLAGS = -module -avoid-version
-module_oss_mmap_la_LIBADD = $(AM_LIBADD) liboss-util.la
-
-module_cli_la_SOURCES = module-cli.c
-module_cli_la_LDFLAGS = -module -avoid-version
-module_cli_la_LIBADD = $(AM_LIBADD) libcli.la libiochannel.la
-
-module_sine_la_SOURCES = module-sine.c
-module_sine_la_LDFLAGS = -module -avoid-version
-module_sine_la_LIBADD = $(AM_LIBADD)
-
-module_combine_la_SOURCES = module-combine.c
-module_combine_la_LDFLAGS = -module -avoid-version
-module_combine_la_LIBADD = $(AM_LIBADD)
-
-module_null_sink_la_SOURCES = module-null-sink.c
-module_null_sink_la_LDFLAGS = -module -avoid-version
-module_null_sink_la_LIBADD = $(AM_LIBADD)
-
-module_match_la_SOURCES = module-match.c
-module_match_la_LDFLAGS = -module -avoid-version
-module_match_la_LIBADD = $(AM_LIBADD)
-
-module_tunnel_sink_la_SOURCES = module-tunnel.c
-module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS)
-module_tunnel_sink_la_LDFLAGS = -module -avoid-version
-module_tunnel_sink_la_LIBADD = $(AM_LIBADD) libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la
-
-module_tunnel_source_la_SOURCES = module-tunnel.c
-module_tunnel_source_la_LDFLAGS = -module -avoid-version
-module_tunnel_source_la_LIBADD = $(AM_LIBADD) libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la
-
-module_esound_compat_spawnfd_la_SOURCES = module-esound-compat-spawnfd.c
-module_esound_compat_spawnfd_la_LDFLAGS = -module -avoid-version
-module_esound_compat_spawnfd_la_LIBADD = $(AM_LIBADD)
-
-module_esound_compat_spawnpid_la_SOURCES = module-esound-compat-spawnpid.c
-module_esound_compat_spawnpid_la_LDFLAGS = -module -avoid-version
-module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD)
-
-module_esound_sink_la_SOURCES = module-esound-sink.c
-module_esound_sink_la_LDFLAGS = -module -avoid-version
-module_esound_sink_la_LIBADD = $(AM_LIBADD) libsocket-client.la libauthkey.la
-
-libpolyp_@PA_MAJORMINOR@_la_SOURCES = 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 \
-               parseaddr.c parseaddr.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 \
-               sample.c sample.h \
-               xmalloc.c xmalloc.h \
-               polyplib-operation.c polyplib-operation.h \
-               polyplib-context.c polyplib-context.h \
-               polyplib-stream.c polyplib-stream.h \
-               polyplib-introspect.c polyplib-introspect.h \
-               polyplib-scache.c polyplib-scache.h \
-               polyplib-subscribe.c polyplib-subscribe.h \
-               polyplib-internal.h \
-               cdecl.h \
-               llist.h \
-               log.c log.h \
-               gcc-printf.h \
-               client-conf.c client-conf.h \
-               conf-parser.c conf-parser.h \
-               strlist.c strlist.h \
-               strbuf.c strbuf.h \
-               mcalign.c mcalign.h \
-               typeid.c typeid.h \
-               random.c random.h
-
-libpolyp_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS)
-libpolyp_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
-libpolyp_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD)
-
-libpolyp_mainloop_@PA_MAJORMINOR@_la_SOURCES = mainloop-api.h mainloop-api.c \
-               mainloop.c mainloop.h \
-               mainloop-signal.c mainloop-signal.h
-libpolyp_mainloop_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS)
-libpolyp_mainloop_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la
-libpolyp_mainloop_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
-
-libpolyp_error_@PA_MAJORMINOR@_la_SOURCES = polyplib-error.c polyplib-error.h
-libpolyp_error_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS)
-libpolyp_error_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la
-libpolyp_error_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
-
-libpolyp_simple_@PA_MAJORMINOR@_la_SOURCES = polyplib-simple.c polyplib-simple.h 
-libpolyp_simple_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS)
-libpolyp_simple_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la
-libpolyp_simple_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
-
-pacat_SOURCES = pacat.c
-pacat_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la
-pacat_CFLAGS = $(AM_CFLAGS) 
-
-paplay_SOURCES = paplay.c
-paplay_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la $(LIBSNDFILE_LIBS)
-paplay_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
-
-pactl_SOURCES = pactl.c
-pactl_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la $(LIBSNDFILE_LIBS)
-pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
-
-pacat_simple_SOURCES = pacat-simple.c
-pacat_simple_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-simple-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la
-pacat_simple_CFLAGS = $(AM_CFLAGS)
-
-parec_simple_SOURCES = parec-simple.c
-parec_simple_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-simple-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la
-parec_simple_CFLAGS = $(AM_CFLAGS)
-
-mainloop_test_SOURCES = mainloop-test.c
-mainloop_test_CFLAGS = $(AM_CFLAGS)
-mainloop_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la libpolyp-@PA_MAJORMINOR@.la
-
-voltest_SOURCES = voltest.c volume.c
-voltest_CFLAGS = $(AM_CFLAGS)
-voltest_LDADD = $(AM_LDADD)
-
-strlist_test_SOURCES = strlist-test.c strlist.c strlist.h strbuf.c strbuf.h util.c util.h xmalloc.c xmalloc.h log.c log.h
-strlist_test_CFLAGS = $(AM_CFLAGS)
-strlist_test_LDADD = $(AM_LDADD)
-
-mcalign_test_SOURCES = mcalign-test.c util.c util.h xmalloc.c xmalloc.h log.c log.h mcalign.c mcalign.h memchunk.c memchunk.h memblock.c memblock.h
-mcalign_test_CFLAGS = $(AM_CFLAGS)
-mcalign_test_LDADD = $(AM_LDADD)
-
-pacmd_SOURCES = pacmd.c util.c util.h xmalloc.c xmalloc.h log.c log.h pid.c pid.h
-pacmd_CFLAGS = $(AM_CFLAGS)
-pacmd_LDADD = $(AM_LDADD)
-
-cpulimit_test_SOURCES = cpulimit-test.c cpulimit.c util.c log.c cpulimit.h util.h log.h
-cpulimit_test_CFLAGS = $(AM_CFLAGS)
-cpulimit_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la
-
-cpulimit_test2_SOURCES = cpulimit-test.c cpulimit.c util.c log.c cpulimit.h util.h log.h
-cpulimit_test2_CFLAGS = $(AM_CFLAGS) -DTEST2
-cpulimit_test2_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la
-
-### X11 stuff
-
-if HAVE_X11
-modlib_LTLIBRARIES+= \
-               libx11wrap.la \
-               libx11prop.la \
-               module-x11-bell.la \
-               module-x11-publish.la
-SYMDEF_FILES += \
-               module-x11-bell-symdef.h \
-               module-x11-publish-symdef.h
-
-libx11wrap_la_SOURCES = x11wrap.c x11wrap.h
-libx11wrap_la_LDFLAGS = -avoid-version
-libx11wrap_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-libx11wrap_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
-
-libx11prop_la_SOURCES = x11prop.c x11prop.h
-libx11prop_la_LDFLAGS = -avoid-version
-libx11prop_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-libx11prop_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
-
-module_x11_bell_la_SOURCES = module-x11-bell.c
-module_x11_bell_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-module_x11_bell_la_LDFLAGS = -module -avoid-version
-module_x11_bell_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la
-
-module_x11_publish_la_SOURCES = module-x11-publish.c
-module_x11_publish_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-module_x11_publish_la_LDFLAGS = -module -avoid-version
-module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libauthkey.la libauthkey-prop.la libx11prop.la libstrlist.la
-
-bin_PROGRAMS+= \
-               pax11publish
-
-pax11publish_SOURCES = pax11publish.c util.c util.h xmalloc.c xmalloc.h log.c log.h authkey.c authkey.h client-conf.c client-conf.h conf-parser.c conf-parser.h x11prop.c x11prop.h random.c random.h
-pax11publish_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-pax11publish_LDADD = $(AM_LDADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
-
-libpolyp_@PA_MAJORMINOR@_la_CFLAGS += $(X_CFLAGS)
-libpolyp_@PA_MAJORMINOR@_la_LDFLAGS += $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
-libpolyp_@PA_MAJORMINOR@_la_SOURCES += x11prop.c x11prop.h client-conf-x11.c client-conf-x11.h
-
-endif
-
-### libasyncns stuff
-
-if HAVE_LIBASYNCNS
-libpolyp_@PA_MAJORMINOR@_la_CFLAGS += $(LIBASYNCNS_CFLAGS)
-libpolyp_@PA_MAJORMINOR@_la_LIBADD += $(LIBASYNCNS_LIBS)
-endif
-
-### ALSA modules
-
-if HAVE_ALSA
-modlib_LTLIBRARIES+= \
-               libalsa-util.la \
-               module-alsa-sink.la \
-               module-alsa-source.la
-SYMDEF_FILES += \
-               module-alsa-sink-symdef.h \
-               module-alsa-source-symdef.h
-
-libalsa_util_la_SOURCES = alsa-util.c alsa-util.h
-libalsa_util_la_LDFLAGS = -avoid-version
-libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS)
-libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
-
-module_alsa_sink_la_SOURCES = module-alsa-sink.c
-module_alsa_sink_la_LDFLAGS = -module -avoid-version
-module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
-module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
-
-module_alsa_source_la_SOURCES = module-alsa-source.c
-module_alsa_source_la_LDFLAGS = -module -avoid-version
-module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
-module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
-endif
-
-### HOWL modules
-if HAVE_HOWL
-modlib_LTLIBRARIES+= \
-               libhowl-wrap.la \
-               module-zeroconf-publish.la
-
-libhowl_wrap_la_SOURCES = howl-wrap.c howl-wrap.h
-libhowl_wrap_la_LDFLAGS = -avoid-version
-libhowl_wrap_la_LIBADD = $(AM_LIBADD) $(HOWL_LIBS)
-libhowl_wrap_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS)
-
-module_zeroconf_publish_la_SOURCES = module-zeroconf-publish.c
-module_zeroconf_publish_la_LDFLAGS = -module -avoid-version
-module_zeroconf_publish_la_LIBADD = $(AM_LIBADD) $(HOWL_LIBS) libhowl-wrap.la
-module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS)
-
-lib_LTLIBRARIES+= \
-               libpolyp-browse-@PA_MAJORMINOR@.la
-
-libpolyp_browse_@PA_MAJORMINOR@_la_SOURCES = polyplib-browser.c polyplib-browser.h 
-libpolyp_browse_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS)
-libpolyp_browse_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la $(HOWL_LIBS)
-libpolyp_browse_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 
-
-bin_PROGRAMS += \
-               pabrowse
-
-pabrowse_SOURCES = pabrowse.c
-pabrowse_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la libpolyp-browse-@PA_MAJORMINOR@.la
-pabrowse_CFLAGS = $(AM_CFLAGS)
-
-polypinclude_HEADERS+=polyplib-browser.h
-
-endif
-
-### GLIB 2.0 support
-
-if HAVE_GLIB20
-lib_LTLIBRARIES+= \
-               libpolyp-mainloop-glib-@PA_MAJORMINOR@.la
-
-noinst_PROGRAMS+= \
-               mainloop-test-glib
-
-libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_SOURCES = glib-mainloop.h glib-mainloop.c
-libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS)
-libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-mainloop-@PA_MAJORMINOR@.la $(GLIB20_LIBS)
-libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
-
-mainloop_test_glib_SOURCES = $(mainloop_test_SOURCES)
-mainloop_test_glib_CFLAGS = $(mainloop_test_CFLAGS) $(GLIB20_CFLAGS) -DGLIB_MAIN_LOOP
-mainloop_test_glib_LDADD = $(mainloop_test_LDADD) $(GLIB20_LIBS) libpolyp-mainloop-glib-@PA_MAJORMINOR@.la
-endif
-
-### GLIB 1.2 support
-
-if HAVE_GLIB12
-
-lib_LTLIBRARIES+= \
-               libpolyp-mainloop-glib12-@PA_MAJORMINOR@.la
-
-noinst_PROGRAMS+= \
-               mainloop-test-glib12
-
-libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_SOURCES = glib-mainloop.h glib12-mainloop.c
-libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(GLIB12_CFLAGS)
-libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-mainloop-@PA_MAJORMINOR@.la $(GLIB12_LIBS)
-libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
-
-mainloop_test_glib12_SOURCES = $(mainloop_test_SOURCES)
-mainloop_test_glib12_CFLAGS = $(mainloop_test_CFLAGS) $(GLIB12_CFLAGS) -DGLIB_MAIN_LOOP
-mainloop_test_glib12_LDADD = $(mainloop_test_LDADD) $(GLIB12_LIBS) libpolyp-mainloop-glib12-@PA_MAJORMINOR@.la
-
-endif
-
-### LIRC support
-
-if HAVE_LIRC
-
-modlib_LTLIBRARIES+= \
-               module-lirc.la
-
-module_lirc_la_SOURCES = module-lirc.c
-module_lirc_la_LDFLAGS = -module -avoid-version
-module_lirc_la_LIBADD = $(AM_LIBADD) $(LIRC_LIBS) 
-module_lirc_la_CFLAGS = $(AM_CFLAGS) $(LIRC_CFLAGS)
-
-endif
-
-
-### Linux evdev
-
-if HAVE_EVDEV
-
-modlib_LTLIBRARIES+= \
-               module-mmkbd-evdev.la
-
-module_mmkbd_evdev_la_SOURCES = module-mmkbd-evdev.c
-module_mmkbd_evdev_la_LDFLAGS = -module -avoid-version
-module_mmkbd_evdev_la_LIBADD = $(AM_LIBADD)
-module_mmkbd_evdev_la_CFLAGS = $(AM_CFLAGS)
-
-endif
-
-### libpolypcore (needs to be updated)
-
-if BUILD_LIBPOLYPCORE
-
-polypinclude_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 \
-               cli-text.c cli-text.h \
-               tokenizer.c tokenizer.h \
-               dynarray.c dynarray.h
-
-endif
-
-### Some minor stuff
-
-suid: polypaudio
-       chown root $<
-       chmod u+s $<
-
-esdcompat.sh: esdcompat.sh.in Makefile
-       sed -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \
-               -e 's,@PACKAGE_NAME\@,$(PACKAGE_NAME),g' \
-               -e 's,@POLYPAUDIO_BINARY\@,$(bindir)/polypaudio,g' < $< > $@
-
-client.conf: client.conf.in Makefile
-       sed -e 's,@POLYPAUDIO_BINARY\@,$(bindir)/polypaudio,g' < $< > $@
-
-default.pa: default.pa.in Makefile
-       sed -e 's,@POLYPAUDIO_BINARY\@,$(bindir)/polypaudio,g' < $< > $@
-
-daemon.conf: daemon.conf.in Makefile
-       sed -e 's,@DLSEARCHPATH\@,$(modlibdir),g' \
-               -e 's,@DEFAULT_CONFIG_FILE\@,$(polypconfdir)/daemon.conf,g' < $< > $@
-
-install-exec-hook:
-       chown root $(DESTDIR)$(bindir)/polypaudio ; true
-       chmod u+s $(DESTDIR)$(bindir)/polypaudio
-       ln -sf pacat $(DESTDIR)$(bindir)/parec
-
-$(SYMDEF_FILES): module-defs.h.m4
-       $(M4) -Dfname="$@" $< > $@
diff --git a/polyp/alsa-util.c b/polyp/alsa-util.c
deleted file mode 100644 (file)
index 2894c9e..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <sys/types.h>
-#include <asoundlib.h>
-
-#include "alsa-util.h"
-#include "sample.h"
-#include "xmalloc.h"
-#include "log.h"
-
-/* Set the hardware parameters of the given ALSA device. Returns the
- * selected fragment settings in *period and *period_size */
-int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size) {
-    int ret = -1;
-    snd_pcm_uframes_t buffer_size;
-    snd_pcm_hw_params_t *hwparams = NULL;
-    static const snd_pcm_format_t format_trans[] = {
-        [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8,
-        [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
-        [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
-        [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
-        [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
-        [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
-        [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
-    };
-    assert(pcm_handle && ss && periods && period_size);
-    
-    if (snd_pcm_hw_params_malloc(&hwparams) < 0 ||
-        snd_pcm_hw_params_any(pcm_handle, hwparams) < 0 ||
-        snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 ||
-        snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[ss->format]) < 0 ||
-        snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &ss->rate, NULL) < 0 ||
-        snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss->channels) < 0 ||
-        (*periods > 0 && snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0) || 
-        (*period_size > 0 && snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL) < 0) || 
-        snd_pcm_hw_params(pcm_handle, hwparams) < 0)
-        goto finish;
-    
-    if (snd_pcm_prepare(pcm_handle) < 0)
-        goto finish;
-
-    if (snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size) < 0 ||
-        snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL) < 0)
-        goto finish;
-    
-    assert(buffer_size > 0);
-    assert(*period_size > 0);
-    *periods = buffer_size / *period_size;
-    assert(*periods > 0);
-    
-    ret = 0;
-    
-finish:
-    if (hwparams)
-        snd_pcm_hw_params_free(hwparams);
-    
-    return ret;
-}
-
-/* Allocate an IO event for every ALSA poll descriptor for the
- * specified ALSA device. Return a pointer to such an array in
- * *io_events. Store the length of that array in *n_io_events. Use the
- * specified callback function and userdata. The array has to be freed
- * with pa_free_io_events(). */
-int pa_create_io_events(snd_pcm_t *pcm_handle, struct pa_mainloop_api* m, struct pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(struct pa_mainloop_api*a, struct pa_io_event *e, int fd, enum pa_io_event_flags events, void *userdata), void *userdata) {
-    unsigned i;
-    struct pollfd *pfds, *ppfd;
-    struct pa_io_event **ios;
-    assert(pcm_handle && m && io_events && n_io_events && cb);
-
-    *n_io_events = snd_pcm_poll_descriptors_count(pcm_handle);
-
-    pfds = pa_xmalloc(sizeof(struct pollfd) * *n_io_events);
-    if (snd_pcm_poll_descriptors(pcm_handle, pfds, *n_io_events) < 0) {
-        pa_xfree(pfds);
-        return -1;
-    }
-    
-    *io_events = pa_xmalloc(sizeof(void*) * *n_io_events);
-
-    for (i = 0, ios = *io_events, ppfd = pfds; i < *n_io_events; i++, ios++, ppfd++) {
-        *ios = m->io_new(m, ppfd->fd,
-                            ((ppfd->events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
-                            ((ppfd->events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0), cb, userdata);
-        assert(*ios);
-    }
-
-    pa_xfree(pfds);
-    return 0;
-}
-
-/* Free the memory allocated by pa_create_io_events() */
-void pa_free_io_events(struct pa_mainloop_api* m, struct pa_io_event **io_events, unsigned n_io_events) {
-    unsigned i;
-    struct pa_io_event **ios;
-    assert(m && io_events);
-    
-    for (ios = io_events, i = 0; i < n_io_events; i++, ios++)
-        m->io_free(*ios);
-    pa_xfree(io_events);
-}
diff --git a/polyp/alsa-util.h b/polyp/alsa-util.h
deleted file mode 100644 (file)
index adec143..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef fooalsautilhfoo
-#define fooalsautilhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <asoundlib.h>
-
-#include "sample.h"
-#include "mainloop-api.h"
-
-int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size);
-
-int pa_create_io_events(snd_pcm_t *pcm_handle, struct pa_mainloop_api *m, struct pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(struct pa_mainloop_api*a, struct pa_io_event *e, int fd, enum pa_io_event_flags events, void *userdata), void *userdata);
-void pa_free_io_events(struct pa_mainloop_api* m, struct pa_io_event **io_sources, unsigned n_io_sources);
-
-#endif
diff --git a/polyp/authkey-prop.c b/polyp/authkey-prop.c
deleted file mode 100644 (file)
index 2adfc41..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <assert.h>
-#include <string.h>
-
-#include "xmalloc.h"
-#include "authkey-prop.h"
-#include "props.h"
-#include "log.h"
-
-struct authkey_data {
-    int ref;
-    size_t length;
-};
-
-int pa_authkey_prop_get(struct pa_core *c, const char *name, void *data, size_t len) {
-    struct authkey_data *a;
-    assert(c && name && data && len > 0);
-    
-    if (!(a = pa_property_get(c, name)))
-        return -1;
-
-    assert(a->length == len);
-    memcpy(data, a+1, len);
-    return 0;
-}
-
-int pa_authkey_prop_put(struct pa_core *c, const char *name, const void *data, size_t len) {
-    struct authkey_data *a;
-    assert(c && name);
-
-    if (pa_property_get(c, name))
-        return -1;
-
-    a = pa_xmalloc(sizeof(struct authkey_data) + len);
-    a->ref = 1;
-    a->length = len;
-    memcpy(a+1, data, len);
-
-    pa_property_set(c, name, a);
-    
-    return 0;
-}
-
-void pa_authkey_prop_ref(struct pa_core *c, const char *name) {
-    struct authkey_data *a;
-    assert(c && name);
-
-    a = pa_property_get(c, name);
-    assert(a && a->ref >= 1);
-
-    a->ref++;
-}
-
-void pa_authkey_prop_unref(struct pa_core *c, const char *name) {
-    struct authkey_data *a;
-    assert(c && name);
-
-    a = pa_property_get(c, name);
-    assert(a && a->ref >= 1);
-
-    if (!(--a->ref)) {
-        pa_property_remove(c, name);
-        pa_xfree(a);
-    }
-}
-
-
diff --git a/polyp/caps.c b/polyp/caps.c
deleted file mode 100644 (file)
index 739e707..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-
-#ifdef HAVE_SYS_CAPABILITY_H
-#include <sys/capability.h>
-#endif
-
-#include "log.h"
-#include "caps.h"
-
-/* Drop root rights when called SUID root */
-void pa_drop_root(void) {
-    uid_t uid = getuid();
-    
-    if (uid == 0 || geteuid() != 0)
-        return;
-
-    pa_log_info(__FILE__": dropping root rights.\n");
-
-#if defined(HAVE_SETRESUID)
-    setresuid(uid, uid, uid);
-#elif defined(HAVE_SETREUID)
-    setreuid(uid, uid);
-#else
-    setuid(uid);
-    seteuid(uid);
-#endif
-}
-
-#ifdef HAVE_SYS_CAPABILITY_H
-
-/* Limit capabilities set to CAPSYS_NICE */
-int pa_limit_caps(void) {
-    int r = -1;
-    cap_t caps;
-    cap_value_t nice_cap = CAP_SYS_NICE;
-
-    caps = cap_init();
-    assert(caps);
-
-    cap_clear(caps);
-
-    cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET);
-    cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET);
-
-    if (cap_set_proc(caps) < 0)
-        goto fail;
-
-    pa_log_info(__FILE__": dropped capabilities successfully.\n"); 
-    
-    r = 0;
-
-fail:
-    cap_free (caps);
-    
-    return r;
-}
-
-/* Drop all capabilities, effectively becoming a normal user */
-int pa_drop_caps(void) {
-    cap_t caps;
-    int r = -1;
-
-    caps = cap_init();
-    assert(caps);
-
-    cap_clear(caps);
-
-    if (cap_set_proc(caps) < 0) {
-        pa_log(__FILE__": failed to drop capabilities: %s\n", strerror(errno));
-        goto fail;
-    }
-    
-    r = 0;
-
-fail:
-    cap_free (caps);
-    
-    return r;
-}
-
-#else
-
-/* NOOPs in case capabilities are not available. */
-int pa_limit_caps(void) {
-    return 0;
-}
-
-int pa_drop_caps(void) {
-    pa_drop_root();
-    return 0;
-}
-
-#endif
-
diff --git a/polyp/channelmap.c b/polyp/channelmap.c
deleted file mode 100644 (file)
index 9787652..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-
-#include "channelmap.h"
-
-
-struct pa_channel_map* pa_channel_map_init(struct pa_channel_map *m) {
-    unsigned c;
-    assert(m);
-
-    for (c = 0; c < PA_CHANNELS_MAX; c++)
-        m->map[c] = PA_CHANNEL_POSITION_INVALID;
-
-    return m;
-}
-
-struct pa_channel_map* pa_channel_map_init_mono(struct pa_channel_map *m) {
-    assert(m);
-
-    pa_channel_map_init(m);
-    m->map[0] = PA_CHANNEL_POSITION_MONO;
-    return m;
-}
-
-struct pa_channel_map* pa_channel_map_init_stereo(struct pa_channel_map *m) {
-    assert(m);
-
-    pa_channel_map_init(m);
-    m->map[0] = PA_CHANNEL_POSITION_LEFT;
-    m->map[1] = PA_CHANNEL_POSITION_RIGHT;
-    return m;
-}
-
-struct pa_channel_map* pa_channel_map_init_auto(struct pa_channel_map *m, int channels) {
-    assert(m);
-    assert(channels > 0);
-
-    pa_channel_map_init(m);
-    
-    switch (channels) {
-        case 1:
-            m->map[0] = PA_CHANNEL_POSITION_MONO;
-            return m;
-
-        case 8:
-            m->mpa[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
-            m->mpa[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
-            /* Fall through */
-            
-        case 6:
-            m->mpa[5] = PA_CHANNEL_POSITION_LFE;
-            /* Fall through */
-            
-        case 5:
-            m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
-            /* Fall through */
-            
-        case 4:
-            m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
-            m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
-            /* Fall through */
-            
-        case 2:
-            m->map[0] = PA_CHANNEL_MAP_FRONT_LEFT;
-            m->map[1] = PA_CHANNEL_MAP_FRONT_RIGHT;
-            return m;
-            
-        default:
-            return NULL;
-    }
-}
-
-
-const char* pa_channel_position_to_string(pa_channel_position_t pos) {
-    const char *const table[] = {
-        [PA_CHANNEL_POSITION_MONO] = "mono",
-
-        [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
-        [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
-        [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
-        
-        [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
-        [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
-        [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
-
-        [PA_CHANNEL_POSITION_LFE] = "lfe",
-
-        [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
-        [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
-        
-        [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
-        [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right"
-    };
-
-    if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
-        return NULL;
-
-    return table[pos];
-}
-
-int pa_channel_map_equal(struct pa_channel_map *a, struct pa_channel_map *b, int channels) {
-    char c;
-    
-    assert(a);
-    assert(b);
-    assert(channels > 0);
-
-    if (channels > PA_CHANNELS_MAX)
-        channels = PA_CHANNELS_MAX;
-
-    for (c = 0; c < channels; c++)
-        if (a->map[c] != b->map[c])
-            return 1;
-
-    return 0;
-}
diff --git a/polyp/channelmap.h b/polyp/channelmap.h
deleted file mode 100644 (file)
index 946247a..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef foochannelmaphfoo
-#define foochannelmaphfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <polyp/sample.h>
-#include <polyp/cdecl.h>
-
-/** \file
- * Constants and routines for channel mapping handling */
-
-PA_C_DECL_BEGIN
-
-typedef enum {
-    PA_CHANNEL_POSITION_INVALID = -1,
-    PA_CHANNEL_POSITION_MONO = 0,
-
-    PA_CHANNEL_POSITION_LEFT,
-    PA_CHANNEL_POSITION_RIGHT,
-
-    PA_CHANNEL_POSITION_FRONT_CENTER,
-    PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT,
-    PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT,
-
-    PA_CHANNEL_POSITION_REAR_CENTER,
-    PA_CHANNEL_POSITION_REAR_LEFT,
-    PA_CHANNEL_POSITION_REAR_RIGHT,
-    
-    PA_CHANNEL_POSITION_LFE,
-    PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_LFE,
-    
-    PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
-    PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
-    
-    PA_CHANNEL_POSITION_SIDE_LEFT,
-    PA_CHANNEL_POSITION_SIDE_RIGHT,
-    
-    PA_CHANNEL_POSITION_MAX
-} pa_channel_position_t;
-
-struct {
-    pa_channel_position_t map[PA_CHANNELS_MAX];
-} pa_channel_map;
-
-struct pa_channel_map* pa_channel_map_init(struct pa_channel_map *m);
-struct pa_channel_map* pa_channel_map_init_mono(struct pa_channel_map *m);
-struct pa_channel_map* pa_channel_map_init_stereo(struct pa_channel_map *m);
-struct pa_channel_map* pa_channel_map_init_auto(struct pa_channel_map *m, int channels);
-
-const char* pa_channel_position_to_string(pa_channel_position_t pos);
-
-int pa_channel_map_equal(struct pa_channel_map *a, struct pa_channel_map *b, int channels)
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/cli-command.c b/polyp/cli-command.c
deleted file mode 100644 (file)
index 0e8e09a..0000000
+++ /dev/null
@@ -1,829 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include "cli-command.h"
-#include "module.h"
-#include "sink.h"
-#include "source.h"
-#include "client.h"
-#include "sink-input.h"
-#include "source-output.h"
-#include "tokenizer.h"
-#include "strbuf.h"
-#include "namereg.h"
-#include "cli-text.h"
-#include "scache.h"
-#include "sample-util.h"
-#include "sound-file.h"
-#include "play-memchunk.h"
-#include "autoload.h"
-#include "xmalloc.h"
-#include "sound-file-stream.h"
-#include "props.h"
-#include "util.h"
-
-struct command {
-    const char *name;
-    int (*proc) (struct pa_core *c, struct pa_tokenizer*t, struct pa_strbuf *buf, int *fail);
-    const char *help;
-    unsigned args;
-};
-
-#define INCLUDE_META ".include"
-#define FAIL_META ".fail"
-#define NOFAIL_META ".nofail"
-
-/* Prototypes for all available commands */
-static int pa_cli_command_exit(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_help(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_modules(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_clients(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_sinks(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_sources(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_inputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_outputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_stat(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_unload(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_input_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_sink_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_kill_sink_input(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_play(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_scache_load_dir(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_add(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_autoload_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_dump(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-static int pa_cli_command_list_props(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail);
-
-
-/* A method table for all available commands */
-
-static const struct command commands[] = {
-    { "exit",                    pa_cli_command_exit,               "Terminate the daemon",         1 },
-    { "help",                    pa_cli_command_help,               "Show this help",               1 },
-    { "list-modules",            pa_cli_command_modules,            "List loaded modules",          1 },
-    { "list-sinks",              pa_cli_command_sinks,              "List loaded sinks",            1 },
-    { "list-sources",            pa_cli_command_sources,            "List loaded sources",          1 },
-    { "list-clients",            pa_cli_command_clients,            "List loaded clients",          1 },
-    { "list-sink-inputs",        pa_cli_command_sink_inputs,        "List sink inputs",             1 },
-    { "list-source-outputs",     pa_cli_command_source_outputs,     "List source outputs",          1 },
-    { "stat",                    pa_cli_command_stat,               "Show memory block statistics", 1 },
-    { "info",                    pa_cli_command_info,               "Show comprehensive status",    1 },
-    { "ls",                      pa_cli_command_info,               NULL,                           1 },
-    { "list",                    pa_cli_command_info,               NULL,                           1 },
-    { "load-module",             pa_cli_command_load,               "Load a module (args: name, arguments)",                     3},
-    { "unload-module",           pa_cli_command_unload,             "Unload a module (args: index)",                             2},
-    { "set-sink-volume",         pa_cli_command_sink_volume,        "Set the volume of a sink (args: index|name, volume)",             3},
-    { "set-sink-input-volume",   pa_cli_command_sink_input_volume,  "Set the volume of a sink input (args: index|name, volume)", 3},
-    { "set-default-sink",        pa_cli_command_sink_default,       "Set the default sink (args: index|name)", 2},
-    { "set-default-source",      pa_cli_command_source_default,     "Set the default source (args: index|name)", 2},
-    { "kill-client",             pa_cli_command_kill_client,        "Kill a client (args: index)", 2},
-    { "kill-sink-input",         pa_cli_command_kill_sink_input,    "Kill a sink input (args: index)", 2},
-    { "kill-source-output",      pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
-    { "list-samples",            pa_cli_command_scache_list,        "List all entries in the sample cache", 1},
-    { "play-sample",             pa_cli_command_scache_play,        "Play a sample from the sample cache (args: name, sink|index)", 3},
-    { "remove-sample",           pa_cli_command_scache_remove,      "Remove a sample from the sample cache (args: name)", 2},
-    { "load-sample",             pa_cli_command_scache_load,        "Load a sound file into the sample cache (args: name, filename)", 3},
-    { "load-sample-lazy",        pa_cli_command_scache_load,        "Lazily load a sound file into the sample cache (args: name, filename)", 3},
-    { "load-sample-dir-lazy",    pa_cli_command_scache_load_dir,    "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
-    { "play-file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3},
-    { "list-autoload",           pa_cli_command_autoload_list,      "List autoload entries", 1},
-    { "add-autoload-sink",       pa_cli_command_autoload_add,       "Add autoload entry for a sink (args: sink, module name, arguments)", 4},
-    { "add-autoload-source",     pa_cli_command_autoload_add,       "Add autoload entry for a source (args: source, module name, arguments)", 4},
-    { "remove-autoload-sink",    pa_cli_command_autoload_remove,    "Remove autoload entry for a sink (args: name)", 2},
-    { "remove-autoload-source",  pa_cli_command_autoload_remove,    "Remove autoload entry for a source (args: name)", 2},
-    { "dump",                    pa_cli_command_dump,               "Dump daemon configuration", 1},
-    { "list-props",              pa_cli_command_list_props,         NULL, 1},
-    { NULL, NULL, NULL, 0 }
-};
-
-static const char whitespace[] = " \t\n\r";
-static const char linebreak[] = "\n\r";
-
-static uint32_t parse_index(const char *n) {
-    uint32_t index;
-
-    if (pa_atou(n, &index) < 0)
-        return (uint32_t) PA_IDXSET_INVALID;
-
-    return index;
-}
-
-static int pa_cli_command_exit(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    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) {
-    const struct command*command;
-    assert(c && t && buf);
-
-    pa_strbuf_puts(buf, "Available commands:\n");
-    
-    for (command = commands; command->name; command++)
-        if (command->help)
-            pa_strbuf_printf(buf, "    %-25s %s\n", command->name, command->help);
-    return 0;
-}
-
-static int pa_cli_command_modules(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    char *s;
-    assert(c && t);
-    s = pa_module_list_to_string(c);
-    assert(s);
-    pa_strbuf_puts(buf, s);
-    pa_xfree(s);
-    return 0;
-}
-
-static int pa_cli_command_clients(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    char *s;
-    assert(c && t);
-    s = pa_client_list_to_string(c);
-    assert(s);
-    pa_strbuf_puts(buf, s);
-    pa_xfree(s);
-    return 0;
-}
-
-static int pa_cli_command_sinks(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    char *s;
-    assert(c && t);
-    s = pa_sink_list_to_string(c);
-    assert(s);
-    pa_strbuf_puts(buf, s);
-    pa_xfree(s);
-    return 0;
-}
-
-static int pa_cli_command_sources(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    char *s;
-    assert(c && t);
-    s = pa_source_list_to_string(c);
-    assert(s);
-    pa_strbuf_puts(buf, s);
-    pa_xfree(s);
-    return 0;
-}
-
-static int pa_cli_command_sink_inputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    char *s;
-    assert(c && t);
-    s = pa_sink_input_list_to_string(c);
-    assert(s);
-    pa_strbuf_puts(buf, s);
-    pa_xfree(s);
-    return 0;
-}
-
-static int pa_cli_command_source_outputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    char *s;
-    assert(c && t);
-    s = pa_source_output_list_to_string(c);
-    assert(s);
-    pa_strbuf_puts(buf, s);
-    pa_xfree(s);
-    return 0;
-}
-
-static int pa_cli_command_stat(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    char s[256];
-    assert(c && t);
-
-    pa_bytes_snprint(s, sizeof(s), c->memblock_stat->total_size);
-    pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
-                     c->memblock_stat->total,
-                     s);
-
-    pa_bytes_snprint(s, sizeof(s), c->memblock_stat->allocated_size);
-    pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
-                     c->memblock_stat->allocated,
-                     s);
-
-    pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c));
-    pa_strbuf_printf(buf, "Total sample cache size: %s.\n", s);
-
-    pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec);
-    pa_strbuf_printf(buf, "Default sample spec: %s\n", s);
-
-    pa_strbuf_printf(buf, "Default sink name: %s\n"
-                     "Default source name: %s\n",
-                     pa_namereg_get_default_sink_name(c),
-                     pa_namereg_get_default_source_name(c));
-
-    return 0;
-}
-
-static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    assert(c && t);
-    pa_cli_command_stat(c, t, buf, fail);
-    pa_cli_command_modules(c, t, buf, fail);
-    pa_cli_command_sinks(c, t, buf, fail);
-    pa_cli_command_sources(c, t, buf, fail);
-    pa_cli_command_clients(c, t, buf, fail);
-    pa_cli_command_sink_inputs(c, t, buf, fail);
-    pa_cli_command_source_outputs(c, t, buf, fail);
-    pa_cli_command_scache_list(c, t, buf, fail);
-    pa_cli_command_autoload_list(c, t, buf, fail);
-    return 0;
-}
-
-static int pa_cli_command_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    struct pa_module *m;
-    const char *name;
-    assert(c && t);
-
-    if (!(name = pa_tokenizer_get(t, 1))) {
-        pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
-        return -1;
-    }
-    
-    if (!(m = pa_module_load(c, name,  pa_tokenizer_get(t, 2)))) {
-        pa_strbuf_puts(buf, "Module load failed.\n");
-        return -1;
-    }
-
-    return 0;
-}
-
-static int pa_cli_command_unload(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    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(m);
-    return 0;
-}
-
-static int pa_cli_command_sink_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    const char *n, *v;
-    struct pa_sink *sink;
-    uint32_t 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;
-    }
-
-    if (pa_atou(v, &volume) < 0) {
-        pa_strbuf_puts(buf, "Failed to parse volume.\n");
-        return -1;
-    }
-
-    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
-        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
-        return -1;
-    }
-
-    pa_sink_set_volume(sink, (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) {
-    const char *n, *v;
-    struct pa_sink_input *si;
-    uint32_t volume;
-    uint32_t index;
-
-    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;
-    }
-
-    if (pa_atou(v, &volume) < 0) {
-        pa_strbuf_puts(buf, "Failed to parse volume.\n");
-        return -1;
-    }
-
-    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) index))) {
-        pa_strbuf_puts(buf, "No sink input found with this index.\n");
-        return -1;
-    }
-
-    pa_sink_input_set_volume(si, (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) {
-    const char *n;
-    assert(c && t);
-
-    if (!(n = pa_tokenizer_get(t, 1))) {
-        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
-        return -1;
-    }
-
-    pa_namereg_set_default(c, n, PA_NAMEREG_SINK);
-    return 0;
-}
-
-static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    const char *n;
-    assert(c && t);
-
-    if (!(n = pa_tokenizer_get(t, 1))) {
-        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
-        return -1;
-    }
-
-    pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE);
-    return 0;
-}
-
-static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    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) {
-    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) {
-    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;
-}
-
-static int pa_cli_command_scache_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    char *s;
-    assert(c && t);
-    s = pa_scache_list_to_string(c);
-    assert(s);
-    pa_strbuf_puts(buf, s);
-    pa_xfree(s);
-    return 0;
-}
-
-static int pa_cli_command_scache_play(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    const char *n, *sink_name;
-    struct pa_sink *sink;
-    assert(c && t && buf && fail);
-
-    if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
-        pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
-        return -1;
-    }
-
-    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
-        pa_strbuf_puts(buf, "No sink by that name.\n");
-        return -1;
-    }
-
-    if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) {
-        pa_strbuf_puts(buf, "Failed to play sample.\n");
-        return -1;
-    }
-
-    return 0;
-}
-
-static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    const char *n;
-    assert(c && t && buf && fail);
-
-    if (!(n = pa_tokenizer_get(t, 1))) {
-        pa_strbuf_puts(buf, "You need to specify a sample name.\n");
-        return -1;
-    }
-
-    if (pa_scache_remove_item(c, n) < 0) {
-        pa_strbuf_puts(buf, "Failed to remove sample.\n");
-        return -1;
-    }
-
-    return 0;
-}
-
-static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    const char *fname, *n;
-    int r;
-    assert(c && t && buf && fail);
-
-    if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
-        pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
-        return -1;
-    }
-
-    if (strstr(pa_tokenizer_get(t, 0), "lazy"))
-        r = pa_scache_add_file_lazy(c, n, fname, NULL);
-    else
-        r = pa_scache_add_file(c, n, fname, NULL);
-
-    if (r < 0)
-        pa_strbuf_puts(buf, "Failed to load sound file.\n");
-
-    return 0;
-}
-
-static int pa_cli_command_scache_load_dir(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    const char *pname;
-    assert(c && t && buf && fail);
-
-    if (!(pname = pa_tokenizer_get(t, 1))) {
-        pa_strbuf_puts(buf, "You need to specify a path name.\n");
-        return -1;
-    }
-
-    if (pa_scache_add_directory_lazy(c, pname) < 0) {
-        pa_strbuf_puts(buf, "Failed to load directory.\n");
-        return -1;
-    }
-
-    return 0;
-}
-
-static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    const char *fname, *sink_name;
-    struct pa_sink *sink;
-    assert(c && t && buf && fail);
-
-    if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
-        pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
-        return -1;
-    }
-
-    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
-        pa_strbuf_puts(buf, "No sink by that name.\n");
-        return -1;
-    }
-
-
-    return pa_play_file(sink, fname, PA_VOLUME_NORM);
-}
-
-static int pa_cli_command_autoload_add(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    const char *a, *b;
-    assert(c && t && buf && fail);
-
-    if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
-        pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
-        return -1;
-    }
-
-    pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL);
-    
-    return 0;
-}
-
-static int pa_cli_command_autoload_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    const char *name;
-    assert(c && t && buf && fail);
-    
-    if (!(name = pa_tokenizer_get(t, 1))) {
-        pa_strbuf_puts(buf, "You need to specify a device name\n");
-        return -1;
-    }
-
-    if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) {
-        pa_strbuf_puts(buf, "Failed to remove autload entry\n");
-        return -1;
-    }
-
-    return 0;        
-}
-
-static int pa_cli_command_autoload_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    char *s;
-    assert(c && t);
-    s = pa_autoload_list_to_string(c);
-    assert(s);
-    pa_strbuf_puts(buf, s);
-    pa_xfree(s);
-    return 0;
-}
-
-static int pa_cli_command_list_props(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    assert(c && t);
-    pa_property_dump(c, buf);
-    return 0;
-}
-
-static int pa_cli_command_dump(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail) {
-    struct pa_module *m;
-    struct pa_sink *s;
-    int nl;
-    const char *p;
-    uint32_t index;
-    char txt[256];
-    time_t now;
-    void *i;
-    struct pa_autoload_entry *a;
-    
-    assert(c && t);
-
-    time(&now);
-
-    pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt));
-
-    
-    for (m = pa_idxset_first(c->modules, &index); m; m = pa_idxset_next(c->modules, &index)) {
-        if (m->auto_unload)
-            continue;
-
-        pa_strbuf_printf(buf, "load-module %s", m->name);
-
-        if (m->argument)
-            pa_strbuf_printf(buf, " %s", m->argument);
-
-        pa_strbuf_puts(buf, "\n");
-    }
-
-    nl = 0;
-
-    for (s = pa_idxset_first(c->sinks, &index); s; s = pa_idxset_next(c->sinks, &index)) {
-        if (s->volume == PA_VOLUME_NORM)
-            continue;
-        
-        if (s->owner && s->owner->auto_unload)
-            continue;
-
-        if (!nl) {
-            pa_strbuf_puts(buf, "\n");
-            nl = 1;
-        }
-        
-        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", s->name, s->volume);
-    }
-
-
-    if (c->autoload_hashmap) {
-        nl = 0;
-        
-        i = NULL;
-        while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) {
-
-            if (!nl) {
-                pa_strbuf_puts(buf, "\n");
-                nl = 1;
-            }
-            
-            pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module);
-            
-            if (a->argument)
-                pa_strbuf_printf(buf, " %s", a->argument);
-            
-            pa_strbuf_puts(buf, "\n");
-        }
-    }
-
-    nl = 0;
-    
-    if ((p = pa_namereg_get_default_sink_name(c))) {
-        if (!nl) {
-            pa_strbuf_puts(buf, "\n");
-            nl = 1;
-        }
-        pa_strbuf_printf(buf, "set-default-sink %s\n", p);
-    }
-
-    if ((p = pa_namereg_get_default_source_name(c))) {
-        if (!nl) {
-            pa_strbuf_puts(buf, "\n");
-            nl = 1;
-        }
-        pa_strbuf_printf(buf, "set-default-source %s\n", p);
-    }
-
-    pa_strbuf_puts(buf, "\n### EOF\n");
-
-    return 0;
-}
-
-
-int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail) {
-    const char *cs;
-    
-    cs = s+strspn(s, whitespace);
-
-    if (*cs == '#' || !*cs)
-        return 0;
-    else if (*cs == '.') {
-        if (!strcmp(cs, FAIL_META))
-            *fail = 1;
-        else if (!strcmp(cs, NOFAIL_META))
-            *fail = 0;
-        else {
-            size_t l;
-            l = strcspn(cs, whitespace);
-
-            if (l == sizeof(INCLUDE_META)-1 && !strncmp(cs, INCLUDE_META, l)) {
-                const char *filename = cs+l+strspn(cs+l, whitespace);
-
-                if (pa_cli_command_execute_file(c, filename, buf, fail) < 0)
-                    if (*fail) return -1;
-            } else {
-                pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
-                if (*fail) return -1;
-            }
-        }
-    } else {
-        const struct command*command;
-        int unknown = 1;
-        size_t l;
-        
-        l = strcspn(cs, whitespace);
-
-        for (command = commands; command->name; command++) 
-            if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
-                int ret;
-                struct pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
-                assert(t);
-                ret = command->proc(c, t, buf, fail);
-                pa_tokenizer_free(t);
-                unknown = 0;
-
-                if (ret < 0 && *fail)
-                    return -1;
-                
-                break;
-            }
-
-        if (unknown) {
-            pa_strbuf_printf(buf, "Unknown command: %s\n", cs);
-            if (*fail)
-                return -1;
-        }
-    }
-
-    return 0;
-}
-
-int pa_cli_command_execute_file(struct pa_core *c, const char *fn, struct pa_strbuf *buf, int *fail) {
-    char line[256];
-    FILE *f = NULL;
-    int ret = -1;
-    assert(c && fn && buf);
-
-    if (!(f = fopen(fn, "r"))) {
-        pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, strerror(errno));
-        if (!*fail)
-            ret = 0;
-        goto fail;
-    }
-
-    while (fgets(line, sizeof(line), f)) {
-        char *e = line + strcspn(line, linebreak);
-        *e = 0;
-
-        if (pa_cli_command_execute_line(c, line, buf, fail) < 0 && *fail)
-            goto fail;
-    }
-
-    ret = 0;
-
-fail:
-    if (f)
-        fclose(f);
-
-    return ret;
-}
-
-int pa_cli_command_execute(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail) {
-    const char *p;
-    assert(c && s && buf && fail);
-
-    p = s;
-    while (*p) {
-        size_t l = strcspn(p, linebreak);
-        char *line = pa_xstrndup(p, l);
-        
-        if (pa_cli_command_execute_line(c, line, buf, fail) < 0&& *fail) {
-            pa_xfree(line);
-            return -1;
-        }
-        pa_xfree(line);
-
-        p += l;
-        p += strspn(p, linebreak);
-    }
-
-    return 0;
-}
diff --git a/polyp/cli-text.c b/polyp/cli-text.c
deleted file mode 100644 (file)
index d4c46dc..0000000
+++ /dev/null
@@ -1,352 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <string.h>
-
-#include "cli-text.h"
-#include "module.h"
-#include "client.h"
-#include "sink.h"
-#include "source.h"
-#include "sink-input.h"
-#include "source-output.h"
-#include "strbuf.h"
-#include "sample-util.h"
-#include "scache.h"
-#include "autoload.h"
-#include "xmalloc.h"
-
-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\tused: %i\n\tauto unload: %s\n", m->index, m->name, m->argument, m->n_used, m->auto_unload ? "yes" : "no");
-    
-    return pa_strbuf_tostring_free(s);
-}
-
-char *pa_client_list_to_string(struct pa_core *c) {
-    struct pa_strbuf *s;
-    struct pa_client *client;
-    uint32_t index = PA_IDXSET_INVALID;
-    char tid[5];
-    assert(c);
-
-    s = pa_strbuf_new();
-    assert(s);
-
-    pa_strbuf_printf(s, "%u client(s) logged in.\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\ttype: <%s>\n", client->index, client->name, pa_typeid_to_string(client->typeid, tid, sizeof(tid)));
-
-        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;
-    uint32_t index = PA_IDXSET_INVALID;
-    char tid[5];
-    assert(c);
-
-    s = pa_strbuf_new();
-    assert(s);
-
-    pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_ncontents(c->sinks));
-
-    for (sink = pa_idxset_first(c->sinks, &index); sink; sink = pa_idxset_next(c->sinks, &index)) {
-        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
-        pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec);
-        assert(sink->monitor_source);
-        pa_strbuf_printf(
-            s,
-            "  %c index: %u\n\tname: <%s>\n\ttype: <%s>\n\tvolume: <0x%04x> (%0.2fdB)\n\tlatency: <%0.0f usec>\n\tmonitor_source: <%u>\n\tsample_spec: <%s>\n",
-            c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
-            sink->index, sink->name,
-            pa_typeid_to_string(sink->typeid, tid, sizeof(tid)),
-            (unsigned) sink->volume,
-            pa_volume_to_dB(sink->volume),
-            (float) 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;
-    uint32_t index = PA_IDXSET_INVALID;
-    char tid[5];
-    assert(c);
-
-    s = pa_strbuf_new();
-    assert(s);
-
-    pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_ncontents(c->sources));
-
-    for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index)) {
-        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
-        pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec);
-        pa_strbuf_printf(s, "  %c index: %u\n\tname: <%s>\n\ttype: <%s>\n\tlatency: <%0.0f usec>\n\tsample_spec: <%s>\n",
-                         c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
-                         source->index,
-                         source->name,
-                         pa_typeid_to_string(source->typeid, tid, sizeof(tid)),
-                         (float) pa_source_get_latency(source),
-                         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;
-    char tid[5];
-    static const char* const state_table[] = {
-        "RUNNING",
-        "CORKED",
-        "DISCONNECTED"
-    };
-    assert(c);
-
-    s = pa_strbuf_new();
-    assert(s);
-
-    pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_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_SPEC_SNPRINT_MAX];
-        const char *rm;
-        pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec);
-        assert(o->source);
-
-        if (!(rm = pa_resample_method_to_string(pa_source_output_get_resample_method(o))))
-            rm = "invalid";
-        
-        pa_strbuf_printf(
-            s, "  index: %u\n\tname: '%s'\n\ttype: <%s>\n\tstate: %s\n\tsource: <%u> '%s'\n\tsample_spec: <%s>\n\tresample method: %s\n",
-            o->index,
-            o->name,
-            pa_typeid_to_string(o->typeid, tid, sizeof(tid)),
-            state_table[o->state],
-            o->source->index, o->source->name,
-            ss,
-            rm);
-        if (o->owner)
-            pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index);
-        if (o->client)
-            pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name);
-    }
-    
-    return pa_strbuf_tostring_free(s);
-}
-
-char *pa_sink_input_list_to_string(struct pa_core *c) {
-    struct pa_strbuf *s;
-    struct pa_sink_input *i;
-    uint32_t index = PA_IDXSET_INVALID;
-    char tid[5];
-    static const char* const state_table[] = {
-        "RUNNING",
-        "CORKED",
-        "DISCONNECTED"
-    };
-
-    assert(c);
-    s = pa_strbuf_new();
-    assert(s);
-
-    pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_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_SPEC_SNPRINT_MAX];
-        const char *rm;
-
-        if (!(rm = pa_resample_method_to_string(pa_sink_input_get_resample_method(i))))
-            rm = "invalid";
-
-        pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec);
-        assert(i->sink);
-        pa_strbuf_printf(
-            s, "    index: %u\n\tname: <%s>\n\ttype: <%s>\n\tstate: %s\n\tsink: <%u> '%s'\n\tvolume: <0x%04x> (%0.2fdB)\n\tlatency: <%0.0f usec>\n\tsample_spec: <%s>\n\tresample method: %s\n",
-            i->index,
-            i->name,
-            pa_typeid_to_string(i->typeid, tid, sizeof(tid)),
-            state_table[i->state],
-            i->sink->index, i->sink->name,
-            (unsigned) i->volume,
-            pa_volume_to_dB(i->volume),
-            (float) pa_sink_input_get_latency(i),
-            ss,
-            rm);
-
-        if (i->owner)
-            pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index);
-        if (i->client)
-            pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name);
-    }
-    
-    return pa_strbuf_tostring_free(s);
-}
-
-char *pa_scache_list_to_string(struct pa_core *c) {
-    struct pa_strbuf *s;
-    assert(c);
-
-    s = pa_strbuf_new();
-    assert(s);
-
-    pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_ncontents(c->scache) : 0);
-
-    if (c->scache) {
-        struct pa_scache_entry *e;
-        uint32_t index = PA_IDXSET_INVALID;
-
-        for (e = pa_idxset_first(c->scache, &index); e; e = pa_idxset_next(c->scache, &index)) {
-            double l = 0;
-            char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a";
-            
-            if (e->memchunk.memblock) {
-                pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
-                l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec);
-            }
-            
-            pa_strbuf_printf(
-                s, "    name: <%s>\n\tindex: <%u>\n\tsample_spec: <%s>\n\tlength: <%u>\n\tduration: <%0.1fs>\n\tvolume: <0x%04x>\n\tlazy: %s\n\tfilename: %s\n",
-                e->name,
-                e->index,
-                ss,
-                e->memchunk.memblock ? e->memchunk.length : 0,
-                l,
-                e->volume,
-                e->lazy ? "yes" : "no",
-                e->filename ? e->filename : "n/a");
-        }
-    }
-
-    return pa_strbuf_tostring_free(s);
-}
-
-char *pa_autoload_list_to_string(struct pa_core *c) {
-    struct pa_strbuf *s;
-    assert(c);
-
-    s = pa_strbuf_new();
-    assert(s);
-
-    pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_ncontents(c->autoload_hashmap) : 0);
-
-    if (c->autoload_hashmap) {
-        struct pa_autoload_entry *e;
-        void *state = NULL;
-
-        while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
-            pa_strbuf_printf(
-                s, "    name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n",
-                e->name,
-                e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
-                e->index,
-                e->module,
-                e->argument);
-
-        }
-    }
-
-    return pa_strbuf_tostring_free(s);
-}
-
-char *pa_full_status_string(struct pa_core *c) {
-    struct pa_strbuf *s;
-    int i;
-
-    s = pa_strbuf_new();
-
-    for (i = 0; i < 8; i++) {
-        char *t = NULL;
-        
-        switch (i) {
-            case 0: 
-                t = pa_sink_list_to_string(c);
-                break;
-            case 1:
-                t = pa_source_list_to_string(c);
-                break;
-            case 2:
-                t = pa_sink_input_list_to_string(c);
-                break;
-            case 3:
-                t = pa_source_output_list_to_string(c);
-                break;
-            case 4: 
-                t = pa_client_list_to_string(c);
-                break;
-            case 5:
-                t = pa_module_list_to_string(c);
-                break;
-            case 6:
-                t = pa_scache_list_to_string(c);
-                break;
-            case 7:
-                t = pa_autoload_list_to_string(c);
-                break;
-        }
-
-        pa_strbuf_puts(s, t);
-        pa_xfree(t);
-    }
-
-    return pa_strbuf_tostring_free(s);
-}
diff --git a/polyp/cli-text.h b/polyp/cli-text.h
deleted file mode 100644 (file)
index d19dd48..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef fooclitexthfoo
-#define fooclitexthfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "core.h"
-
-/* Some functions to generate pretty formatted listings of
- * entities. The returned strings have to be freed manually. */
-
-char *pa_sink_input_list_to_string(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);
-char *pa_scache_list_to_string(struct pa_core *c);
-char *pa_autoload_list_to_string(struct pa_core *c);
-
-char *pa_full_status_string(struct pa_core *c);
-
-#endif
-
diff --git a/polyp/cli.c b/polyp/cli.c
deleted file mode 100644 (file)
index 16323cb..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <stdlib.h>
-
-#include "ioline.h"
-#include "cli.h"
-#include "module.h"
-#include "sink.h"
-#include "source.h"
-#include "client.h"
-#include "sink-input.h"
-#include "source-output.h"
-#include "tokenizer.h"
-#include "strbuf.h"
-#include "namereg.h"
-#include "cli-text.h"
-#include "cli-command.h"
-#include "xmalloc.h"
-#include "log.h"
-
-#define PROMPT ">>> "
-#define PA_TYPEID_CLI PA_TYPEID_MAKE('C', 'L', 'I', '_')
-
-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, kill_requested, defer_kill;
-};
-
-static void line_callback(struct pa_ioline *line, const char *s, void *userdata);
-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 = pa_xmalloc(sizeof(struct pa_cli));
-    c->core = core;
-    c->line = pa_ioline_new(io);
-    assert(c->line);
-
-    c->userdata = NULL;
-    c->eof_callback = NULL;
-
-    pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
-    c->client = pa_client_new(core, PA_TYPEID_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"PROMPT);
-
-    c->fail = c->kill_requested = c->defer_kill = 0;
-    
-    return c;
-}
-
-void pa_cli_free(struct pa_cli *c) {
-    assert(c);
-    pa_ioline_close(c->line);
-    pa_ioline_unref(c->line);
-    pa_client_free(c->client);
-    pa_xfree(c);
-}
-
-static void client_kill(struct pa_client *client) {
-    struct pa_cli *c;
-    assert(client && client->userdata);
-    c = client->userdata;
-    
-    pa_log_debug(__FILE__": CLI client killed.\n");
-    if (c->defer_kill)
-        c->kill_requested = 1;
-    else {
-        if (c->eof_callback)
-            c->eof_callback(c, c->userdata);
-    }
-}
-
-static void line_callback(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) {
-        pa_log_debug(__FILE__": CLI got EOF from user.\n");
-        if (c->eof_callback)
-            c->eof_callback(c, c->userdata);
-
-        return;
-    }
-
-    buf = pa_strbuf_new();
-    assert(buf);
-    c->defer_kill++;
-    pa_cli_command_execute_line(c->core, s, buf, &c->fail);
-    c->defer_kill--;
-    pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf));
-    pa_xfree(p);
-
-    if (c->kill_requested) {
-        if (c->eof_callback)
-            c->eof_callback(c, c->userdata);
-    } else    
-        pa_ioline_puts(line, PROMPT);
-}
-
-void pa_cli_set_eof_callback(struct pa_cli *c, void (*cb)(struct pa_cli*c, void *userdata), void *userdata) {
-    assert(c);
-    c->eof_callback = cb;
-    c->userdata = userdata;
-}
diff --git a/polyp/client.c b/polyp/client.c
deleted file mode 100644 (file)
index dca7b52..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "client.h"
-#include "xmalloc.h"
-#include "subscribe.h"
-#include "log.h"
-
-struct pa_client *pa_client_new(struct pa_core *core, const char *name, const char *driver) {
-    struct pa_client *c;
-    int r;
-    assert(core);
-
-    c = pa_xmalloc(sizeof(struct pa_client));
-    c->name = pa_xstrdup(name);
-    c->driver = pa_xstrdup(driver);
-    c->owner = NULL;
-    c->core = core;
-
-    c->kill = NULL;
-    c->userdata = NULL;
-
-    r = pa_idxset_put(core->clients, c, &c->index);
-    assert(c->index != PA_IDXSET_INVALID && r >= 0);
-
-    pa_log_info(__FILE__": created %u \"%s\"\n", c->index, c->name);
-    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
-
-    pa_core_check_quit(core);
-    
-    return c;
-}
-
-void pa_client_free(struct pa_client *c) {
-    assert(c && c->core);
-
-    pa_idxset_remove_by_data(c->core->clients, c, NULL);
-
-    pa_core_check_quit(c->core);
-
-    pa_log_info(__FILE__": freed %u \"%s\"\n", c->index, c->name);
-    pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
-    pa_xfree(c->name);
-    pa_xfree(c->driver);
-    pa_xfree(c);
-}
-
-void pa_client_kill(struct pa_client *c) {
-    assert(c);
-    if (!c->kill) {
-        pa_log_warn(__FILE__": kill() operation not implemented for client %u\n", c->index);
-        return;
-    }
-
-    c->kill(c);
-}
-
-void pa_client_set_name(struct pa_client *c, const char *name) {
-    assert(c);
-    pa_xfree(c->name);
-    c->name = pa_xstrdup(name);
-
-    pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
-}
diff --git a/polyp/client.conf.in b/polyp/client.conf.in
deleted file mode 100644 (file)
index fbf645a..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-# $Id$
-#
-# This file is part of polypaudio.
-#
-# polypaudio is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# polypaudio is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA.
-
-## Configuration file for polypaudio clients. Default values are
-## commented out.  Use either ; or # for commenting
-
-## Path to the polypaudio daemon to run when autospawning.
-; daemon-binary = @POLYPAUDIO_BINARY@
-
-## Extra arguments to pass to the polypaudio daemon
-; extra-arguments = --log-target=syslog --exit-idle-time=5
-
-## The default sink to connect to
-; default-sink = 
-
-## The default source to connect to
-; default-source =
-
-## The default sever to connect to
-; default-server =
-
-## Autospawn daemons?
-; autospawn = 0
diff --git a/polyp/core.c b/polyp/core.c
deleted file mode 100644 (file)
index c213faa..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <assert.h>
-#include <stdio.h>
-#include <signal.h>
-
-#include "core.h"
-#include "module.h"
-#include "sink.h"
-#include "source.h"
-#include "namereg.h"
-#include "util.h"
-#include "scache.h"
-#include "autoload.h"
-#include "xmalloc.h"
-#include "subscribe.h"
-#include "props.h"
-#include "random.h"
-
-struct pa_core* pa_core_new(struct pa_mainloop_api *m) {
-    struct pa_core* c;
-    c = pa_xmalloc(sizeof(struct pa_core));
-
-    c->mainloop = m;
-    c->clients = pa_idxset_new(NULL, NULL);
-    c->sinks = pa_idxset_new(NULL, NULL);
-    c->sources = pa_idxset_new(NULL, NULL);
-    c->source_outputs = pa_idxset_new(NULL, NULL);
-    c->sink_inputs = pa_idxset_new(NULL, NULL);
-
-    c->default_source_name = c->default_sink_name = NULL;
-
-    c->modules = NULL;
-    c->namereg = NULL;
-    c->scache = NULL;
-    c->autoload_idxset = NULL;
-    c->autoload_hashmap = NULL;
-
-    c->default_sample_spec.format = PA_SAMPLE_S16NE;
-    c->default_sample_spec.rate = 44100;
-    c->default_sample_spec.channels = 2;
-
-    c->module_auto_unload_event = NULL;
-    c->module_defer_unload_event = NULL;
-    c->scache_auto_unload_event = NULL;
-
-    c->subscription_defer_event = NULL;
-    c->subscription_event_queue = NULL;
-    c->subscriptions = NULL;
-
-    c->memblock_stat = pa_memblock_stat_new();
-
-    c->disallow_module_loading = 0;
-
-    c->quit_event = NULL;
-
-    c->exit_idle_time = -1;
-    c->module_idle_time = 20;
-    c->scache_idle_time = 20;
-
-    c->resample_method = PA_RESAMPLER_SRC_SINC_FASTEST;
-
-    pa_property_init(c);
-
-    pa_random(&c->cookie, sizeof(c->cookie));
-    
-    pa_check_signal_is_blocked(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_scache_free(c);
-    pa_namereg_free(c);
-    pa_autoload_free(c);
-    pa_subscription_free_all(c);
-
-    if (c->quit_event)
-        c->mainloop->time_free(c->quit_event);
-
-    pa_xfree(c->default_source_name);
-    pa_xfree(c->default_sink_name);
-
-    pa_memblock_stat_unref(c->memblock_stat);
-
-    pa_property_cleanup(c);
-    
-    pa_xfree(c);    
-}
-
-static void quit_callback(struct pa_mainloop_api*m, struct pa_time_event *e, const struct timeval *tv, void *userdata) {
-    struct pa_core *c = userdata;
-    assert(c->quit_event = e);
-
-    m->quit(m, 0);
-}
-
-void pa_core_check_quit(struct pa_core *c) {
-    assert(c);
-
-    if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_ncontents(c->clients) == 0) {
-        struct timeval tv;
-        gettimeofday(&tv, NULL);
-        tv.tv_sec+= c->exit_idle_time;
-        c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c);
-    } else if (c->quit_event && pa_idxset_ncontents(c->clients) > 0) {
-        c->mainloop->time_free(c->quit_event);
-        c->quit_event = NULL;
-    }
-}
-
diff --git a/polyp/core.h b/polyp/core.h
deleted file mode 100644 (file)
index 3660442..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef foocorehfoo
-#define foocorehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "idxset.h"
-#include "hashmap.h"
-#include "mainloop-api.h"
-#include "sample.h"
-#include "memblock.h"
-#include "resampler.h"
-
-/* The core structure of polypaudio. Every polypaudio daemon contains
- * exactly one of these. It is used for storing kind of global
- * variables for the daemon. */
-
-struct pa_core {
-    /* A random value which may be used to identify this instance of
-     * polypaudio. Not cryptographically secure in any way. */
-    uint32_t cookie;
-    
-    struct pa_mainloop_api *mainloop;
-
-    /* idxset of all kinds of entities */
-    struct pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset;
-
-    /* Some hashmaps for all sorts of entities */
-    struct pa_hashmap *namereg, *autoload_hashmap, *properties;
-
-    /* The name of the default sink/source */
-    char *default_source_name, *default_sink_name;
-
-    struct pa_sample_spec default_sample_spec;
-    struct pa_time_event *module_auto_unload_event;
-    struct pa_defer_event *module_defer_unload_event;
-
-    struct pa_defer_event *subscription_defer_event;
-    struct pa_queue *subscription_event_queue;
-    struct pa_subscription *subscriptions;
-
-    struct pa_memblock_stat *memblock_stat;
-
-    int disallow_module_loading;
-    int exit_idle_time, module_idle_time, scache_idle_time;
-
-    struct pa_time_event *quit_event;
-
-    struct pa_time_event *scache_auto_unload_event;
-
-    enum pa_resample_method resample_method;
-};
-
-struct pa_core* pa_core_new(struct pa_mainloop_api *m);
-void pa_core_free(struct pa_core*c);
-
-/* Check whether noone is connected to this core */
-void pa_core_check_quit(struct pa_core *c);
-
-#endif
diff --git a/polyp/daemon-conf.c b/polyp/daemon-conf.c
deleted file mode 100644 (file)
index a6afd05..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <unistd.h>
-
-#include "daemon-conf.h"
-#include "util.h"
-#include "xmalloc.h"
-#include "strbuf.h"
-#include "conf-parser.h"
-#include "resampler.h"
-
-#ifndef DEFAULT_CONFIG_DIR
-#define DEFAULT_CONFIG_DIR "/etc/polypaudio"
-#endif
-
-#define DEFAULT_SCRIPT_FILE DEFAULT_CONFIG_DIR"/default.pa"
-#define DEFAULT_SCRIPT_FILE_USER ".polypaudio/default.pa"
-#define DEFAULT_CONFIG_FILE DEFAULT_CONFIG_DIR"/daemon.conf"
-#define DEFAULT_CONFIG_FILE_USER ".polypaudio/daemon.conf"
-
-#define ENV_SCRIPT_FILE "POLYP_SCRIPT"
-#define ENV_CONFIG_FILE "POLYP_CONFIG"
-#define ENV_DL_SEARCH_PATH "POLYP_DLPATH"
-
-static const struct pa_daemon_conf default_conf = {
-    .cmd = PA_CMD_DAEMON,
-    .daemonize = 0,
-    .fail = 1,
-    .high_priority = 0,
-    .disallow_module_loading = 0,
-    .exit_idle_time = -1,
-    .module_idle_time = 20,
-    .scache_idle_time = 20,
-    .auto_log_target = 1,
-    .script_commands = NULL,
-    .dl_search_path = NULL,
-    .default_script_file = NULL,
-    .log_target = PA_LOG_SYSLOG,
-    .log_level = PA_LOG_NOTICE,
-    .resample_method = PA_RESAMPLER_SRC_SINC_FASTEST,
-    .config_file = NULL,
-    .use_pid_file = 1
-};
-
-struct pa_daemon_conf* pa_daemon_conf_new(void) {
-    FILE *f;
-    struct pa_daemon_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
-
-    if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file)))
-        fclose(f);
-
-#ifdef DLSEARCHPATH
-    c->dl_search_path = pa_xstrdup(DLSEARCHPATH);
-#endif
-    return c;
-}
-
-void pa_daemon_conf_free(struct pa_daemon_conf *c) {
-    assert(c);
-    pa_xfree(c->script_commands);
-    pa_xfree(c->dl_search_path);
-    pa_xfree(c->default_script_file);
-    pa_xfree(c->config_file);
-    pa_xfree(c);
-}
-
-int pa_daemon_conf_set_log_target(struct pa_daemon_conf *c, const char *string) {
-    assert(c && string);
-
-    if (!strcmp(string, "auto"))
-        c->auto_log_target = 1;
-    else if (!strcmp(string, "syslog")) {
-        c->auto_log_target = 0;
-        c->log_target = PA_LOG_SYSLOG;
-    } else if (!strcmp(string, "stderr")) {
-        c->auto_log_target = 0;
-        c->log_target = PA_LOG_STDERR;
-    } else
-        return -1;
-
-    return 0;
-}
-
-int pa_daemon_conf_set_log_level(struct pa_daemon_conf *c, const char *string) {
-    uint32_t u;
-    assert(c && string);
-
-    if (pa_atou(string, &u) >= 0) {
-        if (u >= PA_LOG_LEVEL_MAX)
-            return -1;
-
-        c->log_level = (enum pa_log_level) u;
-    } else if (pa_startswith(string, "debug"))
-        c->log_level = PA_LOG_DEBUG;
-    else if (pa_startswith(string, "info"))
-        c->log_level = PA_LOG_INFO;
-    else if (pa_startswith(string, "notice"))
-        c->log_level = PA_LOG_NOTICE;
-    else if (pa_startswith(string, "warn"))
-        c->log_level = PA_LOG_WARN;
-    else if (pa_startswith(string, "err"))
-        c->log_level = PA_LOG_ERROR;
-    else
-        return -1;
-
-    return 0;
-}
-
-int pa_daemon_conf_set_resample_method(struct pa_daemon_conf *c, const char *string) {
-    int m;
-    assert(c && string);
-
-    if ((m = pa_parse_resample_method(string)) < 0)
-        return -1;
-
-    c->resample_method = m;
-    return 0;
-}
-
-static int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
-    struct pa_daemon_conf *c = data;
-    assert(filename && lvalue && rvalue && data);
-
-    if (pa_daemon_conf_set_log_target(c, rvalue) < 0) {
-        pa_log(__FILE__": [%s:%u] Invalid log target '%s'.\n", filename, line, rvalue);
-        return -1;
-    }
-
-    return 0;
-}
-
-static int parse_log_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
-    struct pa_daemon_conf *c = data;
-    assert(filename && lvalue && rvalue && data);
-
-    if (pa_daemon_conf_set_log_level(c, rvalue) < 0) {
-        pa_log(__FILE__": [%s:%u] Invalid log level '%s'.\n", filename, line, rvalue);
-        return -1;
-    }
-
-    return 0;
-}
-
-static int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
-    struct pa_daemon_conf *c = data;
-    assert(filename && lvalue && rvalue && data);
-
-    if (pa_daemon_conf_set_resample_method(c, rvalue) < 0) {
-        pa_log(__FILE__": [%s:%u] Inavalid resample method '%s'.\n", filename, line, rvalue);
-        return -1;
-    }
-
-    return 0;
-}
-
-int pa_daemon_conf_load(struct pa_daemon_conf *c, const char *filename) {
-    int r = -1;
-    FILE *f = NULL;
-    
-    struct pa_config_item table[] = {
-        { "daemonize",               pa_config_parse_bool,    NULL },
-        { "fail",                    pa_config_parse_bool,    NULL },
-        { "high-priority",           pa_config_parse_bool,    NULL },
-        { "disallow-module-loading", pa_config_parse_bool,    NULL },
-        { "exit-idle-time",          pa_config_parse_int,     NULL },
-        { "module-idle-time",        pa_config_parse_int,     NULL },
-        { "scache-idle-time",        pa_config_parse_int,     NULL },
-        { "dl-search-path",          pa_config_parse_string,  NULL },
-        { "default-script-file",     pa_config_parse_string,  NULL },
-        { "log-target",              parse_log_target,        NULL },
-        { "log-level",               parse_log_level,         NULL },
-        { "verbose",                 parse_log_level,         NULL },
-        { "resample-method",         parse_resample_method,   NULL },
-        { "use-pid-file",            pa_config_parse_bool,    NULL },
-        { NULL,                      NULL,                    NULL },
-    };
-    
-    table[0].data = &c->daemonize;
-    table[1].data = &c->fail;
-    table[2].data = &c->high_priority;
-    table[3].data = &c->disallow_module_loading;
-    table[4].data = &c->exit_idle_time;
-    table[5].data = &c->module_idle_time;
-    table[6].data = &c->scache_idle_time;
-    table[7].data = &c->dl_search_path;
-    table[8].data = &c->default_script_file;
-    table[9].data = c;
-    table[10].data = c;
-    table[11].data = c;
-    table[12].data = c;
-    table[13].data = &c->use_pid_file;
-    
-    pa_xfree(c->config_file);
-    c->config_file = NULL;
-
-    f = filename ?
-        fopen(c->config_file = pa_xstrdup(filename), "r") :
-        pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
-
-    if (!f && errno != ENOENT) {
-        pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno));
-        goto finish;
-    }
-
-    r = f ? pa_config_parse(c->config_file, f, table, NULL) : 0;
-    
-finish:
-    if (f)
-        fclose(f);
-    
-    return r;
-}
-
-int pa_daemon_conf_env(struct pa_daemon_conf *c) {
-    char *e;
-
-    if ((e = getenv(ENV_DL_SEARCH_PATH))) {
-        pa_xfree(c->dl_search_path);
-        c->dl_search_path = pa_xstrdup(e);
-    }
-    if ((e = getenv(ENV_SCRIPT_FILE))) {
-        pa_xfree(c->default_script_file);
-        c->default_script_file = pa_xstrdup(e);
-    }
-
-    return 0;
-}
-
-static const char* const log_level_to_string[] = {
-    [PA_LOG_DEBUG] = "debug",
-    [PA_LOG_INFO] = "info",
-    [PA_LOG_NOTICE] = "notice",
-    [PA_LOG_WARN] = "warning",
-    [PA_LOG_ERROR] = "error"
-};
-
-char *pa_daemon_conf_dump(struct pa_daemon_conf *c) {
-    struct pa_strbuf *s = pa_strbuf_new();
-
-    if (c->config_file)
-        pa_strbuf_printf(s, "### Read from configuration file: %s ###\n", c->config_file);
-
-    assert(c->log_level <= PA_LOG_LEVEL_MAX);
-    
-    pa_strbuf_printf(s, "daemonize = %i\n", !!c->daemonize);
-    pa_strbuf_printf(s, "fail = %i\n", !!c->fail);
-    pa_strbuf_printf(s, "high-priority = %i\n", !!c->high_priority);
-    pa_strbuf_printf(s, "disallow-module-loading = %i\n", !!c->disallow_module_loading);
-    pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
-    pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
-    pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
-    pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : "");
-    pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file);
-    pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
-    pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
-    pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
-    pa_strbuf_printf(s, "use-pid-file = %i\n", c->use_pid_file);
-    
-    return pa_strbuf_tostring_free(s);
-}
diff --git a/polyp/daemon-conf.h b/polyp/daemon-conf.h
deleted file mode 100644 (file)
index 30137e8..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#ifndef foodaemonconfhfoo
-#define foodaemonconfhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "log.h"
-
-/* The actual command to execute */
-enum pa_daemon_conf_cmd {
-    PA_CMD_DAEMON,  /* the default */
-    PA_CMD_HELP,
-    PA_CMD_VERSION,
-    PA_CMD_DUMP_CONF,
-    PA_CMD_DUMP_MODULES,
-    PA_CMD_KILL,
-    PA_CMD_CHECK
-};
-
-/* A structure containing configuration data for the Polypaudio server . */
-struct pa_daemon_conf {
-    enum pa_daemon_conf_cmd cmd;
-    int daemonize,
-        fail,
-        high_priority,
-        disallow_module_loading,
-        exit_idle_time,
-        module_idle_time,
-        scache_idle_time,
-        auto_log_target,
-        use_pid_file;
-    char *script_commands, *dl_search_path, *default_script_file;
-    enum pa_log_target log_target;
-    enum pa_log_level log_level;
-    int resample_method;
-    char *config_file;
-};
-
-/* Allocate a new structure and fill it with sane defaults */
-struct pa_daemon_conf* pa_daemon_conf_new(void);
-void pa_daemon_conf_free(struct pa_daemon_conf*c);
-
-/* Load configuration data from the specified file overwriting the
- * current settings in *c. If filename is NULL load the default daemon
- * configuration file */
-int pa_daemon_conf_load(struct pa_daemon_conf *c, const char *filename);
-
-/* Pretty print the current configuration data of the daemon. The
- * returned string has to be freed manually. The output of this
- * function may be parsed with pa_daemon_conf_load(). */
-char *pa_daemon_conf_dump(struct pa_daemon_conf *c);
-
-/* Load the configuration data from the process' environment
- * overwriting the current settings in *c. */
-int pa_daemon_conf_env(struct pa_daemon_conf *c);
-
-/* Set these configuration variables in the structure by passing a string */
-int pa_daemon_conf_set_log_target(struct pa_daemon_conf *c, const char *string);
-int pa_daemon_conf_set_log_level(struct pa_daemon_conf *c, const char *string);
-int pa_daemon_conf_set_resample_method(struct pa_daemon_conf *c, const char *string);
-
-#endif
diff --git a/polyp/daemon.conf.in b/polyp/daemon.conf.in
deleted file mode 100644 (file)
index d537301..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-# $Id$
-#
-# This file is part of polypaudio.
-#
-# polypaudio is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# polypaudio is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA.
-
-## Configuration file for the polypaudio daemon. Default values are
-## commented out.  Use either ; or # for commenting
-
-# Extra verbositiy
-; verbose = 0
-
-## Daemonize after startup
-; daemonize = 0
-
-## Quit if startup fails
-; fail = 1
-
-## Renice the daemon to level -15 and try to get SCHED_FIFO
-## scheduling. This a good idea if you hear annyoing noise in the
-## playback. However, this is a certain security issue, since it works 
-## when called SUID root only. root is dropped immediately after gaining
-## the nice level and SCHED_FIFO scheduling on startup.
-; high-priority = 0
-
-## Disallow module loading after startup
-; disallow-module-loading = 0
-
-## Terminate the daemon after the last client quit and this time
-## passed. Use a negative value to disable this feature.
-; exit-idle-time = -1
-
-## Unload autoloaded modules after being idle for this time 
-; module-idle-time = 20
-
-## The path were to look for dynamic shared objects (DSOs aka
-## plugins).  You may specify more than one path seperated by
-## colons. 
-; dl-search-path = @DLSEARCHPATH@
-
-## The default script file to load. Specify an empty string for not
-## loading a default script file. The 
-; default-script-file = @DEFAULT_CONFIG_FILE@
-
-## The default log target. Use either "stderr", "syslog" or
-## "auto". The latter is equivalent to "sylog" in case daemonize is
-## true, otherwise to "stderr".
-; log-target = auto
-
-## The resampling algorithm to use. Use one of src-sinc-best-quality,
-## src-sinc-medium-quality, src-sinc-fastest, src-zero-order-hold,
-## src-linear, trivial. See the documentation of libsamplerate for an
-## explanation for the different methods. The method 'trivial' is the
-## only algorithm implemented without usage of floating point
-## numbers. If you're tight on CPU consider using this. On the other
-## hand it has the worst quality of all.
-; resample-method = sinc-fastest
-
-## Create a PID file in /tmp/polypaudio-$USER/pid. Of this is enabled
-## you may use commands like "polypaudio --kill" or "polypaudio
-## --check". If you are planning to start more than one polypaudio
-## process per user, you better disable this option since it
-## effectively disables multiple instances.
-; use-pid-file = 1
diff --git a/polyp/default.pa.in b/polyp/default.pa.in
deleted file mode 100755 (executable)
index 3aaeeaf..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#!@POLYPAUDIO_BINARY@ -nF 
-
-#
-# This file is part of polypaudio.
-#
-# polypaudio is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# polypaudio is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software Foundation,
-# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
-
-# Load audio drivers statically
-
-#load-module module-alsa-sink
-# load-module module-alsa-source device=plughw:1,0
-load-module module-oss device="/dev/dsp" sink_name=output source_name=input
-#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
-load-module module-null-sink
-#load-module module-pipe-sink
-
-# Load audio drivers automatically on access
-
-#add-autoload-sink output module-oss device="/dev/dsp" sink_name=output source_name=input
-#add-autoload-source input module-oss device="/dev/dsp" sink_name=output source_name=input
-#add-autoload-sink output module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
-#add-autoload-source input module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
-#add-autoload-sink output module-alsa-sink sink_name=output
-#add-autoload-source input module-alsa-source source_name=input
-
-# Load several protocols
-load-module module-esound-protocol-unix
-#load-module module-esound-protocol-tcp
-load-module module-native-protocol-unix
-#load-module module-simple-protocol-tcp
-#load-module module-cli-protocol-unix
-
-# Load the CLI module
-load-module module-cli
-
-# Make some devices default
-set-default-sink output
-set-default-source input
-
-.nofail
-
-# Load something to the sample cache
-load-sample x11-bell /usr/share/sounds/KDE_Notify.wav
-load-sample-dir-lazy /usr/share/sounds/*.wav
-
-# Load X11 bell module
-load-module module-x11-bell sample=x11-bell sink=output
-
-# Publish connection data in the X11 root window
-load-module module-x11-publish
-
-#load-module module-pipe-source
-#load-module module-pipe-sink
diff --git a/polyp/dumpmodules.c b/polyp/dumpmodules.c
deleted file mode 100644 (file)
index 1dc14ed..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <getopt.h>
-#include <assert.h>
-#include <stdio.h>
-#include <ltdl.h>
-
-#include "dumpmodules.h"
-#include "modinfo.h"
-
-#define PREFIX "module-"
-
-static void short_info(const char *name, const char *path, struct pa_modinfo *i) {
-    assert(name && i);
-    printf("%-40s%s\n", name, i->description ? i->description : "n/a");
-}
-
-static void long_info(const char *name, const char *path, struct pa_modinfo *i) {
-    static int nl = 0;
-    assert(name && i);
-    
-    if (nl)
-        printf("\n");
-
-    nl = 1;
-
-    printf("Name: %s\n", name);
-    
-    if (!i->description && !i->version && !i->author && !i->usage)
-        printf("No module information available\n");
-    else {
-        if (i->version)
-            printf("Version: %s\n", i->version);
-        if (i->description)
-            printf("Description: %s\n", i->description);
-        if (i->author)
-            printf("Author: %s\n", i->author);
-        if (i->usage)
-            printf("Usage: %s\n", i->usage);
-    }
-    
-    if (path)
-        printf("Path: %s\n", path);
-}
-
-static void show_info(const char *name, const char *path, void (*info)(const char *name, const char *path, struct pa_modinfo*i)) {
-    struct pa_modinfo *i;
-    
-    if ((i = pa_modinfo_get_by_name(path ? path : name))) {
-        info(name, path, i);
-        pa_modinfo_free(i);
-    }
-}
-
-static int callback(const char *path, lt_ptr data) {
-    const char *e;
-    struct pa_daemon_conf *c = (data);
-
-    if ((e = (const char*) strrchr(path, '/')))
-        e++;
-    else
-        e = path;
-
-    if (strlen(e) > sizeof(PREFIX)-1 && !strncmp(e, PREFIX, sizeof(PREFIX)-1))
-        show_info(e, path, c->log_level >= PA_LOG_INFO ? long_info : short_info);
-    
-    return 0;
-}
-
-void pa_dump_modules(struct pa_daemon_conf *c, int argc, char * const argv[]) {
-    if (argc > 0) {
-        int i;
-        for (i = 0; i < argc; i++)
-            show_info(argv[i], NULL, long_info);
-    } else
-        lt_dlforeachfile(NULL, callback, c);
-}
diff --git a/polyp/endianmacros.h b/polyp/endianmacros.h
deleted file mode 100644 (file)
index 00b992d..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef fooendianmacroshfoo
-#define fooendianmacroshfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#define INT16_SWAP(x) ((int16_t)(((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))
-
- #define UINT32_TO_LE(x) UINT32_SWAP(x)
- #define UINT32_TO_BE(x) ((uint32_t)(x))
-#else
- #define INT16_FROM_LE(x) ((int16_t)(x))
- #define INT16_FROM_BE(x) INT16_SWAP(x)
-
- #define INT16_TO_LE(x) ((int16_t)(x))
- #define INT16_TO_BE(x) INT16_SWAP(x)
-
- #define UINT16_FROM_LE(x) ((uint16_t)(x))
- #define UINT16_FROM_BE(x) UINT16_SWAP(x)
-
- #define INT32_FROM_LE(x) ((int32_t)(x))
- #define INT32_FROM_BE(x) INT32_SWAP(x)
-
- #define UINT32_FROM_LE(x) ((uint32_t)(x))
- #define UINT32_FROM_BE(x) UINT32_SWAP(x)
-
- #define UINT32_TO_LE(x) ((uint32_t)(x))
- #define UINT32_TO_BE(x) UINT32_SWAP(x)
-#endif
-
-#endif
diff --git a/polyp/gcc-printf.h b/polyp/gcc-printf.h
deleted file mode 100644 (file)
index 70679df..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef foogccprintfhfoo
-#define foogccprintfhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-/* If we're in GNU C, use some magic for detecting invalid format strings */
-
-#ifdef __GNUC__
-#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
-#else
-#define PA_GCC_PRINTF_ATTR(a,b)
-#endif
-
-#endif
diff --git a/polyp/glib-mainloop.c b/polyp/glib-mainloop.c
deleted file mode 100644 (file)
index 809c0b0..0000000
+++ /dev/null
@@ -1,536 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-
-#include "glib-mainloop.h"
-#include "idxset.h"
-#include "xmalloc.h"
-
-struct pa_io_event {
-    struct pa_glib_mainloop *mainloop;
-    int dead;
-    GIOChannel *io_channel;
-    GSource *source;
-    GIOCondition io_condition;
-    int fd;
-    void (*callback) (struct pa_mainloop_api*m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata);
-    void *userdata;
-    void (*destroy_callback) (struct pa_mainloop_api *m, struct pa_io_event*e, void *userdata);
-    struct pa_io_event *next, *prev;
-};
-
-struct pa_time_event {
-    struct pa_glib_mainloop *mainloop;
-    int dead;
-    GSource *source;
-    struct timeval timeval;
-    void (*callback) (struct pa_mainloop_api*m, struct pa_time_event *e, const struct timeval *tv, void *userdata);
-    void *userdata;
-    void (*destroy_callback) (struct pa_mainloop_api *m, struct pa_time_event*e, void *userdata);
-    struct pa_time_event *next, *prev;
-};
-
-struct pa_defer_event {
-    struct pa_glib_mainloop *mainloop;
-    int dead;
-    GSource *source;
-    void (*callback) (struct pa_mainloop_api*m, struct pa_defer_event *e, void *userdata);
-    void *userdata;
-    void (*destroy_callback) (struct pa_mainloop_api *m, struct pa_defer_event*e, void *userdata);
-    struct pa_defer_event *next, *prev;
-};
-
-struct pa_glib_mainloop {
-    GMainContext *glib_main_context;
-    struct pa_mainloop_api api;
-    GSource *cleanup_source;
-    struct pa_io_event *io_events, *dead_io_events;
-    struct pa_time_event *time_events, *dead_time_events;
-    struct pa_defer_event *defer_events, *dead_defer_events;
-};
-
-static void schedule_free_dead_events(struct pa_glib_mainloop *g);
-
-static void glib_io_enable(struct pa_io_event*e, enum pa_io_event_flags f);
-
-static struct pa_io_event* glib_io_new(struct pa_mainloop_api*m, int fd, enum pa_io_event_flags f, void (*callback) (struct pa_mainloop_api*m, struct pa_io_event*e, int fd, enum pa_io_event_flags f, void *userdata), void *userdata) {
-    struct pa_io_event *e;
-    struct pa_glib_mainloop *g;
-
-    assert(m && m->userdata && fd >= 0 && callback);
-    g = m->userdata;
-
-    e = pa_xmalloc(sizeof(struct pa_io_event));
-    e->mainloop = m->userdata;
-    e->dead = 0;
-    e->fd = fd;
-    e->callback = callback;
-    e->userdata = userdata;
-    e->destroy_callback = NULL;
-
-    e->io_channel = g_io_channel_unix_new(e->fd);
-    assert(e->io_channel);
-    e->source = NULL;
-    e->io_condition = 0;
-
-    glib_io_enable(e, f);
-
-    e->next = g->io_events;
-    if (e->next) e->next->prev = e;
-    g->io_events = e;
-    e->prev = NULL;
-    
-    return e;
-}
-
-/* The callback GLIB calls whenever an IO condition is met */
-static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
-    struct pa_io_event *e = data;
-    enum pa_io_event_flags f;
-    assert(source && e && e->io_channel == source);
-
-    f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
-        (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
-        (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
-        (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
-    
-    e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
-    return TRUE;
-}
-
-static void glib_io_enable(struct pa_io_event*e, enum pa_io_event_flags f) {
-    GIOCondition c;
-    assert(e && !e->dead);
-
-    c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
-    
-    if (c == e->io_condition)
-        return;
-    
-    if (e->source) {
-        g_source_destroy(e->source);
-        g_source_unref(e->source);
-    }
-    
-    e->source = g_io_create_watch(e->io_channel, c | G_IO_ERR | G_IO_HUP);
-    assert(e->source);
-    
-    g_source_set_callback(e->source, (GSourceFunc) io_cb, e, NULL);
-    g_source_attach(e->source, e->mainloop->glib_main_context);
-    g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
-    
-    e->io_condition = c;
-}
-
-static void glib_io_free(struct pa_io_event*e) {
-    assert(e && !e->dead);
-
-    if (e->source) {
-        g_source_destroy(e->source);
-        g_source_unref(e->source);
-        e->source = NULL;
-    }
-    
-    if (e->prev)
-        e->prev->next = e->next;
-    else
-        e->mainloop->io_events = e->next;
-
-    if (e->next)
-        e->next->prev = e->prev;
-
-    if ((e->next = e->mainloop->dead_io_events))
-        e->next->prev = e;
-
-    e->mainloop->dead_io_events = e;
-    e->prev = NULL;
-
-    e->dead = 1;
-    schedule_free_dead_events(e->mainloop);
-}
-
-static void glib_io_set_destroy(struct pa_io_event*e, void (*callback)(struct pa_mainloop_api*m, struct pa_io_event *e, void *userdata)) {
-    assert(e);
-    e->destroy_callback = callback;
-}
-
-/* Time sources */
-
-static void glib_time_restart(struct pa_time_event*e, const struct timeval *tv);
-
-static struct pa_time_event* glib_time_new(struct pa_mainloop_api*m, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*m, struct pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
-    struct pa_glib_mainloop *g;
-    struct pa_time_event *e;
-    
-    assert(m && m->userdata && tv && callback);
-    g = m->userdata;
-
-    e = pa_xmalloc(sizeof(struct pa_time_event));
-    e->mainloop = g;
-    e->dead = 0;
-    e->callback = callback;
-    e->userdata = userdata;
-    e->destroy_callback = NULL;
-    e->source = NULL;
-
-    glib_time_restart(e, tv);
-
-    e->next = g->time_events;
-    if (e->next) e->next->prev = e;
-    g->time_events = e;
-    e->prev = NULL;
-    
-    return e;
-}
-
-static guint msec_diff(const struct timeval *a, const struct timeval *b) {
-    guint r;
-    assert(a && b);
-    
-    if (a->tv_sec < b->tv_sec)
-        return 0;
-
-    if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
-        return 0;
-
-    r = (a->tv_sec-b->tv_sec)*1000;
-
-    if (a->tv_usec >= b->tv_usec)
-        r += (a->tv_usec - b->tv_usec) / 1000;
-    else
-        r -= (b->tv_usec - a->tv_usec) / 1000;
-    
-    return r;
-}
-
-static gboolean time_cb(gpointer data) {
-    struct pa_time_event* e = data;
-    assert(e && e->mainloop && e->source);
-
-    g_source_unref(e->source);
-    e->source = NULL;
-
-    e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
-    return FALSE;
-}
-
-static void glib_time_restart(struct pa_time_event*e, const struct timeval *tv) {
-    struct timeval now;
-    assert(e && e->mainloop && !e->dead);
-
-    gettimeofday(&now, NULL);
-    if (e->source) {
-        g_source_destroy(e->source);
-        g_source_unref(e->source);
-    }
-
-    if (tv) {
-        e->timeval = *tv;
-        e->source = g_timeout_source_new(msec_diff(tv, &now));
-        assert(e->source);
-        g_source_set_callback(e->source, time_cb, e, NULL);
-        g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
-        g_source_attach(e->source, e->mainloop->glib_main_context);
-    } else
-        e->source = NULL;
- }
-
-static void glib_time_free(struct pa_time_event *e) {
-    assert(e && e->mainloop && !e->dead);
-
-    if (e->source) {
-        g_source_destroy(e->source);
-        g_source_unref(e->source);
-        e->source = NULL;
-    }
-
-    if (e->prev)
-        e->prev->next = e->next;
-    else
-        e->mainloop->time_events = e->next;
-
-    if (e->next)
-        e->next->prev = e->prev;
-
-    if ((e->next = e->mainloop->dead_time_events))
-        e->next->prev = e;
-
-    e->mainloop->dead_time_events = e;
-    e->prev = NULL;
-
-    e->dead = 1;
-    schedule_free_dead_events(e->mainloop);
-}
-
-static void glib_time_set_destroy(struct pa_time_event *e, void (*callback)(struct pa_mainloop_api*m, struct pa_time_event*e, void *userdata)) {
-    assert(e);
-    e->destroy_callback = callback;
-}
-
-/* Deferred sources */
-
-static void glib_defer_enable(struct pa_defer_event *e, int b);
-
-static struct pa_defer_event* glib_defer_new(struct pa_mainloop_api*m, void (*callback) (struct pa_mainloop_api*m, struct pa_defer_event *e, void *userdata), void *userdata) {
-    struct pa_defer_event *e;
-    struct pa_glib_mainloop *g;
-
-    assert(m && m->userdata && callback);
-    g = m->userdata;
-    
-    e = pa_xmalloc(sizeof(struct pa_defer_event));
-    e->mainloop = g;
-    e->dead = 0;
-    e->callback = callback;
-    e->userdata = userdata;
-    e->destroy_callback = NULL;
-    e->source = NULL;
-
-    glib_defer_enable(e, 1);
-
-    e->next = g->defer_events;
-    if (e->next) e->next->prev = e;
-    g->defer_events = e;
-    e->prev = NULL;
-    return e;
-}
-
-static gboolean idle_cb(gpointer data) {
-    struct pa_defer_event* e = data;
-    assert(e && e->mainloop && e->source);
-
-    e->callback(&e->mainloop->api, e, e->userdata);
-    return TRUE;
-}
-
-static void glib_defer_enable(struct pa_defer_event *e, int b) {
-    assert(e && e->mainloop);
-
-    if (e->source && !b) {
-        g_source_destroy(e->source);
-        g_source_unref(e->source);
-        e->source = NULL;
-    } else if (!e->source && b) {
-        e->source = g_idle_source_new();
-        assert(e->source);
-        g_source_set_callback(e->source, idle_cb, e, NULL);
-        g_source_attach(e->source, e->mainloop->glib_main_context);
-        g_source_set_priority(e->source, G_PRIORITY_HIGH);
-    }
-}
-
-static void glib_defer_free(struct pa_defer_event *e) {
-    assert(e && e->mainloop && !e->dead);
-
-    if (e->source) {
-        g_source_destroy(e->source);
-        g_source_unref(e->source);
-        e->source = NULL;
-    }
-
-    if (e->prev)
-        e->prev->next = e->next;
-    else
-        e->mainloop->defer_events = e->next;
-
-    if (e->next)
-        e->next->prev = e->prev;
-
-    if ((e->next = e->mainloop->dead_defer_events))
-        e->next->prev = e;
-
-    e->mainloop->dead_defer_events = e;
-    e->prev = NULL;
-
-    e->dead = 1;
-    schedule_free_dead_events(e->mainloop);
-}
-
-static void glib_defer_set_destroy(struct pa_defer_event *e, void (*callback)(struct pa_mainloop_api *m, struct pa_defer_event *e, void *userdata)) {
-    assert(e);
-    e->destroy_callback = callback;
-}
-
-/* quit() */
-
-static void glib_quit(struct pa_mainloop_api*a, int retval) {
-    struct pa_glib_mainloop *g;
-    assert(a && a->userdata);
-    g = a->userdata;
-
-    /* NOOP */
-}
-
-static const struct pa_mainloop_api vtable = {
-    .userdata = NULL,
-
-    .io_new = glib_io_new,
-    .io_enable = glib_io_enable,
-    .io_free = glib_io_free,
-    .io_set_destroy= glib_io_set_destroy,
-
-    .time_new = glib_time_new,
-    .time_restart = glib_time_restart,
-    .time_free = glib_time_free,
-    .time_set_destroy = glib_time_set_destroy,
-    
-    .defer_new = glib_defer_new,
-    .defer_enable = glib_defer_enable,
-    .defer_free = glib_defer_free,
-    .defer_set_destroy = glib_defer_set_destroy,
-    
-    .quit = glib_quit,
-};
-
-struct pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
-    struct pa_glib_mainloop *g;
-    
-    g = pa_xmalloc(sizeof(struct pa_glib_mainloop));
-    if (c) {
-        g->glib_main_context = c;
-        g_main_context_ref(c);
-    } else
-        g->glib_main_context = g_main_context_default();
-    
-    g->api = vtable;
-    g->api.userdata = g;
-
-    g->io_events = g->dead_io_events = NULL;
-    g->time_events = g->dead_time_events = NULL;
-    g->defer_events = g->dead_defer_events = NULL;
-
-    g->cleanup_source = NULL;
-    return g;
-}
-
-static void free_io_events(struct pa_io_event *e) {
-    while (e) {
-        struct pa_io_event *r = e;
-        e = r->next;
-
-        if (r->source) {
-            g_source_destroy(r->source);
-            g_source_unref(r->source);
-        }
-
-        if (r->io_channel)
-            g_io_channel_unref(r->io_channel);
-        
-        if (r->destroy_callback)
-            r->destroy_callback(&r->mainloop->api, r, r->userdata);
-
-        pa_xfree(r);
-    }
-}
-
-static void free_time_events(struct pa_time_event *e) {
-    while (e) {
-        struct pa_time_event *r = e;
-        e = r->next;
-
-        if (r->source) {
-            g_source_destroy(r->source);
-            g_source_unref(r->source);
-        }
-        
-        if (r->destroy_callback)
-            r->destroy_callback(&r->mainloop->api, r, r->userdata);
-
-        pa_xfree(r);
-    }
-}
-
-static void free_defer_events(struct pa_defer_event *e) {
-    while (e) {
-        struct pa_defer_event *r = e;
-        e = r->next;
-
-        if (r->source) {
-            g_source_destroy(r->source);
-            g_source_unref(r->source);
-        }
-        
-        if (r->destroy_callback)
-            r->destroy_callback(&r->mainloop->api, r, r->userdata);
-
-        pa_xfree(r);
-    }
-}
-
-void pa_glib_mainloop_free(struct pa_glib_mainloop* g) {
-    assert(g);
-
-    free_io_events(g->io_events);
-    free_io_events(g->dead_io_events);
-    free_defer_events(g->defer_events);
-    free_defer_events(g->dead_defer_events);
-    free_time_events(g->time_events);
-    free_time_events(g->dead_time_events);
-
-    if (g->cleanup_source) {
-        g_source_destroy(g->cleanup_source);
-        g_source_unref(g->cleanup_source);
-    }
-
-    g_main_context_unref(g->glib_main_context);
-    pa_xfree(g);
-}
-
-struct pa_mainloop_api* pa_glib_mainloop_get_api(struct pa_glib_mainloop *g) {
-    assert(g);
-    return &g->api;
-}
-
-static gboolean free_dead_events(gpointer p) {
-    struct pa_glib_mainloop *g = p;
-    assert(g);
-
-    free_io_events(g->dead_io_events);
-    free_defer_events(g->dead_defer_events);
-    free_time_events(g->dead_time_events);
-
-    g->dead_io_events = NULL;
-    g->dead_defer_events = NULL;
-    g->dead_time_events = NULL;
-
-    g_source_destroy(g->cleanup_source);
-    g_source_unref(g->cleanup_source);
-    g->cleanup_source = NULL;
-
-    return FALSE;
-}
-
-static void schedule_free_dead_events(struct pa_glib_mainloop *g) {
-    assert(g && g->glib_main_context);
-
-    if (g->cleanup_source)
-        return;
-    
-    g->cleanup_source = g_idle_source_new();
-    assert(g->cleanup_source);
-    g_source_set_callback(g->cleanup_source, free_dead_events, g, NULL);
-    g_source_attach(g->cleanup_source, g->glib_main_context);
-}
diff --git a/polyp/glib-mainloop.h b/polyp/glib-mainloop.h
deleted file mode 100644 (file)
index d830561..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef fooglibmainloophfoo
-#define fooglibmainloophfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <glib.h>
-
-#include "mainloop-api.h"
-#include "cdecl.h"
-
-/** \file
- * GLIB main loop support */
-
-PA_C_DECL_BEGIN
-
-/** \struct pa_glib_mainloop
- * An opaque GLIB main loop object */
-struct pa_glib_mainloop;
-
-/** Create a new GLIB main loop object for the specified GLIB main loop context. If c is NULL the default context is used. */
-#if GLIB_MAJOR_VERSION >= 2
-struct pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c);
-#else
-struct pa_glib_mainloop *pa_glib_mainloop_new(void);
-#endif
-
-
-/** Free the GLIB main loop object */
-void pa_glib_mainloop_free(struct pa_glib_mainloop* g);
-
-/** Return the abstract main loop API vtable for the GLIB main loop object */
-struct pa_mainloop_api* pa_glib_mainloop_get_api(struct pa_glib_mainloop *g);
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/glib12-mainloop.c b/polyp/glib12-mainloop.c
deleted file mode 100644 (file)
index 5f03734..0000000
+++ /dev/null
@@ -1,499 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-
-#include "glib-mainloop.h"
-#include "idxset.h"
-#include "xmalloc.h"
-
-/* A mainloop implementation based on GLIB 1.2 */
-
-struct pa_io_event {
-    struct pa_glib_mainloop *mainloop;
-    int dead;
-    GIOChannel *io_channel;
-    guint source;
-    GIOCondition io_condition;
-    int fd;
-    void (*callback) (struct pa_mainloop_api*m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata);
-    void *userdata;
-    void (*destroy_callback) (struct pa_mainloop_api *m, struct pa_io_event*e, void *userdata);
-    struct pa_io_event *next, *prev;
-};
-
-struct pa_time_event {
-    struct pa_glib_mainloop *mainloop;
-    int dead;
-    guint source;
-    struct timeval timeval;
-    void (*callback) (struct pa_mainloop_api*m, struct pa_time_event *e, const struct timeval *tv, void *userdata);
-    void *userdata;
-    void (*destroy_callback) (struct pa_mainloop_api *m, struct pa_time_event*e, void *userdata);
-    struct pa_time_event *next, *prev;
-};
-
-struct pa_defer_event {
-    struct pa_glib_mainloop *mainloop;
-    int dead;
-    guint source;
-    void (*callback) (struct pa_mainloop_api*m, struct pa_defer_event *e, void *userdata);
-    void *userdata;
-    void (*destroy_callback) (struct pa_mainloop_api *m, struct pa_defer_event*e, void *userdata);
-    struct pa_defer_event *next, *prev;
-};
-
-struct pa_glib_mainloop {
-    struct pa_mainloop_api api;
-    guint cleanup_source;
-    struct pa_io_event *io_events, *dead_io_events;
-    struct pa_time_event *time_events, *dead_time_events;
-    struct pa_defer_event *defer_events, *dead_defer_events;
-};
-
-static void schedule_free_dead_events(struct pa_glib_mainloop *g);
-
-static void glib_io_enable(struct pa_io_event*e, enum pa_io_event_flags f);
-
-static struct pa_io_event* glib_io_new(struct pa_mainloop_api*m, int fd, enum pa_io_event_flags f, void (*callback) (struct pa_mainloop_api*m, struct pa_io_event*e, int fd, enum pa_io_event_flags f, void *userdata), void *userdata) {
-    struct pa_io_event *e;
-    struct pa_glib_mainloop *g;
-
-    assert(m && m->userdata && fd >= 0 && callback);
-    g = m->userdata;
-
-    e = pa_xmalloc(sizeof(struct pa_io_event));
-    e->mainloop = m->userdata;
-    e->dead = 0;
-    e->fd = fd;
-    e->callback = callback;
-    e->userdata = userdata;
-    e->destroy_callback = NULL;
-
-    e->io_channel = g_io_channel_unix_new(e->fd);
-    assert(e->io_channel);
-    e->source = (guint) -1;
-    e->io_condition = 0;
-
-    glib_io_enable(e, f);
-
-    e->next = g->io_events;
-    if (e->next) e->next->prev = e;
-    g->io_events = e;
-    e->prev = NULL;
-    
-    return e;
-}
-
-static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
-    struct pa_io_event *e = data;
-    enum pa_io_event_flags f;
-    assert(source && e && e->io_channel == source);
-
-    f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
-        (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
-        (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
-        (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
-    
-    e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
-    return TRUE;
-}
-
-static void glib_io_enable(struct pa_io_event*e, enum pa_io_event_flags f) {
-    GIOCondition c;
-    assert(e && !e->dead);
-
-    c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
-    
-    if (c == e->io_condition)
-        return;
-    
-    if (e->source != (guint) -1)
-        g_source_remove(e->source);
-
-    e->source = g_io_add_watch_full(e->io_channel, G_PRIORITY_DEFAULT, c | G_IO_ERR | G_IO_HUP, io_cb, e, NULL);
-    assert(e->source != (guint) -1);
-    e->io_condition = c;
-}
-
-static void glib_io_free(struct pa_io_event*e) {
-    assert(e && !e->dead);
-
-    if (e->source != (guint) -1) {
-        g_source_remove(e->source);
-        e->source = (guint) -1;
-    }
-    
-    if (e->prev)
-        e->prev->next = e->next;
-    else
-        e->mainloop->io_events = e->next;
-
-    if (e->next)
-        e->next->prev = e->prev;
-
-    if ((e->next = e->mainloop->dead_io_events))
-        e->next->prev = e;
-
-    e->mainloop->dead_io_events = e;
-    e->prev = NULL;
-
-    e->dead = 1;
-    schedule_free_dead_events(e->mainloop);
-}
-
-static void glib_io_set_destroy(struct pa_io_event*e, void (*callback)(struct pa_mainloop_api*m, struct pa_io_event *e, void *userdata)) {
-    assert(e);
-    e->destroy_callback = callback;
-}
-
-/* Time sources */
-
-static void glib_time_restart(struct pa_time_event*e, const struct timeval *tv);
-
-static struct pa_time_event* glib_time_new(struct pa_mainloop_api*m, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*m, struct pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
-    struct pa_glib_mainloop *g;
-    struct pa_time_event *e;
-    
-    assert(m && m->userdata && tv && callback);
-    g = m->userdata;
-
-    e = pa_xmalloc(sizeof(struct pa_time_event));
-    e->mainloop = g;
-    e->dead = 0;
-    e->callback = callback;
-    e->userdata = userdata;
-    e->destroy_callback = NULL;
-    e->source = (guint) -1;
-
-    glib_time_restart(e, tv);
-
-    e->next = g->time_events;
-    if (e->next) e->next->prev = e;
-    g->time_events = e;
-    e->prev = NULL;
-    
-    return e;
-}
-
-static guint msec_diff(const struct timeval *a, const struct timeval *b) {
-    guint r;
-    assert(a && b);
-    
-    if (a->tv_sec < b->tv_sec)
-        return 0;
-
-    if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
-        return 0;
-
-    r = (a->tv_sec-b->tv_sec)*1000;
-
-    if (a->tv_usec >= b->tv_usec)
-        r += (a->tv_usec - b->tv_usec) / 1000;
-    else
-        r -= (b->tv_usec - a->tv_usec) / 1000;
-    
-    return r;
-}
-
-static gboolean time_cb(gpointer data) {
-    struct pa_time_event* e = data;
-    assert(e && e->mainloop && e->source != (guint) -1);
-
-    g_source_remove(e->source);
-    e->source = (guint) -1;
-
-    e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
-    return FALSE;
-}
-
-static void glib_time_restart(struct pa_time_event*e, const struct timeval *tv) {
-    struct timeval now;
-    assert(e && e->mainloop && !e->dead);
-
-    gettimeofday(&now, NULL);
-    if (e->source != (guint) -1)
-        g_source_remove(e->source);
-
-    if (tv) {
-        e->timeval = *tv;
-        e->source = g_timeout_add_full(G_PRIORITY_DEFAULT, msec_diff(tv, &now), time_cb, e, NULL);
-        assert(e->source != (guint) -1);
-    } else
-        e->source = (guint) -1;
- }
-
-static void glib_time_free(struct pa_time_event *e) {
-    assert(e && e->mainloop && !e->dead);
-
-    if (e->source != (guint) -1) {
-        g_source_remove(e->source);
-        e->source = (guint) -1;
-    }
-
-    if (e->prev)
-        e->prev->next = e->next;
-    else
-        e->mainloop->time_events = e->next;
-
-    if (e->next)
-        e->next->prev = e->prev;
-
-    if ((e->next = e->mainloop->dead_time_events))
-        e->next->prev = e;
-
-    e->mainloop->dead_time_events = e;
-    e->prev = NULL;
-
-    e->dead = 1;
-    schedule_free_dead_events(e->mainloop);
-}
-
-static void glib_time_set_destroy(struct pa_time_event *e, void (*callback)(struct pa_mainloop_api*m, struct pa_time_event*e, void *userdata)) {
-    assert(e);
-    e->destroy_callback = callback;
-}
-
-/* Deferred sources */
-
-static void glib_defer_enable(struct pa_defer_event *e, int b);
-
-static struct pa_defer_event* glib_defer_new(struct pa_mainloop_api*m, void (*callback) (struct pa_mainloop_api*m, struct pa_defer_event *e, void *userdata), void *userdata) {
-    struct pa_defer_event *e;
-    struct pa_glib_mainloop *g;
-
-    assert(m && m->userdata && callback);
-    g = m->userdata;
-    
-    e = pa_xmalloc(sizeof(struct pa_defer_event));
-    e->mainloop = g;
-    e->dead = 0;
-    e->callback = callback;
-    e->userdata = userdata;
-    e->destroy_callback = NULL;
-    e->source = (guint) -1;
-
-    glib_defer_enable(e, 1);
-
-    e->next = g->defer_events;
-    if (e->next) e->next->prev = e;
-    g->defer_events = e;
-    e->prev = NULL;
-    return e;
-}
-
-static gboolean idle_cb(gpointer data) {
-    struct pa_defer_event* e = data;
-    assert(e && e->mainloop && e->source != (guint) -1);
-
-    e->callback(&e->mainloop->api, e, e->userdata);
-    return TRUE;
-}
-
-static void glib_defer_enable(struct pa_defer_event *e, int b) {
-    assert(e && e->mainloop);
-
-    if (e->source != (guint) -1 && !b) {
-        g_source_remove(e->source);
-        e->source = (guint) -1;
-    } else if (e->source == (guint) -1 && b) {
-        e->source = g_idle_add_full(G_PRIORITY_HIGH, idle_cb, e, NULL);
-        assert(e->source != (guint) -1);
-    }
-}
-
-static void glib_defer_free(struct pa_defer_event *e) {
-    assert(e && e->mainloop && !e->dead);
-
-    if (e->source != (guint) -1) {
-        g_source_remove(e->source);
-        e->source = (guint) -1;
-    }
-
-    if (e->prev)
-        e->prev->next = e->next;
-    else
-        e->mainloop->defer_events = e->next;
-
-    if (e->next)
-        e->next->prev = e->prev;
-
-    if ((e->next = e->mainloop->dead_defer_events))
-        e->next->prev = e;
-
-    e->mainloop->dead_defer_events = e;
-    e->prev = NULL;
-
-    e->dead = 1;
-    schedule_free_dead_events(e->mainloop);
-}
-
-static void glib_defer_set_destroy(struct pa_defer_event *e, void (*callback)(struct pa_mainloop_api *m, struct pa_defer_event *e, void *userdata)) {
-    assert(e);
-    e->destroy_callback = callback;
-}
-
-/* quit() */
-
-static void glib_quit(struct pa_mainloop_api*a, int retval) {
-    struct pa_glib_mainloop *g;
-    assert(a && a->userdata);
-    g = a->userdata;
-
-    /* NOOP */
-}
-
-static const struct pa_mainloop_api vtable = {
-    .userdata = NULL,
-
-    .io_new = glib_io_new,
-    .io_enable = glib_io_enable,
-    .io_free = glib_io_free,
-    .io_set_destroy= glib_io_set_destroy,
-
-    .time_new = glib_time_new,
-    .time_restart = glib_time_restart,
-    .time_free = glib_time_free,
-    .time_set_destroy = glib_time_set_destroy,
-    
-    .defer_new = glib_defer_new,
-    .defer_enable = glib_defer_enable,
-    .defer_free = glib_defer_free,
-    .defer_set_destroy = glib_defer_set_destroy,
-    
-    .quit = glib_quit,
-};
-
-struct pa_glib_mainloop *pa_glib_mainloop_new(void) {
-    struct pa_glib_mainloop *g;
-    
-    g = pa_xmalloc(sizeof(struct pa_glib_mainloop));
-    
-    g->api = vtable;
-    g->api.userdata = g;
-
-    g->io_events = g->dead_io_events = NULL;
-    g->time_events = g->dead_time_events = NULL;
-    g->defer_events = g->dead_defer_events = NULL;
-
-    g->cleanup_source = (guint) -1;
-    return g;
-}
-
-static void free_io_events(struct pa_io_event *e) {
-    while (e) {
-        struct pa_io_event *r = e;
-        e = r->next;
-
-        if (r->source != (guint) -1)
-            g_source_remove(r->source);
-
-        if (r->io_channel)
-            g_io_channel_unref(r->io_channel);
-        
-        if (r->destroy_callback)
-            r->destroy_callback(&r->mainloop->api, r, r->userdata);
-
-        pa_xfree(r);
-    }
-}
-
-static void free_time_events(struct pa_time_event *e) {
-    while (e) {
-        struct pa_time_event *r = e;
-        e = r->next;
-
-        if (r->source != (guint) -1)
-            g_source_remove(r->source);
-        
-        if (r->destroy_callback)
-            r->destroy_callback(&r->mainloop->api, r, r->userdata);
-
-        pa_xfree(r);
-    }
-}
-
-static void free_defer_events(struct pa_defer_event *e) {
-    while (e) {
-        struct pa_defer_event *r = e;
-        e = r->next;
-
-        if (r->source != (guint) -1)
-            g_source_remove(r->source);
-        
-        if (r->destroy_callback)
-            r->destroy_callback(&r->mainloop->api, r, r->userdata);
-
-        pa_xfree(r);
-    }
-}
-
-void pa_glib_mainloop_free(struct pa_glib_mainloop* g) {
-    assert(g);
-
-    free_io_events(g->io_events);
-    free_io_events(g->dead_io_events);
-    free_defer_events(g->defer_events);
-    free_defer_events(g->dead_defer_events);
-    free_time_events(g->time_events);
-    free_time_events(g->dead_time_events);
-
-    if (g->cleanup_source != (guint) -1)
-        g_source_remove(g->cleanup_source);
-
-    pa_xfree(g);
-}
-
-struct pa_mainloop_api* pa_glib_mainloop_get_api(struct pa_glib_mainloop *g) {
-    assert(g);
-    return &g->api;
-}
-
-static gboolean free_dead_events(gpointer p) {
-    struct pa_glib_mainloop *g = p;
-    assert(g);
-
-    free_io_events(g->dead_io_events);
-    free_defer_events(g->dead_defer_events);
-    free_time_events(g->dead_time_events);
-
-    g->dead_io_events = NULL;
-    g->dead_defer_events = NULL;
-    g->dead_time_events = NULL;
-
-    g_source_remove(g->cleanup_source);
-    g->cleanup_source = (guint) -1;
-
-    return FALSE;
-}
-
-static void schedule_free_dead_events(struct pa_glib_mainloop *g) {
-    assert(g);
-
-    if (g->cleanup_source != (guint) -1)
-        return;
-    
-    g->cleanup_source = g_idle_add_full(G_PRIORITY_HIGH, free_dead_events, g, NULL);
-}
diff --git a/polyp/howl-wrap.c b/polyp/howl-wrap.c
deleted file mode 100644 (file)
index af05093..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <assert.h>
-
-#include "howl-wrap.h"
-#include "log.h"
-#include "xmalloc.h"
-#include "props.h"
-
-#define HOWL_PROPERTY "howl"
-
-struct pa_howl_wrapper {
-    struct pa_core *core;
-    int ref;
-
-    struct pa_io_event *io_event;
-    sw_discovery discovery;
-
-};
-
-static void howl_io_event(struct pa_mainloop_api*m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    struct pa_howl_wrapper *w = userdata;
-    assert(m && e && fd >= 0 && w && w->ref >= 1);
-
-    if (f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR))
-        goto fail;
-
-    if (sw_discovery_read_socket(w->discovery) != SW_OKAY)
-        goto fail;
-
-    return;
-
-fail:
-    pa_log(__FILE__": howl connection died.\n");
-    w->core->mainloop->io_free(w->io_event);
-    w->io_event = NULL;
-}
-
-static struct pa_howl_wrapper* howl_wrapper_new(struct pa_core *c) {
-    struct pa_howl_wrapper *h;
-    sw_discovery session;
-    assert(c);
-
-    if (sw_discovery_init(&session) != SW_OKAY) {
-        pa_log("sw_discovery_init() failed.\n");
-        return NULL;
-    }
-
-    h = pa_xmalloc(sizeof(struct pa_howl_wrapper));
-    h->core = c;
-    h->ref = 1;
-    h->discovery = session;
-
-    h->io_event = c->mainloop->io_new(c->mainloop, sw_discovery_socket(session), PA_IO_EVENT_INPUT, howl_io_event, h);
-
-    return h;
-}
-
-static void howl_wrapper_free(struct pa_howl_wrapper *h) {
-    assert(h);
-
-    sw_discovery_fina(h->discovery);
-
-    if (h->io_event)
-        h->core->mainloop->io_free(h->io_event);
-
-    pa_xfree(h);
-}
-
-struct pa_howl_wrapper* pa_howl_wrapper_get(struct pa_core *c) {
-    struct pa_howl_wrapper *h;
-    assert(c);
-    
-    if ((h = pa_property_get(c, HOWL_PROPERTY)))
-        return pa_howl_wrapper_ref(h);
-
-    return howl_wrapper_new(c);
-}
-
-struct pa_howl_wrapper* pa_howl_wrapper_ref(struct pa_howl_wrapper *h) {
-    assert(h && h->ref >= 1);
-    h->ref++;
-    return h;
-}
-
-void pa_howl_wrapper_unref(struct pa_howl_wrapper *h) {
-    assert(h && h->ref >= 1);
-    if (!(--h->ref))
-        howl_wrapper_free(h);
-}
-
-sw_discovery pa_howl_wrapper_get_discovery(struct pa_howl_wrapper *h) {
-    assert(h && h->ref >= 1);
-
-    return h->discovery;
-}
-
diff --git a/polyp/howl-wrap.h b/polyp/howl-wrap.h
deleted file mode 100644 (file)
index feb5455..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef foohowlwrapperhfoo
-#define foohowlwrapperhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <howl.h>
-
-#include "core.h"
-
-struct pa_howl_wrapper;
-
-struct pa_howl_wrapper* pa_howl_wrapper_get(struct pa_core *c);
-struct pa_howl_wrapper* pa_howl_wrapper_ref(struct pa_howl_wrapper *h);
-void pa_howl_wrapper_unref(struct pa_howl_wrapper *h);
-
-sw_discovery pa_howl_wrapper_get_discovery(struct pa_howl_wrapper *h);
-
-#endif
diff --git a/polyp/iochannel.c b/polyp/iochannel.c
deleted file mode 100644 (file)
index 0e7e8db..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <assert.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "iochannel.h"
-#include "util.h"
-#include "socket-util.h"
-#include "xmalloc.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;
-
-    struct pa_io_event* input_event, *output_event;
-};
-
-static void enable_mainloop_sources(struct pa_iochannel *io) {
-    assert(io);
-
-    if (io->input_event == io->output_event && io->input_event) {
-        enum pa_io_event_flags f = PA_IO_EVENT_NULL;
-        assert(io->input_event);
-        
-        if (!io->readable)
-            f |= PA_IO_EVENT_INPUT;
-        if (!io->writable)
-            f |= PA_IO_EVENT_OUTPUT;
-
-        io->mainloop->io_enable(io->input_event, f);
-    } else {
-        if (io->input_event)
-            io->mainloop->io_enable(io->input_event, io->readable ? PA_IO_EVENT_NULL : PA_IO_EVENT_INPUT);
-        if (io->output_event)
-            io->mainloop->io_enable(io->output_event, io->writable ? PA_IO_EVENT_NULL : PA_IO_EVENT_OUTPUT);
-    }
-}
-
-static void callback(struct pa_mainloop_api* m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    struct pa_iochannel *io = userdata;
-    int changed = 0;
-    assert(m && e && fd >= 0 && userdata);
-
-    if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
-        io->hungup = 1;
-        changed = 1;
-
-        if (e == io->input_event) {
-            io->mainloop->io_free(io->input_event);
-            io->input_event = NULL;
-
-            if (io->output_event == e)
-                io->output_event = NULL;
-        }
-
-        if (e == io->output_event) {
-            io->mainloop->io_free(io->output_event);
-            io->output_event = NULL;
-        }
-    } else {
-
-        if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
-            io->readable = 1;
-            changed = 1;
-            assert(e == io->input_event);
-        }
-        
-        if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
-            io->writable = 1;
-            changed = 1;
-            assert(e == io->output_event);
-        }
-    }
-
-    if (changed) {
-        enable_mainloop_sources(io);
-        
-        if (io->callback)
-            io->callback(io, io->userdata);
-    }
-}
-
-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 = pa_xmalloc(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;
-
-    io->input_event = io->output_event = NULL;
-
-    if (ifd == ofd) {
-        assert(ifd >= 0);
-        pa_make_nonblock_fd(io->ifd);
-        io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io);
-    } else {
-
-        if (ifd >= 0) {
-            pa_make_nonblock_fd(io->ifd);
-            io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io);
-        }
-
-        if (ofd >= 0) {
-            pa_make_nonblock_fd(io->ofd);
-            io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io);
-        }
-    }
-
-    return io;
-}
-
-void pa_iochannel_free(struct pa_iochannel*io) {
-    assert(io);
-
-    if (io->input_event)
-        io->mainloop->io_free(io->input_event);
-    if (io->output_event && (io->output_event != io->input_event))
-        io->mainloop->io_free(io->output_event);
-
-    if (!io->no_close) {
-        if (io->ifd >= 0)
-            close(io->ifd);
-        if (io->ofd >= 0 && io->ofd != io->ifd)
-            close(io->ofd);
-    }
-    
-    pa_xfree(io);
-}
-
-int pa_iochannel_is_readable(struct pa_iochannel*io) {
-    assert(io);
-    return io->readable || io->hungup;
-}
-
-int pa_iochannel_is_writable(struct pa_iochannel*io) {
-    assert(io);
-    return io->writable && !io->hungup;
-}
-
-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);
-}
-
-
-struct pa_mainloop_api* pa_iochannel_get_mainloop_api(struct pa_iochannel *io) {
-    assert(io);
-    return io->mainloop;
-}
diff --git a/polyp/iochannel.h b/polyp/iochannel.h
deleted file mode 100644 (file)
index 2a1ba37..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef fooiochannelhfoo
-#define fooiochannelhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <sys/types.h>
-#include "mainloop-api.h"
-
-/* A wrapper around UNIX file descriptors for attaching them to the a
-   main event loop. Everytime new data may be read or be written to
-   the channel a callback function is called. It is safe to destroy
-   the calling iochannel object from the callback */
-
-/* When pa_iochannel_is_readable() returns non-zero, the user has to
- * call this function in a loop until it is no longer set or EOF
- * reached. Otherwise strange things may happen when an EOF is
- * reached. */
-
-struct pa_iochannel;
-
-/* Create a new IO channel for the specified file descriptors for
-input resp. output. It is safe to pass the same file descriptor for
-both parameters (in case of full-duplex channels). For a simplex
-channel specify -1 for the other direction. */
-
-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);
-
-/* Don't close the file descirptors when the io channel is freed. By
- * default the file descriptors are closed. */
-void pa_iochannel_set_noclose(struct pa_iochannel*io, int b);
-
-/* Set the callback function that is called whenever data becomes available for read or write */
-void pa_iochannel_set_callback(struct pa_iochannel*io, void (*callback)(struct pa_iochannel*io, void *userdata), void *userdata);
-
-/* In case the file descriptor is a socket, return a pretty-printed string in *s which describes the peer connected */
-void pa_iochannel_socket_peer_to_string(struct pa_iochannel*io, char*s, size_t l);
-
-/* Use setsockopt() to tune the recieve and send buffers of TCP sockets */
-int pa_iochannel_socket_set_rcvbuf(struct pa_iochannel*io, size_t l);
-int pa_iochannel_socket_set_sndbuf(struct pa_iochannel*io, size_t l);
-
-struct pa_mainloop_api* pa_iochannel_get_mainloop_api(struct pa_iochannel *io);
-
-#endif
diff --git a/polyp/llist.h b/polyp/llist.h
deleted file mode 100644 (file)
index eb8cd01..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef foollistfoo
-#define foollistfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-/* Some macros for maintaining doubly linked lists */
-
-/* The head of the linked list. Use this in the structure that shall
- * contain the head of the linked list */
-#define PA_LLIST_HEAD(t,name) t *name
-
-/* The pointers in the linked list's items. Use this in the item structure */
-#define PA_LLIST_FIELDS(t) t *next, *prev
-
-/* Initialize the list's head */
-#define PA_LLIST_HEAD_INIT(t,item) do { (item) = NULL; } while(0)
-
-/* Initialize a list item */
-#define PA_LLIST_INIT(t,item) do { \
-                               t *_item = (item); \
-                               assert(_item); \
-                               _item->prev = _item->next = NULL; \
-                               } while(0)
-
-/* Prepend an item to the list */
-#define PA_LLIST_PREPEND(t,head,item) do { \
-                                        t **_head = &(head), *_item = (item); \
-                                        assert(_item); \
-                                        if ((_item->next = *_head)) \
-                                           _item->next->prev = _item; \
-                                        _item->prev = NULL; \
-                                        *_head = _item; \
-                                        } while (0)
-
-/* Remove an item from the list */
-#define PA_LLIST_REMOVE(t,head,item) do { \
-                                    t **_head = &(head), *_item = (item); \
-                                    assert(_item); \
-                                    if (_item->next) \
-                                       _item->next->prev = _item->prev; \
-                                    if (_item->prev) \
-                                       _item->prev->next = _item->next; \
-                                    else {\
-                                       assert(*_head == _item); \
-                                       *_head = _item->next; \
-                                    } \
-                                    _item->next = _item->prev = NULL; \
-                                    } while(0)
-
-#endif
diff --git a/polyp/log.c b/polyp/log.c
deleted file mode 100644 (file)
index 78736a4..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdarg.h>
-#include <syslog.h>
-#include <stdio.h>
-
-#include "log.h"
-#include "xmalloc.h"
-#include "util.h"
-
-#define ENV_LOGLEVEL "POLYP_LOG"
-
-static char *log_ident = NULL;
-static enum pa_log_target log_target = PA_LOG_STDERR;
-static void (*user_log_func)(enum pa_log_level l, const char *s) = NULL;
-static enum pa_log_level maximal_level = PA_LOG_NOTICE;
-
-static const int level_to_syslog[] = {
-    [PA_LOG_ERROR] = LOG_ERR,
-    [PA_LOG_WARN] = LOG_WARNING,
-    [PA_LOG_NOTICE] = LOG_NOTICE,
-    [PA_LOG_INFO] = LOG_INFO,
-    [PA_LOG_DEBUG] = LOG_DEBUG
-};
-
-void pa_log_set_ident(const char *p) {
-    if (log_ident)
-        pa_xfree(log_ident);
-
-    log_ident = pa_xstrdup(p);
-}
-
-void pa_log_set_maximal_level(enum pa_log_level l) {
-    assert(l < PA_LOG_LEVEL_MAX);
-    maximal_level = l;
-}
-
-void pa_log_set_target(enum pa_log_target t, void (*func)(enum pa_log_level l, const char*s)) {
-    assert(t == PA_LOG_USER || !func);
-    log_target = t;
-    user_log_func = func;
-}
-
-void pa_log_levelv(enum pa_log_level level, const char *format, va_list ap) {
-    const char *e;
-    assert(level < PA_LOG_LEVEL_MAX);
-
-    if ((e = getenv(ENV_LOGLEVEL)))
-        maximal_level = atoi(e);
-    
-    if (level > maximal_level)
-        return;
-
-    switch (log_target) {
-        case PA_LOG_STDERR:
-            vfprintf(stderr, format, ap);
-            break;
-            
-        case PA_LOG_SYSLOG:
-            openlog(log_ident ? log_ident : "???", LOG_PID, LOG_USER);
-            vsyslog(level_to_syslog[level], format, ap);
-            closelog();
-            break;
-            
-        case PA_LOG_USER: {
-            char *t = pa_vsprintf_malloc(format, ap);
-            assert(user_log_func);
-            user_log_func(level, t);
-           pa_xfree(t);
-        }
-            
-        case PA_LOG_NULL:
-            break;
-    }
-
-}
-
-void pa_log_level(enum pa_log_level level, const char *format, ...) {
-    va_list ap;
-    va_start(ap, format);
-    pa_log_levelv(level, format, ap);
-    va_end(ap);
-}
-
-void pa_log_debug(const char *format, ...) {
-    va_list ap;
-    va_start(ap, format);
-    pa_log_levelv(PA_LOG_DEBUG, format, ap);
-    va_end(ap);
-}
-
-void pa_log_info(const char *format, ...) {
-    va_list ap;
-    va_start(ap, format);
-    pa_log_levelv(PA_LOG_INFO, format, ap);
-    va_end(ap);
-}
-
-void pa_log_notice(const char *format, ...) {
-    va_list ap;
-    va_start(ap, format);
-    pa_log_levelv(PA_LOG_INFO, format, ap);
-    va_end(ap);
-}
-
-void pa_log_warn(const char *format, ...) {
-    va_list ap;
-    va_start(ap, format);
-    pa_log_levelv(PA_LOG_WARN, format, ap);
-    va_end(ap);
-}
-
-void pa_log_error(const char *format, ...) {
-    va_list ap;
-    va_start(ap, format);
-    pa_log_levelv(PA_LOG_ERROR, format, ap);
-    va_end(ap);
-}
diff --git a/polyp/log.h b/polyp/log.h
deleted file mode 100644 (file)
index fe2dad5..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef foologhfoo
-#define foologhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <stdarg.h>
-#include "gcc-printf.h"
-
-/* A simple logging subsystem */
-
-/* Where to log to */
-enum pa_log_target {
-    PA_LOG_STDERR,  /* default */
-    PA_LOG_SYSLOG,
-    PA_LOG_USER,    /* to user specified function */
-    PA_LOG_NULL     /* to /dev/null */
-};
-
-enum pa_log_level {
-    PA_LOG_ERROR  = 0,    /* Error messages */
-    PA_LOG_WARN   = 1,    /* Warning messages */
-    PA_LOG_NOTICE = 2,    /* Notice messages */
-    PA_LOG_INFO   = 3,    /* Info messages */
-    PA_LOG_DEBUG  = 4,    /* debug message */
-    PA_LOG_LEVEL_MAX
-};
-
-/* Set an identifcation for the current daemon. Used when logging to syslog. */
-void pa_log_set_ident(const char *p);
-
-/* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */
-void pa_log_set_target(enum pa_log_target t, void (*func)(enum pa_log_level, const char*s));
-
-/* Minimal log level */
-void pa_log_set_maximal_level(enum pa_log_level l);
-
-/* Do a log line */
-void pa_log_debug(const char *format, ...)  PA_GCC_PRINTF_ATTR(1,2);
-void pa_log_info(const char *format, ...)  PA_GCC_PRINTF_ATTR(1,2);
-void pa_log_notice(const char *format, ...)  PA_GCC_PRINTF_ATTR(1,2);
-void pa_log_warn(const char *format, ...)  PA_GCC_PRINTF_ATTR(1,2);
-void pa_log_error(const char *format, ...)  PA_GCC_PRINTF_ATTR(1,2);
-
-void pa_log_level(enum pa_log_level level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
-
-void pa_log_levelv(enum pa_log_level level, const char *format, va_list ap);
-
-#define pa_log pa_log_error
-
-#endif
diff --git a/polyp/main.c b/polyp/main.c
deleted file mode 100644 (file)
index d6c25b4..0000000
+++ /dev/null
@@ -1,363 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <signal.h>
-#include <stddef.h>
-#include <assert.h>
-#include <ltdl.h>
-#include <memblock.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#ifdef HAVE_LIBWRAP
-#include <syslog.h>
-#include <tcpd.h>
-#endif
-
-#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"
-#include "xmalloc.h"
-#include "cpulimit.h"
-#include "log.h"
-#include "daemon-conf.h"
-#include "dumpmodules.h"
-#include "caps.h"
-#include "cli-text.h"
-#include "pid.h"
-
-#ifdef HAVE_LIBWRAP
-/* Only one instance of these variables */
-int allow_severity = LOG_INFO;
-int deny_severity = LOG_WARNING;
-#endif
-
-static void signal_callback(struct pa_mainloop_api*m, struct pa_signal_event *e, int sig, void *userdata) {
-    pa_log_info(__FILE__": Got signal %s.\n", pa_strsignal(sig));
-
-    switch (sig) {
-        case SIGUSR1:
-            pa_module_load(userdata, "module-cli", NULL);
-            break;
-            
-        case SIGUSR2:
-            pa_module_load(userdata, "module-cli-protocol-unix", NULL);
-            break;
-
-        case SIGHUP: {
-            char *c = pa_full_status_string(userdata);
-            pa_log_notice(c);
-            pa_xfree(c);
-            return;
-        }
-
-        case SIGINT:
-        case SIGTERM:
-        default:
-            pa_log_info(__FILE__": Exiting.\n");
-            m->quit(m, 1);
-            break;
-    }
-}
-
-static void close_pipe(int p[2]) {
-    if (p[0] != -1)
-        close(p[0]);
-    if (p[1] != -1)
-        close(p[1]);
-    p[0] = p[1] = -1;
-}
-
-int main(int argc, char *argv[]) {
-    struct pa_core *c;
-    struct pa_strbuf *buf = NULL;
-    struct pa_daemon_conf *conf;
-    struct pa_mainloop *mainloop;
-
-    char *s;
-    int r, retval = 1, d = 0;
-    int daemon_pipe[2] = { -1, -1 };
-    gid_t gid = (gid_t) -1;
-    int suid_root;
-    int valid_pid_file = 0;
-
-    pa_limit_caps();
-
-    suid_root = getuid() != 0 && geteuid() == 0;
-    
-    if (suid_root && (pa_uid_in_group("realtime", &gid) <= 0 || gid >= 1000)) {
-        pa_log_warn(__FILE__": WARNING: called SUID root, but not in group 'realtime'.\n");
-        pa_drop_root();
-    }
-    
-    LTDL_SET_PRELOADED_SYMBOLS();
-    
-    r = lt_dlinit();
-    assert(r == 0);
-
-    pa_log_set_ident("polypaudio");
-    
-    conf = pa_daemon_conf_new();
-    
-    if (pa_daemon_conf_load(conf, NULL) < 0)
-        goto finish;
-
-    if (pa_daemon_conf_env(conf) < 0)
-        goto finish;
-
-    if (pa_cmdline_parse(conf, argc, argv, &d) < 0) {
-        pa_log(__FILE__": failed to parse command line.\n");
-        goto finish;
-    }
-
-    pa_log_set_maximal_level(conf->log_level);
-    pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
-
-    if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
-        pa_raise_priority();
-
-    pa_drop_caps();
-
-    if (suid_root)
-        pa_drop_root();
-    
-    if (conf->dl_search_path)
-        lt_dlsetsearchpath(conf->dl_search_path);
-
-    switch (conf->cmd) {
-        case PA_CMD_DUMP_MODULES:
-            pa_dump_modules(conf, argc-d, argv+d);
-            retval = 0;
-            goto finish;
-
-        case PA_CMD_DUMP_CONF: {
-            char *s = pa_daemon_conf_dump(conf);
-            fputs(s, stdout);
-            pa_xfree(s);
-            retval = 0;
-            goto finish;
-        }
-
-        case PA_CMD_HELP :
-            pa_cmdline_help(argv[0]);
-            retval = 0;
-            goto finish;
-
-        case PA_CMD_VERSION :
-            printf(PACKAGE_NAME" "PACKAGE_VERSION"\n");
-            retval = 0;
-            goto finish;
-
-        case PA_CMD_CHECK: {
-            pid_t pid;
-
-            if (pa_pid_file_check_running(&pid) < 0) {
-                pa_log_info(__FILE__": daemon not running\n");
-            } else {
-                pa_log_info(__FILE__": daemon running as PID %u\n", pid);
-                retval = 0;
-            }
-
-            goto finish;
-
-        }
-        case PA_CMD_KILL:
-
-            if (pa_pid_file_kill(SIGINT, NULL) < 0)
-                pa_log(__FILE__": failed to kill daemon.\n");
-            else
-                retval = 0;
-            
-            goto finish;
-            
-        default:
-            assert(conf->cmd == PA_CMD_DAEMON);
-    }
-
-    if (conf->daemonize) {
-        pid_t child;
-        int tty_fd;
-
-        if (pa_stdio_acquire() < 0) {
-            pa_log(__FILE__": failed to acquire stdio.\n");
-            goto finish;
-        }
-
-        if (pipe(daemon_pipe) < 0) {
-            pa_log(__FILE__": failed to create pipe.\n");
-            goto finish;
-        }
-        
-        if ((child = fork()) < 0) {
-            pa_log(__FILE__": fork() failed: %s\n", strerror(errno));
-            goto finish;
-        }
-
-        if (child != 0) {
-            /* Father */
-
-            close(daemon_pipe[1]);
-            daemon_pipe[1] = -1;
-
-            if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval)) != sizeof(retval)) {
-                pa_log(__FILE__": read() failed: %s\n", strerror(errno));
-                retval = 1;
-            }
-
-            if (retval)
-                pa_log(__FILE__": daemon startup failed.\n");
-            else
-                pa_log_info(__FILE__": daemon startup successful.\n");
-            
-            goto finish;
-        }
-
-        close(daemon_pipe[0]);
-        daemon_pipe[0] = -1;
-
-        if (conf->auto_log_target)
-            pa_log_set_target(PA_LOG_SYSLOG, NULL);
-
-        setsid();
-        setpgid(0,0);
-        
-        close(0);
-        close(1);
-        close(2);
-
-        open("/dev/null", O_RDONLY);
-        open("/dev/null", O_WRONLY);
-        open("/dev/null", O_WRONLY);
-        
-        signal(SIGTTOU, SIG_IGN);
-        signal(SIGTTIN, SIG_IGN);
-        signal(SIGTSTP, SIG_IGN);
-        
-        if ((tty_fd = open("/dev/tty", O_RDWR)) >= 0) {
-            ioctl(tty_fd, TIOCNOTTY, (char*) 0);
-            close(tty_fd);
-        }
-    }
-
-    chdir("/");
-    
-    if (conf->use_pid_file) {
-        if (pa_pid_file_create() < 0) {
-            if (conf->daemonize)
-                pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
-            goto finish;
-        }
-
-        valid_pid_file = 1;
-    }
-
-    mainloop = pa_mainloop_new();
-    assert(mainloop);
-
-    r = pa_signal_init(pa_mainloop_get_api(mainloop));
-    assert(r == 0);
-    pa_signal_new(SIGINT, signal_callback, c);
-    pa_signal_new(SIGTERM, signal_callback, c);
-    signal(SIGPIPE, SIG_IGN);
-
-    c = pa_core_new(pa_mainloop_get_api(mainloop));
-    assert(c);
-    
-    pa_signal_new(SIGUSR1, signal_callback, c);
-    pa_signal_new(SIGUSR2, signal_callback, c);
-    pa_signal_new(SIGHUP, signal_callback, c);
-
-    r = pa_cpu_limit_init(pa_mainloop_get_api(mainloop));
-    assert(r == 0);
-    
-    buf = pa_strbuf_new();
-    assert(buf);
-    if (conf->default_script_file)
-        r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail);
-
-    if (r >= 0)
-        r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
-    pa_log(s = pa_strbuf_tostring_free(buf));
-    pa_xfree(s);
-    
-    if (r < 0 && conf->fail) {
-        pa_log(__FILE__": failed to initialize daemon.\n");
-        if (conf->daemonize)
-            pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
-    } else if (!c->modules || pa_idxset_ncontents(c->modules) == 0) {
-        pa_log(__FILE__": daemon startup without any loaded modules, refusing to work.\n");
-        if (conf->daemonize)
-            pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
-    } else {
-
-        retval = 0;
-        if (conf->daemonize)
-            pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
-
-        c->disallow_module_loading = conf->disallow_module_loading;
-        c->exit_idle_time = conf->exit_idle_time;
-        c->module_idle_time = conf->module_idle_time;
-        c->scache_idle_time = conf->scache_idle_time;
-        c->resample_method = conf->resample_method;
-        
-        pa_log_info(__FILE__": Daemon startup complete.\n");
-        if (pa_mainloop_run(mainloop, &retval) < 0)
-            retval = 1;
-        pa_log_info(__FILE__": Daemon shutdown initiated.\n");
-    }
-        
-    pa_core_free(c);
-
-    pa_cpu_limit_done();
-    pa_signal_done();
-    pa_mainloop_free(mainloop);
-    
-    pa_log_info(__FILE__": Daemon terminated.\n");
-    
-finish:
-
-    if (conf)
-        pa_daemon_conf_free(conf);
-
-    if (valid_pid_file)
-        pa_pid_file_remove();
-    
-    close_pipe(daemon_pipe);
-
-    lt_dlexit();
-    
-    return retval;
-}
diff --git a/polyp/mainloop-api.c b/polyp/mainloop-api.c
deleted file mode 100644 (file)
index 7b80e4f..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "mainloop-api.h"
-#include "xmalloc.h"
-
-struct once_info {
-    void (*callback)(struct pa_mainloop_api*m, void *userdata);
-    void *userdata;
-};
-
-static void once_callback(struct pa_mainloop_api *m, struct pa_defer_event *e, void *userdata) {
-    struct once_info *i = userdata;
-    assert(m && i && i->callback);
-
-    i->callback(m, i->userdata);
-
-    assert(m->defer_free);
-    m->defer_free(e);
-}
-
-static void free_callback(struct pa_mainloop_api *m, struct pa_defer_event *e, void *userdata) {
-    struct once_info *i = userdata;
-    assert(m && i);
-    pa_xfree(i);
-}
-
-void pa_mainloop_api_once(struct pa_mainloop_api* m, void (*callback)(struct pa_mainloop_api *m, void *userdata), void *userdata) {
-    struct once_info *i;
-    struct pa_defer_event *e;
-    assert(m && callback);
-
-    i = pa_xmalloc(sizeof(struct once_info));
-    i->callback = callback;
-    i->userdata = userdata;
-
-    assert(m->defer_new);
-    e = m->defer_new(m, once_callback, i);
-    assert(e);
-    m->defer_set_destroy(e, free_callback);
-}
-
diff --git a/polyp/mainloop-api.h b/polyp/mainloop-api.h
deleted file mode 100644 (file)
index 3fb221f..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-#ifndef foomainloopapihfoo
-#define foomainloopapihfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <sys/time.h>
-#include <time.h>
-
-#include <polyp/cdecl.h>
-
-/** \file
- * 
- * Main loop abstraction layer. Both the polypaudio core and the
- * polypaudio client library use a main loop abstraction layer. Due to
- * this it is possible to embed polypaudio into other
- * applications easily. Two main loop implemenations are
- * currently available:
- * \li A minimal implementation based on the C library's poll() function (See \ref mainloop.h)
- * \li A wrapper around the GLIB main loop. Use this to embed polypaudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h)
- *
- * The structure pa_mainloop_api is used as vtable for the main loop abstraction.
- *
- * This mainloop abstraction layer has no direct support for UNIX signals. Generic, mainloop implementation agnostic support is available throught \ref mainloop-signal.h.
- * */
-
-PA_C_DECL_BEGIN
-
-/** A bitmask for IO events */
-enum pa_io_event_flags {
-    PA_IO_EVENT_NULL = 0,     /**< No event */
-    PA_IO_EVENT_INPUT = 1,    /**< Input event */
-    PA_IO_EVENT_OUTPUT = 2,   /**< Output event */
-    PA_IO_EVENT_HANGUP = 4,   /**< Hangup event */
-    PA_IO_EVENT_ERROR = 8     /**< Error event */
-};
-
-/** \struct pa_io_event
- * An opaque IO event source object */
-struct pa_io_event;
-
-/** \struct pa_defer_event
- * An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */
-struct pa_defer_event;
-
-/** \struct pa_time_event
- * An opaque timer event source object */
-struct pa_time_event;
-
-/** An abstract mainloop API vtable */
-struct pa_mainloop_api {
-    /** A pointer to some private, arbitrary data of the main loop implementation */
-    void *userdata;
-
-    /** Create a new IO event source object */
-    struct pa_io_event* (*io_new)(struct pa_mainloop_api*a, int fd, enum pa_io_event_flags events, void (*callback) (struct pa_mainloop_api*a, struct pa_io_event* e, int fd, enum pa_io_event_flags events, void *userdata), void *userdata);
-
-    /** Enable or disable IO events on this object */
-    void (*io_enable)(struct pa_io_event* e, enum pa_io_event_flags events);
-
-    /** Free a IO event source object */
-    void (*io_free)(struct pa_io_event* e);
-
-    /** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */
-    void (*io_set_destroy)(struct pa_io_event *e, void (*callback) (struct pa_mainloop_api*a, struct pa_io_event *e, void *userdata));
-
-    /** Create a new timer event source object for the specified Unix time */
-    struct pa_time_event* (*time_new)(struct pa_mainloop_api*a, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*a, struct pa_time_event* e, const struct timeval *tv, void *userdata), void *userdata);
-
-    /** Restart a running or expired timer event source with a new Unix time */
-    void (*time_restart)(struct pa_time_event* e, const struct timeval *tv);
-
-    /** Free a deferred timer event source object */
-    void (*time_free)(struct pa_time_event* e);
-
-    /** Set a function that is called when the timer event source is destroyed. Use this to free the userdata argument if required */
-    void (*time_set_destroy)(struct pa_time_event *e, void (*callback) (struct pa_mainloop_api*a, struct pa_time_event *e, void *userdata));
-
-    /** Create a new deferred event source object */
-    struct pa_defer_event* (*defer_new)(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, struct pa_defer_event* e, void *userdata), void *userdata);
-
-    /** Enable or disable a deferred event source temporarily */
-    void (*defer_enable)(struct pa_defer_event* e, int b);
-
-    /** Free a deferred event source object */
-    void (*defer_free)(struct pa_defer_event* e);
-
-    /** Set a function that is called when the deferred event source is destroyed. Use this to free the userdata argument if required */
-    void (*defer_set_destroy)(struct pa_defer_event *e, void (*callback) (struct pa_mainloop_api*a, struct pa_defer_event *e, void *userdata));
-
-    /** Exit the main loop and return the specfied retval*/
-    void (*quit)(struct pa_mainloop_api*a, int retval);
-};
-
-/** Run the specified callback function once from the main loop using an anonymous defer event. */
-void pa_mainloop_api_once(struct pa_mainloop_api*m, void (*callback)(struct pa_mainloop_api*m, void *userdata), void *userdata);
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/mainloop-signal.c b/polyp/mainloop-signal.c
deleted file mode 100644 (file)
index 89f195e..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <signal.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "mainloop-signal.h"
-#include "util.h"
-#include "xmalloc.h"
-#include "log.h"
-
-struct pa_signal_event {
-    int sig;
-    struct sigaction saved_sigaction;
-    void (*callback) (struct pa_mainloop_api*a, struct pa_signal_event *e, int signal, void *userdata);
-    void *userdata;
-    void (*destroy_callback) (struct pa_mainloop_api*a, struct pa_signal_event*e, void *userdata);
-    struct pa_signal_event *previous, *next;
-};
-
-static struct pa_mainloop_api *api = NULL;
-static int signal_pipe[2] = { -1, -1 };
-static struct pa_io_event* io_event = NULL;
-static struct pa_signal_event *signals = NULL;
-
-static void signal_handler(int sig) {
-    write(signal_pipe[1], &sig, sizeof(sig));
-}
-
-static void callback(struct pa_mainloop_api*a, struct pa_io_event*e, int fd, enum pa_io_event_flags f, void *userdata) {
-    ssize_t r;
-    int sig;
-    struct pa_signal_event*s;
-    assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]);
-
-        
-    if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) {
-        if (errno == EAGAIN)
-            return;
-
-        pa_log(__FILE__": read(): %s\n", strerror(errno));
-        return;
-    }
-    
-    if (r != sizeof(sig)) {
-        pa_log(__FILE__": short read()\n");
-        return;
-    }
-    
-    for (s = signals; s; s = s->next) 
-        if (s->sig == sig) {
-            assert(s->callback);
-            s->callback(a, s, sig, s->userdata);
-            break;
-        }
-}
-
-int pa_signal_init(struct pa_mainloop_api *a) {
-    assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event);
-    
-    if (pipe(signal_pipe) < 0) {
-        pa_log(__FILE__": pipe() failed: %s\n", strerror(errno));
-        return -1;
-    }
-
-    pa_make_nonblock_fd(signal_pipe[0]);
-    pa_make_nonblock_fd(signal_pipe[1]);
-    pa_fd_set_cloexec(signal_pipe[0], 1);
-    pa_fd_set_cloexec(signal_pipe[1], 1);
-
-    api = a;
-    io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
-    assert(io_event);
-    return 0;
-}
-
-void pa_signal_done(void) {
-    assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && io_event);
-
-    while (signals)
-        pa_signal_free(signals);
-
-
-        api->io_free(io_event);
-    io_event = NULL;
-
-    close(signal_pipe[0]);
-    close(signal_pipe[1]);
-    signal_pipe[0] = signal_pipe[1] = -1;
-
-    api = NULL;
-}
-
-struct pa_signal_event* pa_signal_new(int sig, void (*callback) (struct pa_mainloop_api *api, struct pa_signal_event*e, int sig, void *userdata), void *userdata) {
-    struct pa_signal_event *e = NULL;
-    struct sigaction sa;
-    assert(sig > 0 && callback);
-    
-    for (e = signals; e; e = e->next)
-        if (e->sig == sig)
-            goto fail;
-    
-    e = pa_xmalloc(sizeof(struct pa_signal_event));
-    e->sig = sig;
-    e->callback = callback;
-    e->userdata = userdata;
-    e->destroy_callback = NULL;
-
-    memset(&sa, 0, sizeof(sa));
-    sa.sa_handler = signal_handler;
-    sigemptyset(&sa.sa_mask);
-    sa.sa_flags = SA_RESTART;
-    
-    if (sigaction(sig, &sa, &e->saved_sigaction) < 0)
-        goto fail;
-
-    e->previous = NULL;
-    e->next = signals;
-    signals = e;
-
-    return e;
-fail:
-    if (e)
-        pa_xfree(e);
-    return NULL;
-}
-
-void pa_signal_free(struct pa_signal_event *e) {
-    assert(e);
-
-    if (e->next)
-        e->next->previous = e->previous;
-    if (e->previous)
-        e->previous->next = e->next;
-    else
-        signals = e->next;
-
-    sigaction(e->sig, &e->saved_sigaction, NULL);
-
-    if (e->destroy_callback)
-        e->destroy_callback(api, e, e->userdata);
-    
-    pa_xfree(e);
-}
-
-void pa_signal_set_destroy(struct pa_signal_event *e, void (*callback) (struct pa_mainloop_api *api, struct pa_signal_event*e, void *userdata)) {
-    assert(e);
-    e->destroy_callback = callback;
-}
diff --git a/polyp/mainloop.c b/polyp/mainloop.c
deleted file mode 100644 (file)
index f6bb414..0000000
+++ /dev/null
@@ -1,643 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <signal.h>
-#include <unistd.h>
-#include <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"
-#include "xmalloc.h"
-#include "log.h"
-
-struct pa_io_event {
-    struct pa_mainloop *mainloop;
-    int dead;
-    int fd;
-    enum pa_io_event_flags events;
-    void (*callback) (struct pa_mainloop_api*a, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata);
-    struct pollfd *pollfd;
-    void *userdata;
-    void (*destroy_callback) (struct pa_mainloop_api*a, struct pa_io_event *e, void *userdata);
-};
-
-struct pa_time_event {
-    struct pa_mainloop *mainloop;
-    int dead;
-    int enabled;
-    struct timeval timeval;
-    void (*callback)(struct pa_mainloop_api*a, struct pa_time_event *e, const struct timeval*tv, void *userdata);
-    void *userdata;
-    void (*destroy_callback) (struct pa_mainloop_api*a, struct pa_time_event *e, void *userdata);
-};
-
-struct pa_defer_event {
-    struct pa_mainloop *mainloop;
-    int dead;
-    int enabled;
-    void (*callback)(struct pa_mainloop_api*a, struct pa_defer_event*e, void *userdata);
-    void *userdata;
-    void (*destroy_callback) (struct pa_mainloop_api*a, struct pa_defer_event *e, void *userdata);
-};
-
-struct pa_mainloop {
-    struct pa_idxset *io_events, *time_events, *defer_events;
-    int io_events_scan_dead, defer_events_scan_dead, time_events_scan_dead;
-
-    struct pollfd *pollfds;
-    unsigned max_pollfds, n_pollfds;
-    int rebuild_pollfds;
-
-    int quit, running, retval;
-    struct pa_mainloop_api api;
-
-    int deferred_pending;
-};
-
-/* IO events */
-static struct pa_io_event* mainloop_io_new(struct pa_mainloop_api*a, int fd, enum pa_io_event_flags events, void (*callback) (struct pa_mainloop_api*a, struct pa_io_event *e, int fd, enum pa_io_event_flags events, void *userdata), void *userdata) {
-    struct pa_mainloop *m;
-    struct pa_io_event *e;
-
-    assert(a && a->userdata && fd >= 0 && callback);
-    m = a->userdata;
-    assert(a == &m->api);
-
-    e = pa_xmalloc(sizeof(struct pa_io_event));
-    e->mainloop = m;
-    e->dead = 0;
-
-    e->fd = fd;
-    e->events = events;
-    e->callback = callback;
-    e->userdata = userdata;
-    e->destroy_callback = NULL;
-    e->pollfd = NULL;
-
-    pa_idxset_put(m->io_events, e, NULL);
-    m->rebuild_pollfds = 1;
-    return e;
-}
-
-static void mainloop_io_enable(struct pa_io_event *e, enum pa_io_event_flags events) {
-    assert(e && e->mainloop);
-
-    e->events = events;
-    if (e->pollfd)
-        e->pollfd->events =
-            (events & PA_IO_EVENT_INPUT ? POLLIN : 0) |
-            (events & PA_IO_EVENT_OUTPUT ? POLLOUT : 0) |
-            POLLERR | POLLHUP;
-}
-
-static void mainloop_io_free(struct pa_io_event *e) {
-    assert(e && e->mainloop);
-    e->dead = e->mainloop->io_events_scan_dead = e->mainloop->rebuild_pollfds = 1;
-}
-
-static void mainloop_io_set_destroy(struct pa_io_event *e, void (*callback)(struct pa_mainloop_api*a, struct pa_io_event *e, void *userdata)) {
-    assert(e);
-    e->destroy_callback = callback;
-}
-
-/* Defer events */
-static struct pa_defer_event* mainloop_defer_new(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, struct pa_defer_event *e, void *userdata), void *userdata) {
-    struct pa_mainloop *m;
-    struct pa_defer_event *e;
-
-    assert(a && a->userdata && callback);
-    m = a->userdata;
-    assert(a == &m->api);
-
-    e = pa_xmalloc(sizeof(struct pa_defer_event));
-    e->mainloop = m;
-    e->dead = 0;
-
-    e->enabled = 1;
-    e->callback = callback;
-    e->userdata = userdata;
-    e->destroy_callback = NULL;
-
-    pa_idxset_put(m->defer_events, e, NULL);
-
-    m->deferred_pending++;
-    return e;
-}
-
-static void mainloop_defer_enable(struct pa_defer_event *e, int b) {
-    assert(e);
-
-    if (e->enabled && !b) {
-        assert(e->mainloop->deferred_pending > 0);
-        e->mainloop->deferred_pending--;
-    } else if (!e->enabled && b)
-        e->mainloop->deferred_pending++;
-    
-    e->enabled = b;
-}
-
-static void mainloop_defer_free(struct pa_defer_event *e) {
-    assert(e);
-    e->dead = e->mainloop->defer_events_scan_dead = 1;
-
-    if (e->enabled) {
-        e->enabled = 0;
-        assert(e->mainloop->deferred_pending > 0);
-        e->mainloop->deferred_pending--;
-    }
-}
-
-static void mainloop_defer_set_destroy(struct pa_defer_event *e, void (*callback)(struct pa_mainloop_api*a, struct pa_defer_event *e, void *userdata)) {
-    assert(e);
-    e->destroy_callback = callback;
-}
-
-/* Time events */
-static struct pa_time_event* mainloop_time_new(struct pa_mainloop_api*a, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*a, struct pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
-    struct pa_mainloop *m;
-    struct pa_time_event *e;
-
-    assert(a && a->userdata && callback);
-    m = a->userdata;
-    assert(a == &m->api);
-
-    e = pa_xmalloc(sizeof(struct pa_time_event));
-    e->mainloop = m;
-    e->dead = 0;
-
-    e->enabled = !!tv;
-    if (tv)
-        e->timeval = *tv;
-
-    e->callback = callback;
-    e->userdata = userdata;
-    e->destroy_callback = NULL;
-
-    pa_idxset_put(m->time_events, e, NULL);
-    
-    return e;
-}
-
-static void mainloop_time_restart(struct pa_time_event *e, const struct timeval *tv) {
-    assert(e);
-
-    if (tv) {
-        e->enabled = 1;
-        e->timeval = *tv;
-    } else
-        e->enabled = 0;
-}
-
-static void mainloop_time_free(struct pa_time_event *e) {
-    assert(e);
-
-    e->dead = e->mainloop->time_events_scan_dead = 1;
-}
-
-static void mainloop_time_set_destroy(struct pa_time_event *e, void (*callback)(struct pa_mainloop_api*a, struct pa_time_event *e, void *userdata)) {
-    assert(e);
-    e->destroy_callback = callback;
-}
-
-/* quit() */
-
-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 const struct pa_mainloop_api vtable = {
-    .userdata = NULL,
-
-    .io_new= mainloop_io_new,
-    .io_enable= mainloop_io_enable,
-    .io_free= mainloop_io_free,
-    .io_set_destroy= mainloop_io_set_destroy,
-
-    .time_new = mainloop_time_new,
-    .time_restart = mainloop_time_restart,
-    .time_free = mainloop_time_free,
-    .time_set_destroy = mainloop_time_set_destroy,
-    
-    .defer_new = mainloop_defer_new,
-    .defer_enable = mainloop_defer_enable,
-    .defer_free = mainloop_defer_free,
-    .defer_set_destroy = mainloop_defer_set_destroy,
-    
-    .quit = mainloop_quit,
-};
-
-struct pa_mainloop *pa_mainloop_new(void) {
-    struct pa_mainloop *m;
-
-    m = pa_xmalloc(sizeof(struct pa_mainloop));
-
-    m->io_events = pa_idxset_new(NULL, NULL);
-    m->defer_events = pa_idxset_new(NULL, NULL);
-    m->time_events = pa_idxset_new(NULL, NULL);
-
-    assert(m->io_events && m->defer_events && m->time_events);
-
-    m->io_events_scan_dead = m->defer_events_scan_dead = m->time_events_scan_dead = 0;
-    
-    m->pollfds = NULL;
-    m->max_pollfds = m->n_pollfds = m->rebuild_pollfds = 0;
-
-    m->quit = m->running = m->retval = 0;
-
-    m->api = vtable;
-    m->api.userdata = m;
-
-    m->deferred_pending = 0;
-    
-    return m;
-}
-
-static int io_foreach(void *p, uint32_t index, int *del, void*userdata) {
-    struct pa_io_event *e = p;
-    int *all = userdata;
-    assert(e && del && all);
-
-    if (!*all && !e->dead)
-        return 0;
-    
-    if (e->destroy_callback)
-        e->destroy_callback(&e->mainloop->api, e, e->userdata);
-    pa_xfree(e);
-    *del = 1;
-    return 0;
-}
-
-static int time_foreach(void *p, uint32_t index, int *del, void*userdata) {
-    struct pa_time_event *e = p;
-    int *all = userdata;
-    assert(e && del && all);
-
-    if (!*all && !e->dead)
-        return 0;
-    
-    if (e->destroy_callback)
-        e->destroy_callback(&e->mainloop->api, e, e->userdata);
-    pa_xfree(e);
-    *del = 1;
-    return 0;
-}
-
-static int defer_foreach(void *p, uint32_t index, int *del, void*userdata) {
-    struct pa_defer_event *e = p;
-    int *all = userdata;
-    assert(e && del && all);
-
-    if (!*all && !e->dead)
-        return 0;
-    
-    if (e->destroy_callback)
-        e->destroy_callback(&e->mainloop->api, e, e->userdata);
-    pa_xfree(e);
-    *del = 1;
-    return 0;
-}
-
-void pa_mainloop_free(struct pa_mainloop* m) {
-    int all = 1;
-    assert(m);
-
-    pa_idxset_foreach(m->io_events, io_foreach, &all);
-    pa_idxset_foreach(m->time_events, time_foreach, &all);
-    pa_idxset_foreach(m->defer_events, defer_foreach, &all);
-
-    pa_idxset_free(m->io_events, NULL, NULL);
-    pa_idxset_free(m->time_events, NULL, NULL);
-    pa_idxset_free(m->defer_events, NULL, NULL);
-
-    pa_xfree(m->pollfds);
-    pa_xfree(m);
-}
-
-static void scan_dead(struct pa_mainloop *m) {
-    int all = 0;
-    assert(m);
-
-    if (m->io_events_scan_dead)
-        pa_idxset_foreach(m->io_events, io_foreach, &all);
-    if (m->time_events_scan_dead)
-        pa_idxset_foreach(m->time_events, time_foreach, &all);
-    if (m->defer_events_scan_dead)
-        pa_idxset_foreach(m->defer_events, defer_foreach, &all);
-
-    m->io_events_scan_dead = m->time_events_scan_dead = m->defer_events_scan_dead = 0;
-}
-
-static void rebuild_pollfds(struct pa_mainloop *m) {
-    struct pa_io_event*e;
-    struct pollfd *p;
-    uint32_t index = PA_IDXSET_INVALID;
-    unsigned l;
-
-    l = pa_idxset_ncontents(m->io_events);
-    if (m->max_pollfds < l) {
-        m->pollfds = pa_xrealloc(m->pollfds, sizeof(struct pollfd)*l);
-        m->max_pollfds = l;
-    }
-
-    m->n_pollfds = 0;
-    p = m->pollfds;
-    for (e = pa_idxset_first(m->io_events, &index); e; e = pa_idxset_next(m->io_events, &index)) {
-        if (e->dead) {
-            e->pollfd = NULL;
-            continue;
-        }
-
-        e->pollfd = p;
-        p->fd = e->fd;
-        p->events =
-            ((e->events & PA_IO_EVENT_INPUT) ? POLLIN : 0) |
-            ((e->events & PA_IO_EVENT_OUTPUT) ? POLLOUT : 0) |
-            POLLHUP |
-            POLLERR;
-        p->revents = 0;
-
-        p++;
-        m->n_pollfds++;
-    }
-}
-
-static int dispatch_pollfds(struct pa_mainloop *m) {
-    uint32_t index = PA_IDXSET_INVALID;
-    struct pa_io_event *e;
-    int r = 0;
-
-    for (e = pa_idxset_first(m->io_events, &index); e && !m->quit; e = pa_idxset_next(m->io_events, &index)) {
-        if (e->dead || !e->pollfd || !e->pollfd->revents)
-            continue;
-        
-        assert(e->pollfd->fd == e->fd && e->callback);
-        e->callback(&m->api, e, e->fd,
-                    (e->pollfd->revents & POLLHUP ? PA_IO_EVENT_HANGUP : 0) |
-                    (e->pollfd->revents & POLLIN ? PA_IO_EVENT_INPUT : 0) |
-                    (e->pollfd->revents & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) |
-                    (e->pollfd->revents & POLLERR ? PA_IO_EVENT_ERROR : 0),
-                    e->userdata);
-        e->pollfd->revents = 0;
-        r++;
-    }
-
-    return r;
-}
-
-static int dispatch_defer(struct pa_mainloop *m) {
-    uint32_t index;
-    struct pa_defer_event *e;
-    int r = 0;
-
-    if (!m->deferred_pending)
-        return 0;
-
-    for (e = pa_idxset_first(m->defer_events, &index); e && !m->quit; e = pa_idxset_next(m->defer_events, &index)) {
-        if (e->dead || !e->enabled)
-            continue;
-        assert(e->callback);
-        e->callback(&m->api, e, e->userdata);
-        r++;
-    }
-
-    return r;
-}
-
-static int calc_next_timeout(struct pa_mainloop *m) {
-    uint32_t index;
-    struct pa_time_event *e;
-    struct timeval now;
-    int t = -1;
-    int got_time = 0;
-
-    if (pa_idxset_isempty(m->time_events))
-        return -1;
-
-    for (e = pa_idxset_first(m->time_events, &index); e; e = pa_idxset_next(m->time_events, &index)) {
-        int tmp;
-        
-        if (e->dead || !e->enabled)
-            continue;
-
-        /* Let's save a system call */
-        if (!got_time) {
-            gettimeofday(&now, NULL);
-            got_time = 1;
-        }
-
-        if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) 
-            return 0;
-
-        tmp = (e->timeval.tv_sec - now.tv_sec)*1000;
-            
-        if (e->timeval.tv_usec > now.tv_usec)
-            tmp += (e->timeval.tv_usec - now.tv_usec)/1000;
-        else
-            tmp -= (now.tv_usec - e->timeval.tv_usec)/1000;
-
-        if (tmp == 0)
-            return 0;
-        else if (t == -1 || tmp < t)
-            t = tmp;
-    }
-
-    return t;
-}
-
-static int dispatch_timeout(struct pa_mainloop *m) {
-    uint32_t index;
-    struct pa_time_event *e;
-    struct timeval now;
-    int got_time = 0;
-    int r = 0;
-    assert(m);
-
-    if (pa_idxset_isempty(m->time_events))
-        return 0;
-
-    for (e = pa_idxset_first(m->time_events, &index); e && !m->quit; e = pa_idxset_next(m->time_events, &index)) {
-        
-        if (e->dead || !e->enabled)
-            continue;
-
-        /* Let's save a system call */
-        if (!got_time) {
-            gettimeofday(&now, NULL);
-            got_time = 1;
-        }
-        
-        if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) {
-            assert(e->callback);
-
-            e->enabled = 0;
-            e->callback(&m->api, e, &e->timeval, e->userdata);
-
-            r++;
-        }
-    }
-
-    return r;
-}
-
-int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval) {
-    int r, t, dispatched = 0;
-    assert(m && !m->running);
-
-    m->running ++;
-
-    if (m->quit)
-        goto quit;
-
-    scan_dead(m);
-    dispatched += dispatch_defer(m);
-
-    if(m->quit)
-        goto quit;
-    
-    if (m->rebuild_pollfds) {
-        rebuild_pollfds(m);
-        m->rebuild_pollfds = 0;
-    }
-
-    t = block ? calc_next_timeout(m) : 0;
-
-    r = poll(m->pollfds, m->n_pollfds, t);
-
-    if (r < 0) {
-        if (errno == EINTR)
-            r = 0;
-        else
-            pa_log(__FILE__": select(): %s\n", strerror(errno));
-    } else {
-        dispatched += dispatch_timeout(m);
-
-        if(m->quit)
-            goto quit;
-        
-        if (r > 0) {
-            dispatched += dispatch_pollfds(m);
-
-            if(m->quit)
-                goto quit;
-        }
-    }
-    
-    m->running--;
-    
-/*     pa_log("dispatched: %i\n", dispatched); */
-    
-    return r < 0 ? -1 : dispatched;
-
-quit:
-    
-    m->running--;
-    
-    if (retval) 
-        *retval = m->retval;
-    
-    return -2;
-}
-
-int pa_mainloop_run(struct pa_mainloop *m, int *retval) {
-    int r;
-    while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0);
-
-    if (r == -2)
-        return 1;
-    else if (r < 0)
-        return -1;
-    else
-        return 0;
-}
-
-void pa_mainloop_quit(struct pa_mainloop *m, int r) {
-    assert(m);
-    m->quit = r;
-}
-
-struct pa_mainloop_api* pa_mainloop_get_api(struct pa_mainloop*m) {
-    assert(m);
-    return &m->api;
-}
-
-int pa_mainloop_deferred_pending(struct pa_mainloop *m) {
-    assert(m);
-    return m->deferred_pending > 0;
-}
-
-
-void pa_mainloop_dump(struct pa_mainloop *m) {
-    assert(m);
-
-    pa_log(__FILE__": Dumping mainloop sources START\n");
-    
-    {
-        uint32_t index = PA_IDXSET_INVALID;
-        struct pa_io_event *e;
-        for (e = pa_idxset_first(m->io_events, &index); e; e = pa_idxset_next(m->io_events, &index)) {
-            if (e->dead)
-                continue;
-            
-            pa_log(__FILE__": kind=io fd=%i events=%i callback=%p userdata=%p\n", e->fd, (int) e->events, (void*) e->callback, (void*) e->userdata);
-        }
-    }
-    {
-        uint32_t index = PA_IDXSET_INVALID;
-        struct pa_defer_event *e;
-        for (e = pa_idxset_first(m->defer_events, &index); e; e = pa_idxset_next(m->defer_events, &index)) {
-            if (e->dead)
-                continue;
-            
-            pa_log(__FILE__": kind=defer enabled=%i callback=%p userdata=%p\n", e->enabled, (void*) e->callback, (void*) e->userdata);
-        }
-    }
-    {
-        uint32_t index = PA_IDXSET_INVALID;
-        struct pa_time_event *e;
-        for (e = pa_idxset_first(m->time_events, &index); e; e = pa_idxset_next(m->time_events, &index)) {
-            if (e->dead)
-                continue;
-            
-            pa_log(__FILE__": kind=time enabled=%i time=%lu.%lu callback=%p userdata=%p\n", e->enabled, (unsigned long) e->timeval.tv_sec, (unsigned long) e->timeval.tv_usec, (void*) e->callback, (void*) e->userdata);
-        }
-    }
-
-    pa_log(__FILE__": Dumping mainloop sources STOP\n");
-
-}
diff --git a/polyp/mainloop.h b/polyp/mainloop.h
deleted file mode 100644 (file)
index 06a6cca..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-#ifndef foomainloophfoo
-#define foomainloophfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "mainloop-api.h"
-#include "cdecl.h"
-
-PA_C_DECL_BEGIN
-
-/** \file
- * 
- * A minimal main loop implementation based on the C library's poll()
- * function. Using the routines defined herein you may create a simple
- * main loop supporting the generic main loop abstraction layer as
- * defined in \ref mainloop-api.h. This implementation is thread safe
- * as long as you access the main loop object from a single thread only.*/
-
-/** \struct pa_mainloop
- * An opaque main loop object
- */
-struct pa_mainloop;
-
-/** Allocate a new main loop object */
-struct pa_mainloop *pa_mainloop_new(void);
-
-/** Free a main loop object */
-void pa_mainloop_free(struct pa_mainloop* m);
-
-/** Run a single iteration of the main loop. Returns a negative value
-on error or exit request. If block is nonzero, block for events if
-none are queued. Optionally return the return value as specified with
-the main loop's quit() routine in the integer variable retval points
-to. On success returns the number of source dispatched in this iteration. */
-int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval);
-
-/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
-int pa_mainloop_run(struct pa_mainloop *m, int *retval);
-
-/** Return the abstract main loop abstraction layer vtable for this main loop. This calls pa_mainloop_iterate() iteratively.*/
-struct pa_mainloop_api* pa_mainloop_get_api(struct pa_mainloop*m);
-
-/** Return non-zero when there are any deferred events pending. \since 0.5 */
-int pa_mainloop_deferred_pending(struct pa_mainloop *m);
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/memblock.c b/polyp/memblock.c
deleted file mode 100644 (file)
index c070bee..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-
-#include "memblock.h"
-#include "xmalloc.h"
-
-static void stat_add(struct pa_memblock*m, struct pa_memblock_stat *s) {
-    assert(m);
-
-    if (!s) {
-        m->stat = NULL;
-        return;
-    }
-
-    m->stat = pa_memblock_stat_ref(s);
-    s->total++;
-    s->allocated++;
-    s->total_size += m->length;
-    s->allocated_size += m->length;
-}
-
-static void stat_remove(struct pa_memblock *m) {
-    assert(m);
-
-    if (!m->stat)
-        return;
-
-    m->stat->total--;
-    m->stat->total_size -= m->length;
-    
-    pa_memblock_stat_unref(m->stat);
-    m->stat = NULL;
-}
-
-struct pa_memblock *pa_memblock_new(size_t length, struct pa_memblock_stat*s) {
-    struct pa_memblock *b = pa_xmalloc(sizeof(struct pa_memblock)+length);
-    b->type = PA_MEMBLOCK_APPENDED;
-    b->ref = 1;
-    b->length = length;
-    b->data = b+1;
-    b->free_cb = NULL;
-    b->read_only = 0;
-    stat_add(b, s);
-    return b;
-}
-
-struct pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, struct pa_memblock_stat*s) {
-    struct pa_memblock *b = pa_xmalloc(sizeof(struct pa_memblock));
-    b->type = PA_MEMBLOCK_DYNAMIC;
-    b->ref = 1;
-    b->length = length;
-    b->data = d;
-    b->free_cb = NULL;
-    b->read_only = 0;
-    stat_add(b, s);
-    return b;
-}
-
-struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length, int read_only, struct pa_memblock_stat*s) {
-    struct pa_memblock *b = pa_xmalloc(sizeof(struct pa_memblock));
-    b->type = PA_MEMBLOCK_FIXED;
-    b->ref = 1;
-    b->length = length;
-    b->data = d;
-    b->free_cb = NULL;
-    b->read_only = read_only;
-    stat_add(b, s);
-    return b;
-}
-
-struct pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), int read_only, struct pa_memblock_stat*s) {
-    struct pa_memblock *b;
-    assert(d && length && free_cb);
-    b = pa_xmalloc(sizeof(struct pa_memblock));
-    b->type = PA_MEMBLOCK_USER;
-    b->ref = 1;
-    b->length = length;
-    b->data = d;
-    b->free_cb = free_cb;
-    b->read_only = read_only;
-    stat_add(b, s);
-    return b;
-}
-
-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);
-
-    if ((--(b->ref)) == 0) {
-        stat_remove(b);
-
-        if (b->type == PA_MEMBLOCK_USER) {
-            assert(b->free_cb);
-            b->free_cb(b->data);
-        } else if (b->type == PA_MEMBLOCK_DYNAMIC)
-            pa_xfree(b->data);
-
-        pa_xfree(b);
-    }
-}
-
-void pa_memblock_unref_fixed(struct pa_memblock *b) {
-    assert(b && b->ref >= 1 && b->type == PA_MEMBLOCK_FIXED);
-
-    if (b->ref == 1)
-        pa_memblock_unref(b);
-    else {
-        b->data = pa_xmemdup(b->data, b->length);
-        b->type = PA_MEMBLOCK_DYNAMIC;
-        b->ref--;
-    }
-}
-
-struct pa_memblock_stat* pa_memblock_stat_new(void) {
-    struct pa_memblock_stat *s;
-
-    s = pa_xmalloc(sizeof(struct pa_memblock_stat));
-    s->ref = 1;
-    s->total = s->total_size = s->allocated = s->allocated_size = 0;
-
-    return s;
-}
-
-void pa_memblock_stat_unref(struct pa_memblock_stat *s) {
-    assert(s && s->ref >= 1);
-
-    if (!(--(s->ref))) {
-        assert(!s->total);
-        pa_xfree(s);
-    }
-}
-
-struct pa_memblock_stat * pa_memblock_stat_ref(struct pa_memblock_stat *s) {
-    assert(s);
-    s->ref++;
-    return s;
-}
diff --git a/polyp/memblock.h b/polyp/memblock.h
deleted file mode 100644 (file)
index 8555954..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#ifndef foomemblockhfoo
-#define foomemblockhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <sys/types.h>
-#include <inttypes.h>
-
-/* A pa_memblock is a reference counted memory block. Polypaudio
- * passed references to pa_memblocks around instead of copying
- * data. See pa_memchunk for a structure that describes parts of
- * memory blocks. */
-
-/* The type of memory this block points to */
-enum pa_memblock_type {
-    PA_MEMBLOCK_FIXED,     /* data is a pointer to fixed memory that needs not to be freed */
-    PA_MEMBLOCK_APPENDED,  /* The most common kind: the data is appended to the memory block */ 
-    PA_MEMBLOCK_DYNAMIC,   /* data is a pointer to some memory allocated with pa_xmalloc() */
-    PA_MEMBLOCK_USER       /* User supplied memory, to be freed with free_cb */
-};
-
-/* A structure of keeping memory block statistics */
-struct pa_memblock_stat;
-
-struct pa_memblock {
-    enum pa_memblock_type type;
-    unsigned ref;  /* the reference counter */
-    int read_only; /* boolean */
-    size_t length;
-    void *data;
-    void (*free_cb)(void *p);  /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
-    struct pa_memblock_stat *stat;
-};
-
-/* Allocate a new memory block of type PA_MEMBLOCK_APPENDED */
-struct pa_memblock *pa_memblock_new(size_t length, struct pa_memblock_stat*s);
-
-/* Allocate a new memory block of type PA_MEMBLOCK_DYNAMIC. The pointer data is to be maintained be the memory block */
-struct pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, struct pa_memblock_stat*s);
-
-/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */
-struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length, int read_only, struct pa_memblock_stat*s);
-
-/* Allocate a new memory block of type PA_MEMBLOCK_USER */
-struct pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), int read_only, struct pa_memblock_stat*s);
-
-void pa_memblock_unref(struct pa_memblock*b);
-struct pa_memblock* pa_memblock_ref(struct pa_memblock*b);
-
-/* This special unref function has to be called by the owner of the
-memory of a static memory block when he wants to release all
-references to the memory. This causes the memory to be copied and
-converted into a PA_MEMBLOCK_DYNAMIC type memory block */
-void pa_memblock_unref_fixed(struct pa_memblock*b);
-
-/* Matinatins statistics about memory blocks */
-struct pa_memblock_stat {
-    int ref;
-    unsigned total;
-    unsigned total_size;
-    unsigned allocated;
-    unsigned allocated_size;
-};
-
-struct pa_memblock_stat* pa_memblock_stat_new(void);
-void pa_memblock_stat_unref(struct pa_memblock_stat *s);
-struct pa_memblock_stat * pa_memblock_stat_ref(struct pa_memblock_stat *s);
-
-#endif
diff --git a/polyp/memblockq.c b/polyp/memblockq.c
deleted file mode 100644 (file)
index 3f2e4db..0000000
+++ /dev/null
@@ -1,343 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <sys/time.h>
-#include <time.h>
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "memblockq.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "mcalign.h"
-
-struct memblock_list {
-    struct memblock_list *next, *prev;
-    struct pa_memchunk chunk;
-};
-
-struct pa_memblockq {
-    struct memblock_list *blocks, *blocks_tail;
-    unsigned n_blocks;
-    size_t current_length, maxlength, tlength, base, prebuf, orig_prebuf, minreq;
-    struct pa_mcalign *mcalign;
-    struct pa_memblock_stat *memblock_stat;
-};
-
-struct pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq, struct pa_memblock_stat *s) {
-    struct pa_memblockq* bq;
-    assert(maxlength && base && maxlength);
-    
-    bq = pa_xmalloc(sizeof(struct pa_memblockq));
-    bq->blocks = bq->blocks_tail = 0;
-    bq->n_blocks = 0;
-
-    bq->current_length = 0;
-
-    pa_log_debug(__FILE__": memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq);
-    
-    bq->base = base;
-
-    bq->maxlength = ((maxlength+base-1)/base)*base;
-    assert(bq->maxlength >= base);
-
-    bq->tlength = ((tlength+base-1)/base)*base;
-    if (!bq->tlength || bq->tlength >= bq->maxlength)
-        bq->tlength = bq->maxlength;
-
-    bq->minreq = (minreq/base)*base;
-    if (bq->minreq == 0)
-        bq->minreq = 1;
-    
-    bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf;
-    bq->prebuf = (bq->prebuf/base)*base;
-    if (bq->prebuf > bq->maxlength)
-        bq->prebuf = bq->maxlength;
-
-    if (bq->prebuf > bq->tlength - bq->minreq)
-        bq->prebuf = bq->tlength - bq->minreq;
-
-    bq->orig_prebuf = bq->prebuf;
-    
-    pa_log_debug(__FILE__": memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq);
-    
-    bq->mcalign = NULL;
-
-    bq->memblock_stat = s;
-
-    return bq;
-}
-
-void pa_memblockq_free(struct pa_memblockq* bq) {
-    assert(bq);
-
-    pa_memblockq_flush(bq);
-    
-    if (bq->mcalign)
-        pa_mcalign_free(bq->mcalign);
-
-    pa_xfree(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);
-
-    pa_memblockq_seek(bq, delta);
-    
-    if (bq->blocks_tail && bq->blocks_tail->chunk.memblock == chunk->memblock) {
-        /* Try to merge memory chunks */
-
-        if (bq->blocks_tail->chunk.index+bq->blocks_tail->chunk.length == chunk->index) {
-            bq->blocks_tail->chunk.length += chunk->length;
-            bq->current_length += chunk->length;
-            return;
-        }
-    }
-    
-    q = pa_xmalloc(sizeof(struct memblock_list));
-
-    q->chunk = *chunk;
-    pa_memblock_ref(q->chunk.memblock);
-    assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length);
-    q->next = NULL;
-    if ((q->prev = bq->blocks_tail))
-        bq->blocks_tail->next = q;
-    else
-        bq->blocks = q;
-    
-    bq->blocks_tail = q;
-
-    bq->n_blocks++;
-    bq->current_length += chunk->length;
-
-    pa_memblockq_shorten(bq, bq->maxlength);
-}
-
-int pa_memblockq_peek(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);
-
-    return 0;
-}
-
-void pa_memblockq_drop(struct pa_memblockq *bq, const struct pa_memchunk *chunk, size_t length) {
-    assert(bq && chunk && length);
-
-    if (!bq->blocks || memcmp(&bq->blocks->chunk, chunk, sizeof(struct pa_memchunk))) 
-        return;
-    
-    assert(length <= bq->blocks->chunk.length);
-    pa_memblockq_skip(bq, length);
-}
-
-static void remove_block(struct pa_memblockq *bq, struct memblock_list *q) {
-    assert(bq && q);
-
-    if (q->prev)
-        q->prev->next = q->next;
-    else {
-        assert(bq->blocks == q);
-        bq->blocks = q->next;
-    }
-    
-    if (q->next)
-        q->next->prev = q->prev;
-    else {
-        assert(bq->blocks_tail == q);
-        bq->blocks_tail = q->prev;
-    }
-    
-    pa_memblock_unref(q->chunk.memblock);
-    pa_xfree(q);
-    
-    bq->n_blocks--;
-}
-
-void pa_memblockq_skip(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;
-
-        bq->blocks->chunk.index += l;
-        bq->blocks->chunk.length -= l;
-        bq->current_length -= l;
-        
-        if (!bq->blocks->chunk.length)
-            remove_block(bq, bq->blocks);
-
-        length -= l;
-    }
-}
-
-void pa_memblockq_shorten(struct pa_memblockq *bq, size_t length) {
-    size_t l;
-    assert(bq);
-
-    if (bq->current_length <= length)
-        return;
-
-    /*pa_log(__FILE__": Warning! pa_memblockq_shorten()\n");*/
-    
-    l = bq->current_length - length;
-    l /= bq->base;
-    l *= bq->base;
-
-    pa_memblockq_skip(bq, l);
-}
-
-
-void pa_memblockq_empty(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_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, bq->memblock_stat);
-        assert(bq->mcalign);
-    }
-    
-    pa_mcalign_push(bq->mcalign, chunk);
-
-    while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
-        pa_memblockq_push(bq, &rchunk, delta);
-        pa_memblock_unref(rchunk.memblock);
-        delta = 0;
-    }
-}
-
-uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq) {
-    assert(bq);
-    return bq->minreq;
-}
-
-void pa_memblockq_prebuf_disable(struct pa_memblockq *bq) {
-    assert(bq);
-    bq->prebuf = 0;
-}
-
-void pa_memblockq_prebuf_reenable(struct pa_memblockq *bq) {
-    assert(bq);
-    bq->prebuf = bq->orig_prebuf;
-}
-
-void pa_memblockq_seek(struct pa_memblockq *bq, size_t length) {
-    assert(bq);
-
-    if (!length)
-        return;
-
-    while (length >= bq->base) {
-        size_t l = length;
-        if (!bq->current_length)
-            return;
-
-        assert(bq->blocks_tail);
-        
-        if (l > bq->blocks_tail->chunk.length)
-            l = bq->blocks_tail->chunk.length;
-
-        bq->blocks_tail->chunk.length -= l;
-        bq->current_length -= l;
-        
-        if (bq->blocks_tail->chunk.length == 0)
-            remove_block(bq, bq->blocks);
-
-        length -= l;
-    }
-}
-
-void pa_memblockq_flush(struct pa_memblockq *bq) {
-    struct memblock_list *l;
-    assert(bq);
-    
-    while ((l = bq->blocks)) {
-        bq->blocks = l->next;
-        pa_memblock_unref(l->chunk.memblock);
-        pa_xfree(l);
-    }
-
-    bq->blocks_tail = NULL;
-    bq->n_blocks = 0;
-    bq->current_length = 0;
-}
-
-uint32_t pa_memblockq_get_tlength(struct pa_memblockq *bq) {
-    assert(bq);
-    return bq->tlength;
-}
diff --git a/polyp/memblockq.h b/polyp/memblockq.h
deleted file mode 100644 (file)
index 2c762bf..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#ifndef foomemblockqhfoo
-#define foomemblockqhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <sys/types.h>
-
-#include "memblock.h"
-#include "memchunk.h"
-
-/* A memblockq is a queue of pa_memchunks (yepp, the name is not
- * perfect). It is similar to the ring buffers used by most other
- * audio software. In contrast to a ring buffer this memblockq data
- * type doesn't need to copy any data around, it just maintains
- * references to reference counted memory blocks. */
-
-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,
-                                      struct pa_memblock_stat *s);
-void pa_memblockq_free(struct pa_memblockq*bq);
-
-/* Push a new memory chunk into the queue. Optionally specify a value for future cancellation. */
-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, only valid aufter pa_memblockq_peek() */
-void pa_memblockq_drop(struct pa_memblockq *bq, const struct pa_memchunk *chunk, size_t length);
-
-/* Drop the specified bytes from the queue */
-void pa_memblockq_skip(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);
-
-/* 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);
-
-/* Returns the minimal request value */
-uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq);
-
-/* Force disabling of pre-buf even when the pre-buffer is not yet filled */
-void pa_memblockq_prebuf_disable(struct pa_memblockq *bq);
-
-/* Reenable pre-buf to the initial level */
-void pa_memblockq_prebuf_reenable(struct pa_memblockq *bq);
-
-/* Manipulate the write pointer */
-void pa_memblockq_seek(struct pa_memblockq *bq, size_t delta);
-
-/* Flush the queue */
-void pa_memblockq_flush(struct pa_memblockq *bq);
-
-/* Get Target length */
-uint32_t pa_memblockq_get_tlength(struct pa_memblockq *bq);
-
-#endif
diff --git a/polyp/memchunk.c b/polyp/memchunk.c
deleted file mode 100644 (file)
index d1c923f..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-
-#include "memchunk.h"
-#include "xmalloc.h"
-
-void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s, size_t min) {
-    struct pa_memblock *n;
-    size_t l;
-    assert(c && c->memblock && c->memblock->ref >= 1);
-
-    if (c->memblock->ref == 1 && !c->memblock->read_only && c->memblock->length >= c->index+min)
-        return;
-
-    l = c->length;
-    if (l < min)
-        l = min;
-    
-    n = pa_memblock_new(l, s);
-    memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length);
-    pa_memblock_unref(c->memblock);
-    c->memblock = n;
-    c->index = 0;
-}
-
-void pa_memchunk_reset(struct pa_memchunk *c) {
-    assert(c);
-
-    c->memblock = NULL;
-    c->length = c->index = 0;
-}
diff --git a/polyp/memchunk.h b/polyp/memchunk.h
deleted file mode 100644 (file)
index a004c2e..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef foomemchunkhfoo
-#define foomemchunkhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "memblock.h"
-
-/* A memchunk is a part of a memblock. In contrast to the memblock, a
- * memchunk is not allocated dynamically or reference counted, instead
- * it is usually stored on the stack and copied around */
-
-struct pa_memchunk {
-    struct pa_memblock *memblock;
-    size_t index, length;
-};
-
-/* Make a memchunk writable, i.e. make sure that the caller may have
- * exclusive access to the memblock and it is not read_only. If needed
- * the memblock in the structure is replaced by a copy. */
-void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s, size_t min);
-
-/* Invalidate a memchunk. This does not free the cotaining memblock,
- * but sets all members to zero. */
-void pa_memchunk_reset(struct pa_memchunk *c);
-
-#endif
diff --git a/polyp/modargs.h b/polyp/modargs.h
deleted file mode 100644 (file)
index 07db6ae..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef foomodargshfoo
-#define foomodargshfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include "sample.h"
-#include "core.h"
-
-struct pa_modargs;
-
-/* A generic parser for module arguments */
-
-/* Parse the string args. The NULL-terminated array keys contains all valid arguments. */
-struct pa_modargs *pa_modargs_new(const char *args, const char* const keys[]);
-void pa_modargs_free(struct pa_modargs*ma);
-
-/* Return the module argument for the specified name as a string. If
- * the argument was not specified, return def instead.*/
-const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def);
-
-/* Return a module argument as unsigned 32bit value in *value */
-int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value);
-int pa_modargs_get_value_s32(struct pa_modargs *ma, const char *key, int32_t *value);
-int pa_modargs_get_value_boolean(struct pa_modargs *ma, const char *key, int *value);
-
-/* Return sample spec data from the three arguments "rate", "format" and "channels" */
-int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *ss);
-
-#endif
diff --git a/polyp/modinfo.c b/polyp/modinfo.c
deleted file mode 100644 (file)
index a96bb17..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <ltdl.h>
-#include <assert.h>
-
-#include "xmalloc.h"
-#include "util.h"
-#include "modinfo.h"
-#include "log.h"
-
-#define PA_SYMBOL_AUTHOR "pa__get_author"
-#define PA_SYMBOL_DESCRIPTION "pa__get_description"
-#define PA_SYMBOL_USAGE "pa__get_usage"
-#define PA_SYMBOL_VERSION "pa__get_version"
-
-struct pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) {
-    struct pa_modinfo *i;
-    const char* (*func)(void);
-    assert(dl);
-
-    i = pa_xmalloc0(sizeof(struct pa_modinfo));
-
-    if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_AUTHOR)))
-        i->author = pa_xstrdup(func());
-
-    if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_DESCRIPTION)))
-        i->description = pa_xstrdup(func());
-
-    if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_USAGE)))
-        i->usage = pa_xstrdup(func());
-
-    if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_VERSION)))
-        i->version = pa_xstrdup(func());
-
-    return i;
-}
-
-struct pa_modinfo *pa_modinfo_get_by_name(const char *name) {
-    lt_dlhandle dl;
-    struct pa_modinfo *i;
-    assert(name);
-
-    if (!(dl = lt_dlopenext(name))) {
-        pa_log(__FILE__": Failed to open module \"%s\": %s\n", name, lt_dlerror());
-        return NULL;
-    }
-
-    i = pa_modinfo_get_by_handle(dl);
-    lt_dlclose(dl);
-
-    return i;
-}
-
-void pa_modinfo_free(struct pa_modinfo *i) {
-    assert(i);
-    pa_xfree(i->author);
-    pa_xfree(i->description);
-    pa_xfree(i->usage);
-    pa_xfree(i->version);
-    pa_xfree(i);
-}
diff --git a/polyp/module-alsa-sink.c b/polyp/module-alsa-sink.c
deleted file mode 100644 (file)
index 9933d37..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdio.h>
-#include <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"
-#include "xmalloc.h"
-#include "log.h"
-#include "module-alsa-sink-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ALSA Sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("sink_name=<name for the sink> device=<ALSA device> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size>")
-
-#define PA_TYPEID_ALSA PA_TYPEID_MAKE('A', 'L', 'S', 'A')
-
-struct userdata {
-    snd_pcm_t *pcm_handle;
-    struct pa_sink *sink;
-    struct pa_io_event **io_events;
-    unsigned n_io_events;
-
-    size_t frame_size, fragment_size;
-    struct pa_memchunk memchunk, silence;
-    struct pa_module *module;
-};
-
-static const char* const valid_modargs[] = {
-    "device",
-    "sink_name",
-    "format",
-    "channels",
-    "rate",
-    "fragments",
-    "fragment_size",
-    NULL
-};
-
-#define DEFAULT_SINK_NAME "alsa_output"
-#define DEFAULT_DEVICE "plughw:0,0"
-
-static void update_usage(struct userdata *u) {
-   pa_module_set_used(u->module,
-                      (u->sink ? pa_idxset_ncontents(u->sink->inputs) : 0) +
-                      (u->sink ? pa_idxset_ncontents(u->sink->monitor_source->outputs) : 0));
-}
-
-static void xrun_recovery(struct userdata *u) {
-    assert(u);
-
-    pa_log(__FILE__": *** ALSA-XRUN (playback) ***\n");
-    
-    if (snd_pcm_prepare(u->pcm_handle) < 0)
-        pa_log(__FILE__": snd_pcm_prepare() failed\n");
-}
-
-static void do_write(struct userdata *u) {
-    assert(u);
-
-    update_usage(u);
-    
-    for (;;) {
-        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, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) {
-            if (frames == -EAGAIN)
-                return;
-
-            if (frames == -EPIPE) {
-                xrun_recovery(u);
-                continue;
-            }
-
-            pa_log(__FILE__": snd_pcm_writei() failed\n");
-            return;
-        }
-
-        if (memchunk == &u->memchunk) {
-            size_t l = frames * u->frame_size;
-            memchunk->index += l;
-            memchunk->length -= l;
-
-            if (memchunk->length == 0) {
-                pa_memblock_unref(memchunk->memblock);
-                memchunk->memblock = NULL;
-                memchunk->index = memchunk->length = 0;
-            }
-        }
-        
-        break;
-    }
-}
-
-static void io_callback(struct pa_mainloop_api*a, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    struct userdata *u = userdata;
-    assert(u && a && e);
-
-    if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
-        xrun_recovery(u);
-
-    do_write(u);
-}
-
-static pa_usec_t sink_get_latency_cb(struct pa_sink *s) {
-    pa_usec_t r = 0;
-    struct userdata *u = s->userdata;
-    snd_pcm_sframes_t frames;
-    assert(s && u && u->sink);
-
-    if (snd_pcm_delay(u->pcm_handle, &frames) < 0) {
-        pa_log(__FILE__": failed to get delay\n");
-        s->get_latency = NULL;
-        return 0;
-    }
-
-    if (frames < 0)
-        frames = 0;
-    
-    r += pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
-
-    if (u->memchunk.memblock)
-        r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
-
-    return r;
-}
-
-int pa__init(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;
-    uint32_t periods, fragsize;
-    snd_pcm_uframes_t period_size;
-    size_t frame_size;
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments\n");
-        goto fail;
-    }
-
-    ss = c->default_sample_spec;
-    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
-        pa_log(__FILE__": failed to parse sample specification\n");
-        goto fail;
-    }
-    frame_size = pa_frame_size(&ss);
-    
-    periods = 12;
-    fragsize = 1024;
-    if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) {
-        pa_log(__FILE__": failed to parse buffer metrics\n");
-        goto fail;
-    }
-    period_size = fragsize;
-    
-    u = pa_xmalloc0(sizeof(struct userdata));
-    m->userdata = u;
-    u->module = m;
-    
-    if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) {
-        pa_log(__FILE__": Error opening PCM device %s\n", dev);
-        goto fail;
-    }
-
-    if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size) < 0) {
-        pa_log(__FILE__": Failed to set hardware parameters\n");
-        goto fail;
-    }
-
-    u->sink = pa_sink_new(c, PA_TYPEID_ALSA, 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_events(u->pcm_handle, c->mainloop, &u->io_events, &u->n_io_events, io_callback, u) < 0) {
-        pa_log(__FILE__": failed to obtain file descriptors\n");
-        goto fail;
-    }
-    
-    u->frame_size = frame_size;
-    u->fragment_size = period_size;
-
-    pa_log(__FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size);
-
-    u->silence.memblock = pa_memblock_new(u->silence.length = u->fragment_size, c->memblock_stat);
-    assert(u->silence.memblock);
-    pa_silence_memblock(u->silence.memblock, &ss);
-    u->silence.index = 0;
-
-    u->memchunk.memblock = NULL;
-    u->memchunk.index = u->memchunk.length = 0;
-    
-    ret = 0;
-     
-finish:
-     if (ma)
-         pa_modargs_free(ma);
-    
-    return ret;
-
-fail:
-    
-    if (u)
-        pa__done(c, m);
-
-    goto finish;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    assert(c && m);
-
-    if (!(u = m->userdata))
-        return;
-    
-    if (u->sink) {
-        pa_sink_disconnect(u->sink);
-        pa_sink_unref(u->sink);
-    }
-    
-    if (u->io_events)
-        pa_free_io_events(c->mainloop, u->io_events, u->n_io_events);
-    
-    if (u->pcm_handle) {
-        snd_pcm_drop(u->pcm_handle);
-        snd_pcm_close(u->pcm_handle);
-    }
-    
-    if (u->memchunk.memblock)
-        pa_memblock_unref(u->memchunk.memblock);
-    if (u->silence.memblock)
-        pa_memblock_unref(u->silence.memblock);
-    
-    pa_xfree(u);
-}
-
diff --git a/polyp/module-alsa-source.c b/polyp/module-alsa-source.c
deleted file mode 100644 (file)
index 67e38a1..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdio.h>
-#include <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"
-#include "xmalloc.h"
-#include "log.h"
-#include "module-alsa-source-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ALSA Source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("source_name=<name for the source> device=<ALSA device> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size>")
-
-#define PA_TYPEID_ALSA PA_TYPEID_MAKE('A', 'L', 'S', 'A')
-
-struct userdata {
-    snd_pcm_t *pcm_handle;
-    struct pa_source *source;
-    struct pa_io_event **io_events;
-    unsigned n_io_events;
-
-    size_t frame_size, fragment_size;
-    struct pa_memchunk memchunk;
-    struct pa_module *module;
-};
-
-static const char* const valid_modargs[] = {
-    "device",
-    "source_name",
-    "channels",
-    "rate",
-    "format",
-    "fragments",
-    "fragment_size",
-    NULL
-};
-
-#define DEFAULT_SOURCE_NAME "alsa_input"
-#define DEFAULT_DEVICE "hw:0,0"
-
-static void update_usage(struct userdata *u) {
-   pa_module_set_used(u->module,
-                      (u->source ? pa_idxset_ncontents(u->source->outputs) : 0));
-}
-
-static void xrun_recovery(struct userdata *u) {
-    assert(u);
-
-    pa_log(__FILE__": *** ALSA-XRUN (capture) ***\n");
-    
-    if (snd_pcm_prepare(u->pcm_handle) < 0)
-        pa_log(__FILE__": snd_pcm_prepare() failed\n");
-}
-
-static void do_read(struct userdata *u) {
-    assert(u);
-
-    update_usage(u);
-    
-    for (;;) {
-        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->source->core->memblock_stat);
-            u->memchunk.index = 0;
-        }
-            
-        assert(u->memchunk.memblock && u->memchunk.memblock->data && u->memchunk.length && u->memchunk.memblock->length && (u->memchunk.length % u->frame_size) == 0);
-
-        if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) {
-            if (frames == -EAGAIN)
-                return;
-            
-            if (frames == -EPIPE) {
-                xrun_recovery(u);
-                continue;
-            }
-
-            pa_log(__FILE__": snd_pcm_readi() failed: %s\n", strerror(-frames));
-            return;
-        }
-
-        l = frames * u->frame_size;
-        
-        post_memchunk = u->memchunk;
-        post_memchunk.length = l;
-
-        pa_source_post(u->source, &post_memchunk);
-
-        u->memchunk.index += l;
-        u->memchunk.length -= l;
-        
-        if (u->memchunk.length == 0) {
-            pa_memblock_unref(u->memchunk.memblock);
-            u->memchunk.memblock = NULL;
-            u->memchunk.index = u->memchunk.length = 0;
-        }
-        
-        break;
-    }
-}
-
-static void io_callback(struct pa_mainloop_api*a, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    struct userdata *u = userdata;
-    assert(u && a && e);
-
-    if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
-        xrun_recovery(u);
-
-    do_read(u);
-}
-
-static pa_usec_t source_get_latency_cb(struct pa_source *s) {
-    struct userdata *u = s->userdata;
-    snd_pcm_sframes_t frames;
-    assert(s && u && u->source);
-
-    if (snd_pcm_delay(u->pcm_handle, &frames) < 0) {
-        pa_log(__FILE__": failed to get delay\n");
-        s->get_latency = NULL;
-        return 0;
-    }
-
-    return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
-}
-
-int pa__init(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 period_size;
-    size_t frame_size;
-    
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments\n");
-        goto fail;
-    }
-
-    ss = c->default_sample_spec;
-    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
-        pa_log(__FILE__": failed to parse sample specification\n");
-        goto fail;
-    }
-    frame_size = pa_frame_size(&ss);
-    
-    periods = 12;
-    fragsize = 1024;
-    if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) {
-        pa_log(__FILE__": failed to parse buffer metrics\n");
-        goto fail;
-    }
-    period_size = fragsize;
-    
-    u = pa_xmalloc0(sizeof(struct userdata));
-    m->userdata = u;
-    u->module = m;
-    
-    if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0) {
-        pa_log(__FILE__": Error opening PCM device %s\n", dev);
-        goto fail;
-    }
-
-    if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size) < 0) {
-        pa_log(__FILE__": Failed to set hardware parameters\n");
-        goto fail;
-    }
-
-    u->source = pa_source_new(c, PA_TYPEID_ALSA, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss);
-    assert(u->source);
-
-    u->source->userdata = u;
-    u->source->get_latency = source_get_latency_cb;
-    pa_source_set_owner(u->source, m);
-    u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev);
-
-    if (pa_create_io_events(u->pcm_handle, c->mainloop, &u->io_events, &u->n_io_events, io_callback, u) < 0) {
-        pa_log(__FILE__": failed to obtain file descriptors\n");
-        goto fail;
-    }
-
-    u->frame_size = frame_size;
-    u->fragment_size = period_size;
-
-    pa_log(__FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size);
-
-    u->memchunk.memblock = NULL;
-    u->memchunk.index = u->memchunk.length = 0;
-
-    snd_pcm_start(u->pcm_handle);
-    
-    ret = 0;
-
-finish:
-     if (ma)
-         pa_modargs_free(ma);
-    
-    return ret;
-
-fail:
-    
-    if (u)
-        pa__done(c, m);
-
-    goto finish;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    assert(c && m);
-
-    if (!(u = m->userdata))
-        return;
-    
-    if (u->source) {
-        pa_source_disconnect(u->source);
-        pa_source_unref(u->source);
-    }
-    
-    if (u->io_events)
-        pa_free_io_events(c->mainloop, u->io_events, u->n_io_events);
-    
-    if (u->pcm_handle) {
-        snd_pcm_drop(u->pcm_handle);
-        snd_pcm_close(u->pcm_handle);
-    }
-    
-    if (u->memchunk.memblock)
-        pa_memblock_unref(u->memchunk.memblock);
-    
-    pa_xfree(u);
-}
-
diff --git a/polyp/module-cli.c b/polyp/module-cli.c
deleted file mode 100644 (file)
index 55fe8ad..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <unistd.h>
-
-#include "module.h"
-#include "iochannel.h"
-#include "cli.h"
-#include "sioman.h"
-#include "log.h"
-#include "module-cli-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Command line interface")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("No arguments")
-
-static void eof_cb(struct pa_cli*c, void *userdata) {
-    struct pa_module *m = userdata;
-    assert(c && m);
-
-    pa_module_unload_request(m);
-}
-
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct pa_iochannel *io;
-    assert(c && m);
-
-    if (m->argument) {
-        pa_log(__FILE__": module doesn't accept arguments.\n");
-        return -1;
-    }
-    
-    if (pa_stdio_acquire() < 0) {
-        pa_log(__FILE__": STDIN/STDUSE already in use.\n");
-        return -1;
-    }
-
-    io = pa_iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO);
-    assert(io);
-    pa_iochannel_set_noclose(io, 1);
-
-    m->userdata = pa_cli_new(c, io, m);
-    assert(m->userdata);
-
-    pa_cli_set_eof_callback(m->userdata, eof_cb, m);
-    
-    return 0;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    assert(c && m);
-
-    pa_cli_free(m->userdata);
-    pa_stdio_release();
-}
diff --git a/polyp/module-combine.c b/polyp/module-combine.c
deleted file mode 100644 (file)
index ca79d7d..0000000
+++ /dev/null
@@ -1,395 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdio.h>
-
-#include "module.h"
-#include "llist.h"
-#include "sink.h"
-#include "sink-input.h"
-#include "memblockq.h"
-#include "log.h"
-#include "util.h"
-#include "xmalloc.h"
-#include "modargs.h"
-#include "namereg.h"
-#include "module-combine-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Combine multiple sinks to one")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("sink_name=<name for the sink> master=<master sink> slaves=<slave sinks> adjust_time=<seconds> resample_method=<method>")
-
-#define PA_TYPEID_COMBINE PA_TYPEID_MAKE('C', 'M', 'B', 'N')
-
-#define DEFAULT_SINK_NAME "combined"
-#define MEMBLOCKQ_MAXLENGTH (1024*170)
-#define RENDER_SIZE (1024*10)
-
-#define DEFAULT_ADJUST_TIME 20
-
-static const char* const valid_modargs[] = {
-    "sink_name",
-    "master",
-    "slaves",
-    "adjust_time",
-    "resample_method",
-    NULL
-};
-
-struct output {
-    struct userdata *userdata;
-    struct pa_sink_input *sink_input;
-    size_t counter;
-    struct pa_memblockq *memblockq;
-    pa_usec_t total_latency;
-    PA_LLIST_FIELDS(struct output);
-};
-
-struct userdata {
-    struct pa_module *module;
-    struct pa_core *core;
-    struct pa_sink *sink;
-    unsigned n_outputs;
-    struct output *master;
-    struct pa_time_event *time_event;
-    uint32_t adjust_time;
-    
-    PA_LLIST_HEAD(struct output, outputs);
-};
-
-static void output_free(struct output *o);
-static void clear_up(struct userdata *u);
-
-static void update_usage(struct userdata *u) {
-    pa_module_set_used(u->module,
-                       (u->sink ? pa_idxset_ncontents(u->sink->inputs) : 0) +
-                       (u->sink ? pa_idxset_ncontents(u->sink->monitor_source->outputs) : 0));
-}
-
-
-static void adjust_rates(struct userdata *u) {
-    struct output *o;
-    pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency;
-    uint32_t base_rate;
-    assert(u && u->sink);
-
-    for (o = u->outputs; o; o = o->next) {
-        uint32_t sink_latency = o->sink_input->sink ? pa_sink_get_latency(o->sink_input->sink) : 0;
-        
-        o->total_latency = sink_latency + pa_sink_input_get_latency(o->sink_input);
-        
-        if (sink_latency > max_sink_latency)
-            max_sink_latency = sink_latency;
-
-        if (o->total_latency < min_total_latency)
-            min_total_latency = o->total_latency;
-    }
-
-    assert(min_total_latency != (pa_usec_t) -1);
-
-    target_latency = max_sink_latency > min_total_latency ? max_sink_latency : min_total_latency;
-    
-    pa_log_info(__FILE__": [%s] target latency is %0.0f usec.\n", u->sink->name, (float) target_latency);
-
-    base_rate = u->sink->sample_spec.rate;
-
-    for (o = u->outputs; o; o = o->next) {
-        uint32_t r = base_rate; 
-        
-        if (o->total_latency < target_latency)
-            r -= (uint32_t) (((((double) target_latency - o->total_latency))/u->adjust_time)*r/ 1000000);
-        else if (o->total_latency > target_latency)
-            r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/ 1000000);
-
-        if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1))
-            pa_log_warn(__FILE__": [%s] sample rates too different, not adjusting (%u vs. %u).\n", o->sink_input->name, base_rate, r);
-        else {
-            pa_log_info(__FILE__": [%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.\n", o->sink_input->name, r, (double) r / base_rate, (float) o->total_latency);
-            pa_sink_input_set_rate(o->sink_input, r);
-        }
-    }
-}
-
-static void request_memblock(struct userdata *u) {
-    struct pa_memchunk chunk;
-    struct output *o;
-    assert(u && u->sink);
-
-    update_usage(u);
-    
-    if (pa_sink_render(u->sink, RENDER_SIZE, &chunk) < 0)
-        return;
-
-    for (o = u->outputs; o; o = o->next)
-        pa_memblockq_push_align(o->memblockq, &chunk, 0);
-
-    pa_memblock_unref(chunk.memblock);
-}
-
-static void time_callback(struct pa_mainloop_api*a, struct pa_time_event* e, const struct timeval *tv, void *userdata) {
-    struct userdata *u = userdata;
-    struct timeval n;
-    assert(u && a && u->time_event == e);
-
-    adjust_rates(u);
-
-    gettimeofday(&n, NULL);
-    n.tv_sec += u->adjust_time;
-    u->sink->core->mainloop->time_restart(e, &n);
-}
-
-static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) {
-    struct output *o = i->userdata;
-    assert(i && o && o->sink_input && chunk);
-
-    if (pa_memblockq_peek(o->memblockq, chunk) >= 0)
-        return 0;
-    
-    /* Try harder */
-    request_memblock(o->userdata);
-    
-    return pa_memblockq_peek(o->memblockq, chunk);
-}
-
-static void sink_input_drop_cb(struct pa_sink_input *i, const struct pa_memchunk *chunk, size_t length) {
-    struct output *o = i->userdata;
-    assert(i && o && o->sink_input && chunk && length);
-
-    pa_memblockq_drop(o->memblockq, chunk, length);
-    o->counter += length;
-}
-
-static void sink_input_kill_cb(struct pa_sink_input *i) {
-    struct output *o = i->userdata;
-    assert(i && o && o->sink_input);
-    pa_module_unload_request(o->userdata->module);
-    clear_up(o->userdata);
-}
-
-static pa_usec_t sink_input_get_latency_cb(struct pa_sink_input *i) {
-    struct output *o = i->userdata;
-    assert(i && o && o->sink_input);
-    
-    return pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &i->sample_spec);
-}
-
-static pa_usec_t sink_get_latency_cb(struct pa_sink *s) {
-    struct userdata *u = s->userdata;
-    assert(s && u && u->sink && u->master);
-
-    return pa_sink_input_get_latency(u->master->sink_input);
-}
-
-static struct output *output_new(struct userdata *u, struct pa_sink *sink, int resample_method) {
-    struct output *o = NULL;
-    char t[256];
-    assert(u && sink && u->sink);
-    
-    o = pa_xmalloc(sizeof(struct output));
-    o->userdata = u;
-    
-    o->counter = 0;
-    o->memblockq = pa_memblockq_new(MEMBLOCKQ_MAXLENGTH, MEMBLOCKQ_MAXLENGTH, pa_frame_size(&u->sink->sample_spec), 0, 0, sink->core->memblock_stat);
-
-    snprintf(t, sizeof(t), "%s: output #%u", u->sink->name, u->n_outputs+1);
-    if (!(o->sink_input = pa_sink_input_new(sink, PA_TYPEID_COMBINE, t, &u->sink->sample_spec, 1, resample_method)))
-        goto fail;
-
-    o->sink_input->get_latency = sink_input_get_latency_cb;
-    o->sink_input->peek = sink_input_peek_cb;
-    o->sink_input->drop = sink_input_drop_cb;
-    o->sink_input->kill = sink_input_kill_cb;
-    o->sink_input->userdata = o;
-    o->sink_input->owner = u->module;
-    
-    PA_LLIST_PREPEND(struct output, u->outputs, o);
-    u->n_outputs++;
-    return o;
-
-fail:
-
-    if (o) {
-        if (o->sink_input) {
-            pa_sink_input_disconnect(o->sink_input);
-            pa_sink_input_unref(o->sink_input);
-        }
-
-        if (o->memblockq)
-            pa_memblockq_free(o->memblockq);
-        
-        pa_xfree(o);
-    }
-
-    return NULL;
-}
-
-static void output_free(struct output *o) {
-    assert(o);
-    PA_LLIST_REMOVE(struct output, o->userdata->outputs, o);
-    o->userdata->n_outputs--;
-    pa_memblockq_free(o->memblockq);
-    pa_sink_input_disconnect(o->sink_input);
-    pa_sink_input_unref(o->sink_input);
-    pa_xfree(o);
-}
-
-static void clear_up(struct userdata *u) {
-    struct output *o;
-    assert(u);
-    
-    if (u->time_event) {
-        u->core->mainloop->time_free(u->time_event);
-        u->time_event = NULL;
-    }
-    
-    while ((o = u->outputs))
-        output_free(o);
-
-    u->master = NULL;
-    
-    if (u->sink) {
-        pa_sink_disconnect(u->sink);
-        pa_sink_unref(u->sink);
-        u->sink = NULL;
-    }
-}
-
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    struct pa_modargs *ma = NULL;
-    const char *master_name, *slaves, *rm;
-    struct pa_sink *master_sink;
-    char *n = NULL;
-    const char*split_state;
-    struct timeval tv;
-    int resample_method = -1;
-    assert(c && m);
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments\n");
-        goto fail;
-    }
-
-    if ((rm = pa_modargs_get_value(ma, "resample_method", NULL))) {
-        if ((resample_method = pa_parse_resample_method(rm)) < 0) {
-            pa_log(__FILE__": invalid resample method '%s'\n", rm);
-            goto fail;
-        }
-    }
-    
-    u = pa_xmalloc(sizeof(struct userdata));
-    m->userdata = u;
-    u->sink = NULL;
-    u->n_outputs = 0;
-    u->master = NULL;
-    u->module = m;
-    u->core = c;
-    u->time_event = NULL;
-    u->adjust_time = DEFAULT_ADJUST_TIME;
-    PA_LLIST_HEAD_INIT(struct output, u->outputs);
-
-    if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) {
-        pa_log(__FILE__": failed to parse adjust_time value\n");
-        goto fail;
-    }
-    
-    if (!(master_name = pa_modargs_get_value(ma, "master", NULL)) || !(slaves = pa_modargs_get_value(ma, "slaves", NULL))) {
-        pa_log(__FILE__": no master or slave sinks specified\n");
-        goto fail;
-    }
-
-    if (!(master_sink = pa_namereg_get(c, master_name, PA_NAMEREG_SINK, 1))) {
-        pa_log(__FILE__": invalid master sink '%s'\n", master_name);
-        goto fail;
-    }
-
-    if (!(u->sink = pa_sink_new(c, PA_TYPEID_COMBINE, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &master_sink->sample_spec))) {
-        pa_log(__FILE__": failed to create sink\n");
-        goto fail;
-    }
-
-    pa_sink_set_owner(u->sink, m);
-    u->sink->description = pa_sprintf_malloc("Combined sink");
-    u->sink->get_latency = sink_get_latency_cb;
-    u->sink->userdata = u;
-    
-    if (!(u->master = output_new(u, master_sink, resample_method))) {
-        pa_log(__FILE__": failed to create master sink input on sink '%s'.\n", u->sink->name);
-        goto fail;
-    }
-    
-    split_state = NULL;
-    while ((n = pa_split(slaves, ",", &split_state))) {
-        struct pa_sink *slave_sink;
-        
-        if (!(slave_sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
-            pa_log(__FILE__": invalid slave sink '%s'\n", n);
-            goto fail;
-        }
-
-        pa_xfree(n);
-
-        if (!output_new(u, slave_sink, resample_method)) {
-            pa_log(__FILE__": failed to create slave sink input on sink '%s'.\n", slave_sink->name);
-            goto fail;
-        }
-    }
-           
-    if (u->n_outputs <= 1)
-        pa_log_warn(__FILE__": WARNING: no slave sinks specified.\n");
-
-    if (u->adjust_time > 0) {
-        gettimeofday(&tv, NULL);
-        tv.tv_sec += u->adjust_time;
-        u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u);
-    }
-    
-    pa_modargs_free(ma);
-    return 0;    
-
-fail:
-    pa_xfree(n);
-    
-    if (ma)
-        pa_modargs_free(ma);
-
-    pa__done(c, m);
-    return -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    assert(c && m);
-
-    if (!(u = m->userdata))
-        return;
-
-    clear_up(u);
-    pa_xfree(u);
-}
-
-
diff --git a/polyp/module-esound-sink.c b/polyp/module-esound-sink.c
deleted file mode 100644 (file)
index 82c38cc..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "iochannel.h"
-#include "sink.h"
-#include "module.h"
-#include "util.h"
-#include "modargs.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "module-esound-sink-symdef.h"
-#include "socket-client.h"
-#include "esound.h"
-#include "authkey.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Esound ")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("sink_name=<name for the sink> server=<address> cookie=<filename>  format=<sample format> channels=<number of channels> rate=<sample rate>")
-
-#define DEFAULT_SINK_NAME "esound_output"
-
-#define PA_TYPEID_ESOUND_SINK PA_TYPEID_MAKE('E', 'S', 'D', 'S')
-
-struct userdata {
-    struct pa_core *core;
-
-    struct pa_sink *sink;
-    struct pa_iochannel *io;
-    struct pa_socket_client *client;
-
-    struct pa_defer_event *defer_event;
-
-    struct pa_memchunk memchunk;
-    struct pa_module *module;
-
-    void *write_data;
-    size_t write_length, write_index;
-    
-    void *read_data;
-    size_t read_length, read_index;
-
-    enum { STATE_AUTH, STATE_LATENCY, STATE_RUNNING, STATE_DEAD } state;
-
-    pa_usec_t latency;
-
-    esd_format_t format;
-    int32_t rate;
-};
-
-static const char* const valid_modargs[] = {
-    "server",
-    "cookie",
-    "rate",
-    "format",
-    "channels",
-    "sink_name",
-    NULL
-};
-
-static void cancel(struct userdata *u) {
-    assert(u);
-
-    u->state = STATE_DEAD;
-
-    if (u->io) {
-        pa_iochannel_free(u->io);
-        u->io = NULL;
-    }
-
-    if (u->defer_event) {
-        u->core->mainloop->defer_free(u->defer_event);
-        u->defer_event = NULL;
-    }
-
-    if (u->sink) {
-        pa_sink_disconnect(u->sink);
-        pa_sink_unref(u->sink);
-        u->sink = NULL;
-    }
-
-    if (u->module) {
-        pa_module_unload_request(u->module);
-        u->module = NULL;
-    }
-}
-
-static int do_write(struct userdata *u) {
-    ssize_t r;
-    assert(u);
-
-    if (!pa_iochannel_is_writable(u->io))
-        return 0;
-
-    if (u->write_data) {
-        assert(u->write_index < u->write_length);
-
-        if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) <= 0) {
-            pa_log(__FILE__": write() failed: %s\n", strerror(errno));
-            return -1;
-        }
-
-        u->write_index += r;
-        assert(u->write_index <= u->write_length);
-        
-        if (u->write_index == u->write_length) {
-            free(u->write_data);
-            u->write_data = NULL;
-            u->write_index = u->write_length = 0;
-        }
-    } else if (u->state == STATE_RUNNING) {
-        pa_module_set_used(u->module, pa_idxset_ncontents(u->sink->inputs) + pa_idxset_ncontents(u->sink->monitor_source->outputs));
-        
-        if (!u->memchunk.length)
-            if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0)
-                return 0;
-
-        assert(u->memchunk.memblock && u->memchunk.length);
-        
-        if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
-            pa_log(__FILE__": write() failed: %s\n", strerror(errno));
-            return -1;
-        }
-
-        u->memchunk.index += r;
-        u->memchunk.length -= r;
-        
-        if (u->memchunk.length <= 0) {
-            pa_memblock_unref(u->memchunk.memblock);
-            u->memchunk.memblock = NULL;
-        }
-    }
-    
-    return 0;
-}
-
-static int handle_response(struct userdata *u) {
-    assert(u);
-
-    switch (u->state) {
-        case STATE_AUTH:
-            assert(u->read_length == sizeof(int32_t));
-
-            /* Process auth data */
-            if (!*(int32_t*) u->read_data) {
-                pa_log(__FILE__": Authentication failed: %s\n", strerror(errno));
-                return -1;
-            }
-
-            /* Request latency data */
-            assert(!u->write_data);
-            *(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY;
-
-            u->write_index = 0;
-            u->state = STATE_LATENCY;
-
-            /* Space for next response */
-            assert(u->read_length >= sizeof(int32_t));
-            u->read_index = 0;
-            u->read_length = sizeof(int32_t);
-            
-            break;
-
-        case STATE_LATENCY: {
-            int32_t *p;
-            assert(u->read_length == sizeof(int32_t));
-
-            /* Process latency info */
-            u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100);
-            if (u->latency > 10000000) {
-                pa_log(__FILE__": WARNING! Invalid latency information received from server\n");
-                u->latency = 0;
-            }
-
-            /* Create stream */
-            assert(!u->write_data);
-            p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX);
-            *(p++) = ESD_PROTO_STREAM_PLAY;
-            *(p++) = u->format;
-            *(p++) = u->rate;
-            pa_strlcpy((char*) p, "Polypaudio Tunnel", ESD_NAME_MAX);
-
-            u->write_index = 0;
-            u->state = STATE_RUNNING;
-
-            /* Don't read any further */
-            pa_xfree(u->read_data);
-            u->read_data = NULL;
-            u->read_index = u->read_length = 0;
-            
-            break;
-        }
-            
-        default:
-            abort();
-    }
-
-    return 0;
-}
-
-static int do_read(struct userdata *u) {
-    assert(u);
-    
-    if (!pa_iochannel_is_readable(u->io))
-        return 0;
-    
-    if (u->state == STATE_AUTH || u->state == STATE_LATENCY) {
-        ssize_t r;
-        
-        if (!u->read_data)
-            return 0;
-        
-        assert(u->read_index < u->read_length);
-        
-        if ((r = pa_iochannel_read(u->io, (uint8_t*) u->read_data + u->read_index, u->read_length - u->read_index)) <= 0) {
-            pa_log(__FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
-            cancel(u);
-            return -1;
-        }
-
-        u->read_index += r;
-        assert(u->read_index <= u->read_length);
-
-        if (u->read_index == u->read_length)
-            return handle_response(u);
-    }
-
-    return 0;
-}
-
-static void do_work(struct userdata *u) {
-    assert(u);
-
-    u->core->mainloop->defer_enable(u->defer_event, 0);
-    
-    if (do_read(u) < 0 || do_write(u) < 0)
-        cancel(u);
-}
-
-static void notify_cb(struct pa_sink*s) {
-    struct userdata *u = s->userdata;
-    assert(s && u);
-
-    if (pa_iochannel_is_writable(u->io))
-        u->core->mainloop->defer_enable(u->defer_event, 1);
-}
-
-static pa_usec_t get_latency_cb(struct pa_sink *s) {
-    struct userdata *u = s->userdata;
-    assert(s && u);
-
-    return
-        u->latency +
-        (u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0);
-}
-
-static void defer_callback(struct pa_mainloop_api *m, struct pa_defer_event*e, void *userdata) {
-    struct userdata *u = userdata;
-    assert(u);
-    do_work(u);
-}
-
-static void io_callback(struct pa_iochannel *io, void*userdata) {
-    struct userdata *u = userdata;
-    assert(u);
-    do_work(u);
-}
-
-static void on_connection(struct pa_socket_client *c, struct pa_iochannel*io, void *userdata) {
-    struct userdata *u = userdata;
-
-    pa_socket_client_unref(u->client);
-    u->client = NULL;
-    
-    if (!io) {
-        pa_log(__FILE__": connection failed: %s\n", strerror(errno));
-        cancel(u);
-        return;
-    }
-    
-    u->io = io;
-    pa_iochannel_set_callback(u->io, io_callback, u);
-}
-
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u = NULL;
-    const char *p;
-    struct pa_sample_spec ss;
-    struct pa_modargs *ma = NULL;
-    assert(c && m);
-    
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments\n");
-        goto fail;
-    }
-
-    ss = c->default_sample_spec;
-    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
-        pa_log(__FILE__": invalid sample format specification\n");
-        goto fail;
-    }
-
-    if ((ss.format != PA_SAMPLE_U8 && ss.format != PA_SAMPLE_S16NE) ||
-        (ss.channels > 2)) {
-        pa_log(__FILE__": esound sample type support is limited to mono/stereo and U8 or S16NE sample data\n");
-        goto fail;
-    }
-        
-    u = pa_xmalloc0(sizeof(struct userdata));
-    u->core = c;
-    u->module = m;
-    m->userdata = u;
-    u->format =
-        (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) |
-        (ss.channels == 2 ? ESD_STEREO : ESD_MONO);
-    u->rate = ss.rate;
-    u->sink = NULL;
-    u->client = NULL;
-    u->io = NULL;
-    u->read_data = u->write_data = NULL;
-    u->read_index = u->write_index = u->read_length = u->write_length = 0;
-    u->state = STATE_AUTH;
-    u->latency = 0;
-
-    if (!(u->sink = pa_sink_new(c, PA_TYPEID_ESOUND_SINK, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss))) {
-        pa_log(__FILE__": failed to create sink.\n");
-        goto fail;
-    }
-
-    if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", ESD_UNIX_SOCKET_NAME), ESD_DEFAULT_PORT))) {
-        pa_log(__FILE__": failed to connect to server.\n");
-        goto fail;
-    }
-    pa_socket_client_set_callback(u->client, on_connection, u);
-
-    /* Prepare the initial request */
-    u->write_data = pa_xmalloc(u->write_length = ESD_KEY_LEN + sizeof(int32_t));
-    if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", ".esd_auth"), u->write_data, ESD_KEY_LEN) < 0) {
-        pa_log(__FILE__": failed to load cookie\n");
-        goto fail;
-    }
-    *(int32_t*) ((uint8_t*) u->write_data + ESD_KEY_LEN) = ESD_ENDIAN_KEY;
-
-    /* Reserve space for the response */
-    u->read_data = pa_xmalloc(u->read_length = sizeof(int32_t));
-    
-    u->sink->notify = notify_cb;
-    u->sink->get_latency = get_latency_cb;
-    u->sink->userdata = u;
-    pa_sink_set_owner(u->sink, m);
-    u->sink->description = pa_sprintf_malloc("Esound sink '%s'", p);
-
-    u->memchunk.memblock = NULL;
-    u->memchunk.length = 0;
-
-    u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u);
-    c->mainloop->defer_enable(u->defer_event, 0);
-
-    
-    pa_modargs_free(ma);
-    
-    return 0;
-
-fail:
-    if (ma)
-        pa_modargs_free(ma);
-        
-    pa__done(c, m);
-
-    return -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    assert(c && m);
-
-    if (!(u = m->userdata))
-        return;
-
-    u->module = NULL;
-    cancel(u);
-    
-    if (u->memchunk.memblock)
-        pa_memblock_unref(u->memchunk.memblock);
-
-    if (u->client)
-        pa_socket_client_unref(u->client);
-    
-    pa_xfree(u->read_data);
-    pa_xfree(u->write_data);
-
-    pa_xfree(u);
-}
-
-
-
diff --git a/polyp/module-lirc.c b/polyp/module-lirc.c
deleted file mode 100644 (file)
index 4cfc09d..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <unistd.h>
-#include <string.h>
-#include <lirc/lirc_client.h>
-#include <stdlib.h>
-
-#include "module.h"
-#include "log.h"
-#include "module-lirc-symdef.h"
-#include "namereg.h"
-#include "sink.h"
-#include "xmalloc.h"
-#include "modargs.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("LIRC volume control")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("config=<config file> sink=<sink name> appname=<lirc application name>")
-
-static const char* const valid_modargs[] = {
-    "config",
-    "sink",
-    "appname",
-    NULL,
-};
-
-struct userdata {
-    int lirc_fd;
-    struct pa_io_event *io;
-    struct lirc_config *config;
-    char *sink_name;
-    struct pa_module *module;
-    float mute_toggle_save;
-};
-
-static int lirc_in_use = 0;
-
-static void io_callback(struct pa_mainloop_api *io, struct pa_io_event *e, int fd, enum pa_io_event_flags events, void*userdata) {
-    struct userdata *u = userdata;
-    char *name = NULL, *code = NULL;
-    assert(io);
-    assert(u);
-
-    if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
-        pa_log(__FILE__": lost connection to LIRC daemon.\n");
-        goto fail;
-    }
-        
-    if (events & PA_IO_EVENT_INPUT) {
-        char *c;
-        
-        if (lirc_nextcode(&code) != 0 || !code) {
-            pa_log(__FILE__": lirc_nextcode() failed.\n");
-            goto fail;
-        }
-        
-        c = pa_xstrdup(code);
-        c[strcspn(c, "\n\r")] = 0;
-        pa_log_debug(__FILE__": raw IR code '%s'\n", c);
-        pa_xfree(c);
-        
-        while (lirc_code2char(u->config, code, &name) == 0 && name) {
-            enum { INVALID, UP, DOWN, MUTE, RESET, MUTE_TOGGLE } volchange = INVALID;
-            
-            pa_log_info(__FILE__": translated IR code '%s'\n", name);
-            
-            if (strcasecmp(name, "volume-up") == 0)
-                volchange = UP;
-            else if (strcasecmp(name, "volume-down") == 0)
-                volchange = DOWN;
-            else if (strcasecmp(name, "mute") == 0)
-                volchange = MUTE;
-            else if (strcasecmp(name, "mute-toggle") == 0)
-                volchange = MUTE_TOGGLE;
-            else if (strcasecmp(name, "reset") == 0)
-                volchange = RESET;
-            
-            if (volchange == INVALID)
-                pa_log_warn(__FILE__": recieved unknown IR code '%s'\n", name);
-            else {
-                struct pa_sink *s;
-                
-                if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
-                    pa_log(__FILE__": failed to get sink '%s'\n", u->sink_name);
-                else {
-                    double v = pa_volume_to_user(s->volume);
-                    
-                    switch (volchange) {
-                        case UP:       v += .05; break;
-                        case DOWN:     v -= .05; break;
-                        case MUTE:     v  =  0; break;
-                        case RESET:    v  =  1; break;
-                        case MUTE_TOGGLE: {
-
-                            if (v > 0) {
-                                u->mute_toggle_save = v;
-                                v = 0;
-                            } else
-                                v = u->mute_toggle_save;
-                        }
-                        default:
-                            ;
-                    }
-                    
-                    pa_sink_set_volume(s, pa_volume_from_user(v));
-                }
-            }
-        }
-    }
-
-    free(code);
-
-    return;
-    
-fail:
-    u->module->core->mainloop->io_free(u->io);
-    u->io = NULL;
-
-    pa_module_unload_request(u->module);
-
-    free(code);
-}
-    
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct pa_modargs *ma = NULL;
-    struct userdata *u;
-    assert(c && m);
-
-    if (lirc_in_use) {
-        pa_log(__FILE__": module-lirc may no be loaded twice.\n");
-        return -1;
-    }
-    
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": Failed to parse module arguments\n");
-        goto fail;
-    }
-
-    m->userdata = u = pa_xmalloc(sizeof(struct userdata));
-    u->module = m;
-    u->io = NULL;
-    u->config = NULL;
-    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
-    u->lirc_fd = -1;
-    u->mute_toggle_save = 0;
-
-    if ((u->lirc_fd = lirc_init((char*) pa_modargs_get_value(ma, "appname", "polypaudio"), 1)) < 0) {
-        pa_log(__FILE__": lirc_init() failed.\n");
-        goto fail;
-    }
-
-    if (lirc_readconfig((char*) pa_modargs_get_value(ma, "config", NULL), &u->config, NULL) < 0) {
-        pa_log(__FILE__": lirc_readconfig() failed.\n");
-        goto fail;
-    }
-    
-    u->io = c->mainloop->io_new(c->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
-
-    lirc_in_use = 1;
-
-    pa_modargs_free(ma);
-    
-    return 0;
-
-fail:
-
-    if (ma)
-        pa_modargs_free(ma);
-
-    pa__done(c, m);
-    return -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    assert(c);
-    assert(m);
-
-    if (!(u = m->userdata))
-        return;
-
-    if (u->io)
-        m->core->mainloop->io_free(u->io);
-
-    if (u->config)
-        lirc_freeconfig(u->config);
-
-    if (u->lirc_fd >= 0)
-        lirc_deinit();
-
-    pa_xfree(u->sink_name);
-    pa_xfree(u);
-
-    lirc_in_use = 0;
-}
diff --git a/polyp/module-mmkbd-evdev.c b/polyp/module-mmkbd-evdev.c
deleted file mode 100644 (file)
index 758aaae..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include <linux/input.h>
-
-#include "module.h"
-#include "log.h"
-#include "module-mmkbd-evdev-symdef.h"
-#include "namereg.h"
-#include "sink.h"
-#include "xmalloc.h"
-#include "modargs.h"
-#include "util.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Multimedia keyboard support via Linux evdev")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("device=<evdev device> sink=<sink name>")
-
-#define DEFAULT_DEVICE "/dev/input/event0"
-
-static const char* const valid_modargs[] = {
-    "device",
-    "sink",
-    NULL,
-};
-
-struct userdata {
-    int fd;
-    struct pa_io_event *io;
-    char *sink_name;
-    struct pa_module *module;
-    float mute_toggle_save;
-};
-
-static void io_callback(struct pa_mainloop_api *io, struct pa_io_event *e, int fd, enum pa_io_event_flags events, void*userdata) {
-    struct userdata *u = userdata;
-    assert(io);
-    assert(u);
-
-    if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
-        pa_log(__FILE__": lost connection to evdev device.\n");
-        goto fail;
-    }
-        
-    if (events & PA_IO_EVENT_INPUT) {
-        struct input_event e;
-
-        if (pa_loop_read(u->fd, &e, sizeof(e)) <= 0) {
-            pa_log(__FILE__": failed to read from event device: %s\n", strerror(errno));
-            goto fail;
-        }
-
-        if (e.type == EV_KEY && (e.value == 1 || e.value == 2)) {
-            enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID;
-
-            pa_log_debug(__FILE__": key code=%u, value=%u\n", e.code, e.value);
-
-            switch (e.code) {
-                case KEY_VOLUMEDOWN:  volchange = DOWN; break;
-                case KEY_VOLUMEUP:    volchange = UP; break;
-                case KEY_MUTE:        volchange = MUTE_TOGGLE; break;
-            }
-
-            if (volchange != INVALID) {
-                struct pa_sink *s;
-                
-                if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
-                    pa_log(__FILE__": failed to get sink '%s'\n", u->sink_name);
-                else {
-                    double v = pa_volume_to_user(s->volume);
-                    
-                    switch (volchange) {
-                        case UP:       v += .05; break;
-                        case DOWN:     v -= .05; break;
-                        case MUTE_TOGGLE: {
-
-                            if (v > 0) {
-                                u->mute_toggle_save = v;
-                                v = 0;
-                            } else
-                                v = u->mute_toggle_save;
-                        }
-                        default:
-                            ;
-                    }
-                    
-                    pa_sink_set_volume(s, pa_volume_from_user(v));
-                }
-            }
-        }
-    }
-
-    return;
-    
-fail:
-    u->module->core->mainloop->io_free(u->io);
-    u->io = NULL;
-
-    pa_module_unload_request(u->module);
-}
-
-#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
-    
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct pa_modargs *ma = NULL;
-    struct userdata *u;
-    int version;
-    struct input_id input_id;
-    char name[256];
-    uint8_t evtype_bitmask[EV_MAX/8 + 1];
-    assert(c && m);
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": Failed to parse module arguments\n");
-        goto fail;
-    }
-
-    m->userdata = u = pa_xmalloc(sizeof(struct userdata));
-    u->module = m;
-    u->io = NULL;
-    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
-    u->fd = -1;
-    u->mute_toggle_save = 0;
-
-    if ((u->fd = open(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), O_RDONLY)) < 0) {
-        pa_log(__FILE__": failed to open evdev device: %s\n", strerror(errno));
-        goto fail;
-    }
-
-    if (ioctl(u->fd, EVIOCGVERSION, &version) < 0) {
-        pa_log(__FILE__": EVIOCGVERSION failed: %s\n", strerror(errno));
-        goto fail;
-    }
-
-    pa_log_info(__FILE__": evdev driver version %i.%i.%i\n", version >> 16, (version >> 8) & 0xff, version & 0xff);
-
-    if(ioctl(u->fd, EVIOCGID, &input_id)) {
-        pa_log(__FILE__": EVIOCGID failed: %s\n", strerror(errno));
-        goto fail;
-    }
-
-    pa_log_info(__FILE__": evdev vendor 0x%04hx product 0x%04hx version 0x%04hx bustype %u\n",
-                input_id.vendor, input_id.product, input_id.version, input_id.bustype);
-
-    if(ioctl(u->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
-        pa_log(__FILE__": EVIOCGNAME failed: %s\n", strerror(errno));
-        goto fail;
-    }
-
-    pa_log_info(__FILE__": evdev device name: %s\n", name);
-
-    memset(evtype_bitmask, 0, sizeof(evtype_bitmask));
-    if (ioctl(u->fd, EVIOCGBIT(0, EV_MAX), evtype_bitmask) < 0) {
-        pa_log(__FILE__": EVIOCGBIT failed: %s\n", strerror(errno));
-        goto fail;
-    }
-
-    if (!test_bit(EV_KEY, evtype_bitmask)) {
-        pa_log(__FILE__": device has no keys.\n");
-        goto fail;
-    }
-
-    u->io = c->mainloop->io_new(c->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
-
-    pa_modargs_free(ma);
-    
-    return 0;
-
-fail:
-
-    if (ma)
-        pa_modargs_free(ma);
-
-    pa__done(c, m);
-    return -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    assert(c);
-    assert(m);
-
-    if (!(u = m->userdata))
-        return;
-
-    if (u->io)
-        m->core->mainloop->io_free(u->io);
-
-    if (u->fd >= 0)
-        close(u->fd);
-
-    pa_xfree(u->sink_name);
-    pa_xfree(u);
-}
diff --git a/polyp/module-null-sink.c b/polyp/module-null-sink.c
deleted file mode 100644 (file)
index fcac1ea..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "iochannel.h"
-#include "sink.h"
-#include "module.h"
-#include "util.h"
-#include "modargs.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "module-null-sink-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Clocked NULL sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("format=<sample format> channels=<number of channels> rate=<sample rate> sink_name=<name of sink>")
-
-#define DEFAULT_SINK_NAME "null"
-
-#define PA_TYPEID_NULL PA_TYPEID_MAKE('N', 'U', 'L', 'L')
-
-struct userdata {
-    struct pa_core *core;
-    struct pa_module *module;
-    struct pa_sink *sink;
-    struct pa_time_event *time_event;
-    size_t block_size;
-};
-
-static const char* const valid_modargs[] = {
-    "rate",
-    "format",
-    "channels",
-    "sink_name",
-    NULL
-};
-
-static void time_callback(struct pa_mainloop_api *m, struct pa_time_event*e, const struct timeval *tv, void *userdata) {
-    struct userdata *u = userdata;
-    struct pa_memchunk chunk;
-    struct timeval ntv = *tv;
-    size_t l;
-
-    assert(u);
-
-    if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) {
-        l = chunk.length;
-        pa_memblock_unref(chunk.memblock);
-    } else
-        l = u->block_size;
-
-    pa_timeval_add(&ntv, pa_bytes_to_usec(l, &u->sink->sample_spec));
-    m->time_restart(e, &ntv);
-}
-
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u = NULL;
-    struct pa_sample_spec ss;
-    struct pa_modargs *ma = NULL;
-    struct timeval tv;
-    assert(c && m);
-    
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments.\n");
-        goto fail;
-    }
-
-    ss = c->default_sample_spec;
-    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
-        pa_log(__FILE__": invalid sample format specification.\n");
-        goto fail;
-    }
-    
-    u = pa_xmalloc0(sizeof(struct userdata));
-    u->core = c;
-    u->module = m;
-    m->userdata = u;
-    
-    if (!(u->sink = pa_sink_new(c, PA_TYPEID_NULL, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss))) {
-        pa_log(__FILE__": failed to create sink.\n");
-        goto fail;
-    }
-    
-    u->sink->userdata = u;
-    pa_sink_set_owner(u->sink, m);
-    u->sink->description = pa_sprintf_malloc("NULL sink");
-
-    gettimeofday(&tv, NULL);
-    u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u);
-
-    u->block_size = pa_bytes_per_second(&ss) / 10;
-    
-    pa_modargs_free(ma);
-    
-    return 0;
-
-fail:
-    if (ma)
-        pa_modargs_free(ma);
-        
-    pa__done(c, m);
-
-    return -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    assert(c && m);
-
-    if (!(u = m->userdata))
-        return;
-    
-    pa_sink_disconnect(u->sink);
-    pa_sink_unref(u->sink);
-
-    u->core->mainloop->time_free(u->time_event);
-
-    pa_xfree(u);
-}
diff --git a/polyp/module-oss-mmap.c b/polyp/module-oss-mmap.c
deleted file mode 100644 (file)
index 3c5c0ad..0000000
+++ /dev/null
@@ -1,427 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <sys/soundcard.h>
-#include <sys/ioctl.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <sys/mman.h>
-
-#include "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"
-#include "xmalloc.h"
-#include "log.h"
-#include "module-oss-mmap-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("OSS Sink/Source (mmap)")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("sink_name=<name for the sink> source_name=<name for the source> device=<OSS device> record=<enable source?> playback=<enable sink?> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size>")
-
-#define PA_TYPEID_OSS_MMAP PA_TYPEID_MAKE('O', 'S', 'S', 'M')
-
-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;
-
-    struct pa_io_event *io_event;
-
-    struct pa_memblock **in_memblocks, **out_memblocks;
-    unsigned out_current, in_current;
-    struct pa_module *module;
-};
-
-static const char* const valid_modargs[] = {
-    "sink_name",
-    "source_name",
-    "device",
-    "record",
-    "playback",
-    "fragments",
-    "fragment_size",
-    "format",
-    "rate",
-    "channels",
-    NULL
-};
-
-#define DEFAULT_SINK_NAME "oss_output"
-#define DEFAULT_SOURCE_NAME "oss_input"
-#define DEFAULT_DEVICE "/dev/dsp"
-
-static void update_usage(struct userdata *u) {
-   pa_module_set_used(u->module,
-                      (u->sink ? pa_idxset_ncontents(u->sink->inputs) : 0) +
-                      (u->sink ? pa_idxset_ncontents(u->sink->monitor_source->outputs) : 0) +
-                      (u->source ? pa_idxset_ncontents(u->source->outputs) : 0));
-}
-
-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((uint8_t*)u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size, 1, u->core->memblock_stat);
-        assert(chunk.memblock);
-        chunk.length = chunk.memblock->length;
-        chunk.index = 0;
-        
-        pa_sink_render_into_full(u->sink, &chunk);
-            
-        u->out_current++;
-        while (u->out_current >= u->out_fragments)
-            u->out_current -= u->out_fragments;
-        
-        n--;
-    }
-}
-
-static void do_write(struct userdata *u) {
-    struct count_info info;
-    assert(u && u->sink);
-
-    update_usage(u);
-    
-    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
-        pa_log(__FILE__": SNDCTL_DSP_GETOPTR: %s\n", strerror(errno));
-        return;
-    }
-
-    u->out_fill = (u->out_fragment_size * u->out_fragments) - (info.ptr % u->out_fragment_size);
-
-    if (!info.blocks)
-        return;
-    
-    out_fill_memblocks(u, info.blocks);
-}
-
-static void in_post_memblocks(struct userdata *u, unsigned n) {
-    assert(u && u->in_memblocks);
-    
-    while (n > 0) {
-        struct pa_memchunk chunk;
-        
-        if (!u->in_memblocks[u->in_current]) {
-            chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed((uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1, u->core->memblock_stat);
-            chunk.length = chunk.memblock->length;
-            chunk.index = 0;
-            
-            pa_source_post(u->source, &chunk);
-        }
-
-        u->in_current++;
-        while (u->in_current >= u->in_fragments)
-            u->in_current -= u->in_fragments;
-        
-        n--;
-    }
-}
-
-static void in_clear_memblocks(struct userdata*u, unsigned n) {
-    unsigned i = u->in_current;
-    assert(u && u->in_memblocks);
-
-    if (n > u->in_fragments)
-        n = u->in_fragments;
-    
-    while (n > 0) {
-        if (u->in_memblocks[i]) {
-            pa_memblock_unref_fixed(u->in_memblocks[i]);
-            u->in_memblocks[i] = NULL;
-        }
-
-        i++;
-        while (i >= u->in_fragments)
-            i -= u->in_fragments;
-
-        n--;
-    }
-}
-
-static void do_read(struct userdata *u) {
-    struct count_info info;
-    assert(u && u->source);
-
-    update_usage(u);
-    
-    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
-        pa_log(__FILE__": SNDCTL_DSP_GETIPTR: %s\n", strerror(errno));
-        return;
-    }
-
-    if (!info.blocks)
-        return;
-    
-    in_post_memblocks(u, info.blocks);
-    in_clear_memblocks(u, u->in_fragments/2);
-}
-
-static void io_callback(struct pa_mainloop_api *m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    struct userdata *u = userdata;
-    assert (u && u->core->mainloop == m && u->io_event == e);
-
-    if (f & PA_IO_EVENT_INPUT)
-        do_read(u);
-    if (f & PA_IO_EVENT_OUTPUT)
-        do_write(u);
-}
-
-static pa_usec_t sink_get_latency_cb(struct pa_sink *s) {
-    struct userdata *u = s->userdata;
-    assert(s && u);
-
-    do_write(u);
-    return pa_bytes_to_usec(u->out_fill, &s->sample_spec);
-}
-
-int pa__init(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 = pa_xmalloc0(sizeof(struct userdata));
-    u->module = m;
-    u->fd = -1;
-    u->core = c;
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments.\n");
-        goto fail;
-    }
-    
-    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
-        pa_log(__FILE__": record= and playback= expect numeric arguments.\n");
-        goto fail;
-    }
-
-    if (!playback && !record) {
-        pa_log(__FILE__": neither playback nor record enabled for device.\n");
-        goto fail;
-    }
-
-    mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
-
-    nfrags = 12;
-    frag_size = 1024;
-    if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
-        pa_log(__FILE__": failed to parse fragments arguments\n");
-        goto fail;
-    }
-
-    u->sample_spec = c->default_sample_spec;
-    if (pa_modargs_get_sample_spec(ma, &u->sample_spec) < 0) {
-        pa_log(__FILE__": failed to parse sample specification\n");
-        goto fail;
-    }
-
-    if ((u->fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
-        goto fail;
-
-    if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_REALTIME) || !(caps & DSP_CAP_TRIGGER)) {
-        pa_log(__FILE__": OSS device not mmap capable.\n");
-        goto fail;
-    }
-
-    pa_log(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
-
-    if (nfrags >= 2 && frag_size >= 1)
-        if (pa_oss_set_fragments(u->fd, nfrags, frag_size) < 0)
-            goto fail;
-    
-    if (pa_oss_auto_format(u->fd, &u->sample_spec) < 0)
-        goto fail;
-
-    if (mode != O_WRONLY) {
-        if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
-            pa_log(__FILE__": SNDCTL_DSP_GETISPACE: %s\n", strerror(errno));
-            goto fail;
-        }
-
-        pa_log_info(__FILE__": input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
-        u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal);
-
-        if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
-            if (mode == O_RDWR) {
-                pa_log(__FILE__": mmap failed for input. Changing to O_WRONLY mode.\n");
-                mode = O_WRONLY;
-            } else {
-                pa_log(__FILE__": mmap(): %s\n", strerror(errno));
-                goto fail;
-            }
-        } else {
-        
-            u->source = pa_source_new(c, PA_TYPEID_OSS_MMAP, 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 = pa_xmalloc0(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) {
-            pa_log(__FILE__": SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno));
-            goto fail;
-        }
-        
-        pa_log_info(__FILE__": output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
-        u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal);
-
-        if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0))  == MAP_FAILED) {
-            if (mode == O_RDWR) {
-                pa_log(__FILE__": mmap filed for output. Changing to O_RDONLY mode.\n");
-                mode = O_RDONLY;
-            } else {
-                pa_log(__FILE__": mmap(): %s\n", strerror(errno));
-                goto fail;
-            }
-        } else {
-            pa_silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec);
-            
-            u->sink = pa_sink_new(c, PA_TYPEID_OSS_MMAP, 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 = pa_xmalloc0(sizeof(struct memblock *)*u->out_fragments);
-            
-            enable_bits |= PCM_ENABLE_OUTPUT;
-        }
-    }
-
-    zero = 0;
-    if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) {
-        pa_log(__FILE__": SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
-        goto fail;
-    }
-    
-    if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) {
-        pa_log(__FILE__": SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
-        goto fail;
-    }
-        
-    assert(u->source || u->sink);
-
-    u->io_event = c->mainloop->io_new(c->mainloop, u->fd, (u->source ? PA_IO_EVENT_INPUT : 0) | (u->sink ? PA_IO_EVENT_OUTPUT : 0), io_callback, u);
-    assert(u->io_event);
-
-    pa_modargs_free(ma);
-    
-    return 0;
-
-fail:
-    pa__done(c, m);
-
-    if (ma)
-        pa_modargs_free(ma);
-    
-    return -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    assert(c && m);
-
-    if (!(u = m->userdata))
-        return;
-
-    if (u->out_memblocks) {
-        unsigned i;
-        for (i = 0; i < u->out_fragments; i++)
-            if (u->out_memblocks[i])
-                pa_memblock_unref_fixed(u->out_memblocks[i]);
-        pa_xfree(u->out_memblocks);
-    }
-
-    if (u->in_memblocks) {
-        unsigned i;
-        for (i = 0; i < u->in_fragments; i++)
-            if (u->in_memblocks[i])
-                pa_memblock_unref_fixed(u->in_memblocks[i]);
-        pa_xfree(u->in_memblocks);
-    }
-    
-    if (u->in_mmap && u->in_mmap != MAP_FAILED)
-        munmap(u->in_mmap, u->in_mmap_length);
-    
-    if (u->out_mmap && u->out_mmap != MAP_FAILED)
-        munmap(u->out_mmap, u->out_mmap_length);
-    
-    if (u->sink) {
-        pa_sink_disconnect(u->sink);
-        pa_sink_unref(u->sink);
-    }
-
-    if (u->source) {
-        pa_source_disconnect(u->source);
-        pa_source_unref(u->source);
-    }
-
-    if (u->io_event)
-        u->core->mainloop->io_free(u->io_event);
-
-    if (u->fd >= 0)
-        close(u->fd);
-
-    pa_xfree(u);
-}
diff --git a/polyp/module-oss.c b/polyp/module-oss.c
deleted file mode 100644 (file)
index 02acc6f..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <sys/soundcard.h>
-#include <sys/ioctl.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "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"
-#include "xmalloc.h"
-#include "log.h"
-#include "module-oss-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("OSS Sink/Source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("sink_name=<name for the sink> source_name=<name for the source> device=<OSS device> record=<enable source?> playback=<enable sink?> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size>")
-
-#define PA_TYPEID_OSS PA_TYPEID_MAKE('O', 'S', 'S', '_')
-
-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 use_getospace, use_getispace;
-
-    int fd;
-    struct pa_module *module;
-};
-
-static const char* const valid_modargs[] = {
-    "sink_name",
-    "source_name",
-    "device",
-    "record",
-    "playback",
-    "fragments",
-    "fragment_size",
-    "format",
-    "rate",
-    "channels",
-    NULL
-};
-
-#define DEFAULT_SINK_NAME "oss_output"
-#define DEFAULT_SOURCE_NAME "oss_input"
-#define DEFAULT_DEVICE "/dev/dsp"
-
-static void update_usage(struct userdata *u) {
-   pa_module_set_used(u->module,
-                      (u->sink ? pa_idxset_ncontents(u->sink->inputs) : 0) +
-                      (u->sink ? pa_idxset_ncontents(u->sink->monitor_source->outputs) : 0) +
-                      (u->source ? pa_idxset_ncontents(u->source->outputs) : 0));
-}
-
-static void do_write(struct userdata *u) {
-    struct pa_memchunk *memchunk;
-    ssize_t r;
-    size_t l;
-    int loop = 0;
-    
-    assert(u);
-
-    if (!u->sink || !pa_iochannel_is_writable(u->io))
-        return;
-
-    update_usage(u);
-
-    l = u->out_fragment_size;
-    
-    if (u->use_getospace) {
-        audio_buf_info info;
-        
-        if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
-            u->use_getospace = 0;
-        else {
-            if (info.bytes/l > 0) {
-                l = (info.bytes/l)*l;
-                loop = 1;
-            }
-        }
-    }
-
-    do {
-        memchunk = &u->memchunk;
-        
-        if (!memchunk->length)
-            if (pa_sink_render(u->sink, l, memchunk) < 0)
-                memchunk = &u->silence;
-        
-        assert(memchunk->memblock);
-        assert(memchunk->memblock->data);
-        assert(memchunk->length);
-        
-        if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) {
-            pa_log(__FILE__": write() failed: %s\n", strerror(errno));
-            break;
-        }
-        
-        if (memchunk == &u->silence)
-            assert(r % u->sample_size == 0);
-        else {
-            u->memchunk.index += r;
-            u->memchunk.length -= r;
-            
-            if (u->memchunk.length <= 0) {
-                pa_memblock_unref(u->memchunk.memblock);
-                u->memchunk.memblock = NULL;
-            }
-        }
-
-        l = l > (size_t) r ? l - r : 0;
-    } while (loop && l > 0);
-}
-
-static void do_read(struct userdata *u) {
-    struct pa_memchunk memchunk;
-    ssize_t r;
-    size_t l;
-    int loop = 0;
-    assert(u);
-    
-    if (!u->source || !pa_iochannel_is_readable(u->io))
-        return;
-
-    update_usage(u);
-
-    l = u->in_fragment_size;
-
-    if (u->use_getispace) {
-        audio_buf_info info;
-        
-        if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0)
-            u->use_getispace = 0;
-        else {
-            if (info.bytes/l > 0) {
-                l = (info.bytes/l)*l;
-                loop = 1;
-            }
-        }
-    }
-    
-    do {
-        memchunk.memblock = pa_memblock_new(l, u->core->memblock_stat);
-        assert(memchunk.memblock);
-        if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
-            pa_memblock_unref(memchunk.memblock);
-            if (errno != EAGAIN)
-                pa_log(__FILE__": read() failed: %s\n", strerror(errno));
-            break;
-        }
-        
-        assert(r <= (ssize_t) memchunk.memblock->length);
-        memchunk.length = memchunk.memblock->length = r;
-        memchunk.index = 0;
-        
-        pa_source_post(u->source, &memchunk);
-        pa_memblock_unref(memchunk.memblock);
-
-        l = l > (size_t) r ? l - r : 0;
-    } while (loop && l > 0);
-}
-
-static void io_callback(struct pa_iochannel *io, void*userdata) {
-    struct userdata *u = userdata;
-    assert(u);
-    do_write(u);
-    do_read(u);
-}
-
-static pa_usec_t sink_get_latency_cb(struct pa_sink *s) {
-    pa_usec_t r = 0;
-    int arg;
-    struct userdata *u = s->userdata;
-    assert(s && u && u->sink);
-
-    if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
-        pa_log_info(__FILE__": device doesn't support SNDCTL_DSP_GETODELAY.\n");
-        s->get_latency = NULL;
-        return 0;
-    }
-
-    r += pa_bytes_to_usec(arg, &s->sample_spec);
-
-    if (u->memchunk.memblock)
-        r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec);
-
-    return r;
-}
-
-static pa_usec_t source_get_latency_cb(struct pa_source *s) {
-    struct userdata *u = s->userdata;
-    audio_buf_info info;
-    assert(s && u && u->source);
-
-    if (!u->use_getispace)
-        return 0;
-    
-    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
-        u->use_getispace = 0;
-        return 0;
-    }
-    
-    if (info.bytes <= 0)
-        return 0;
-
-    return pa_bytes_to_usec(info.bytes, &s->sample_spec);
-}
-
-int pa__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;
-    int 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))) {
-        pa_log(__FILE__": failed to parse module arguments.\n");
-        goto fail;
-    }
-    
-    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
-        pa_log(__FILE__": record= and playback= expect numeric argument.\n");
-        goto fail;
-    }
-
-    if (!playback && !record) {
-        pa_log(__FILE__": neither playback nor record enabled for device.\n");
-        goto fail;
-    }
-
-    mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
-    
-    nfrags = 12;
-    frag_size = 1024;
-    if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
-        pa_log(__FILE__": failed to parse fragments arguments\n");
-        goto fail;
-    }
-
-    ss = c->default_sample_spec;
-    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
-        pa_log(__FILE__": failed to parse sample specification\n");
-        goto fail;
-    }
-    
-    if ((fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, NULL)) < 0)
-        goto fail;
-
-    pa_log_info(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
-
-    if (nfrags >= 2 && frag_size >= 1)
-        if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)   
-            goto fail;   
-
-    if (pa_oss_auto_format(fd, &ss) < 0)
-        goto fail;
-
-    if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
-        pa_log(__FILE__": SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno));
-        goto fail;
-    }
-    assert(frag_size);
-    in_frag_size = out_frag_size = frag_size;
-
-    u = pa_xmalloc(sizeof(struct userdata));
-    u->core = c;
-    u->use_getospace = u->use_getispace = 0;
-    
-    if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
-        pa_log_info(__FILE__": input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
-        in_frag_size = info.fragsize;
-        u->use_getispace = 1;
-    }
-
-    if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
-        pa_log_info(__FILE__": output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
-        out_frag_size = info.fragsize;
-        u->use_getospace = 1;
-    }
-
-    if (mode != O_WRONLY) {
-        u->source = pa_source_new(c, PA_TYPEID_OSS, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss);
-        assert(u->source);
-        u->source->userdata = u;
-        u->source->get_latency = source_get_latency_cb;
-        pa_source_set_owner(u->source, m);
-        u->source->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p);
-    } else
-        u->source = NULL;
-
-    if (mode != O_RDONLY) {
-        u->sink = pa_sink_new(c, PA_TYPEID_OSS, 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_frame_size(&ss);
-
-    u->out_fragment_size = out_frag_size;
-    u->in_fragment_size = in_frag_size;
-    u->silence.memblock = pa_memblock_new(u->silence.length = u->out_fragment_size, u->core->memblock_stat);
-    assert(u->silence.memblock);
-    pa_silence_memblock(u->silence.memblock, &ss);
-    u->silence.index = 0;
-
-    u->module = m;
-    m->userdata = u;
-
-    pa_modargs_free(ma);
-
-    return 0;
-
-fail:
-    if (fd >= 0)
-        close(fd);
-
-    if (ma)
-        pa_modargs_free(ma);
-    
-    return -1;
-}
-
-void pa__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);
-    if (u->silence.memblock)
-        pa_memblock_unref(u->silence.memblock);
-
-    if (u->sink) {
-        pa_sink_disconnect(u->sink);
-        pa_sink_unref(u->sink);
-    }
-    
-    if (u->source) {
-        pa_source_disconnect(u->source);
-        pa_source_unref(u->source);
-    }
-    
-    pa_iochannel_free(u->io);
-    pa_xfree(u);
-}
diff --git a/polyp/module-pipe-sink.c b/polyp/module-pipe-sink.c
deleted file mode 100644 (file)
index b6d8dc2..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "iochannel.h"
-#include "sink.h"
-#include "module.h"
-#include "util.h"
-#include "modargs.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "module-pipe-sink-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("UNIX pipe sink")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("sink_name=<name for the sink> file=<path of the FIFO> format=<sample format> channels=<number of channels> rate=<sample rate>")
-
-#define DEFAULT_FIFO_NAME "/tmp/music.output"
-#define DEFAULT_SINK_NAME "fifo_output"
-
-#define PA_TYPEID_PIPE PA_TYPEID_MAKE('P', 'I', 'P', 'E')
-
-struct userdata {
-    struct pa_core *core;
-
-    char *filename;
-    
-    struct pa_sink *sink;
-    struct pa_iochannel *io;
-    struct pa_defer_event *defer_event;
-
-    struct pa_memchunk memchunk;
-    struct pa_module *module;
-};
-
-static const char* const valid_modargs[] = {
-    "file",
-    "rate",
-    "format",
-    "channels",
-    "sink_name",
-    NULL
-};
-
-static void do_write(struct userdata *u) {
-    ssize_t r;
-    assert(u);
-
-    u->core->mainloop->defer_enable(u->defer_event, 0);
-        
-    if (!pa_iochannel_is_writable(u->io))
-        return;
-
-    pa_module_set_used(u->module, pa_idxset_ncontents(u->sink->inputs) + pa_idxset_ncontents(u->sink->monitor_source->outputs));
-    
-    if (!u->memchunk.length)
-        if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0)
-            return;
-
-    assert(u->memchunk.memblock && u->memchunk.length);
-    
-    if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) {
-        pa_log(__FILE__": write() failed: %s\n", strerror(errno));
-        return;
-    }
-
-    u->memchunk.index += r;
-    u->memchunk.length -= r;
-        
-    if (u->memchunk.length <= 0) {
-        pa_memblock_unref(u->memchunk.memblock);
-        u->memchunk.memblock = NULL;
-    }
-}
-
-static void notify_cb(struct pa_sink*s) {
-    struct userdata *u = s->userdata;
-    assert(s && u);
-
-    if (pa_iochannel_is_writable(u->io))
-        u->core->mainloop->defer_enable(u->defer_event, 1);
-}
-
-static pa_usec_t get_latency_cb(struct pa_sink *s) {
-    struct userdata *u = s->userdata;
-    assert(s && u);
-
-    return u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0;
-}
-
-static void defer_callback(struct pa_mainloop_api *m, struct pa_defer_event*e, 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__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))) {
-        pa_log(__FILE__": failed to parse module arguments\n");
-        goto fail;
-    }
-
-    ss = c->default_sample_spec;
-    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
-        pa_log(__FILE__": invalid sample format specification\n");
-        goto fail;
-    }
-    
-    mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
-
-    if ((fd = open(p, O_RDWR)) < 0) {
-        pa_log(__FILE__": open('%s'): %s\n", p, strerror(errno));
-        goto fail;
-    }
-
-    pa_fd_set_cloexec(fd, 1);
-    
-    if (fstat(fd, &st) < 0) {
-        pa_log(__FILE__": fstat('%s'): %s\n", p, strerror(errno));
-        goto fail;
-    }
-
-    if (!S_ISFIFO(st.st_mode)) {
-        pa_log(__FILE__": '%s' is not a FIFO.\n", p);
-        goto fail;
-    }
-
-    u = pa_xmalloc0(sizeof(struct userdata));
-    u->filename = pa_xstrdup(p);
-    u->core = c;
-    u->module = m;
-    m->userdata = u;
-    
-    if (!(u->sink = pa_sink_new(c, PA_TYPEID_PIPE, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss))) {
-        pa_log(__FILE__": failed to create sink.\n");
-        goto fail;
-    }
-    u->sink->notify = notify_cb;
-    u->sink->get_latency = get_latency_cb;
-    u->sink->userdata = u;
-    pa_sink_set_owner(u->sink, m);
-    u->sink->description = pa_sprintf_malloc("Unix FIFO sink '%s'", p);
-    assert(u->sink->description);
-
-    u->io = pa_iochannel_new(c->mainloop, -1, fd);
-    assert(u->io);
-    pa_iochannel_set_callback(u->io, io_callback, u);
-
-    u->memchunk.memblock = NULL;
-    u->memchunk.length = 0;
-
-    u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u);
-    assert(u->defer_event);
-    c->mainloop->defer_enable(u->defer_event, 0);
-
-    pa_modargs_free(ma);
-    
-    return 0;
-
-fail:
-    if (ma)
-        pa_modargs_free(ma);
-        
-    if (fd >= 0)
-        close(fd);
-
-    pa__done(c, m);
-
-    return -1;
-}
-
-void pa__done(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_disconnect(u->sink);
-    pa_sink_unref(u->sink);
-    pa_iochannel_free(u->io);
-    u->core->mainloop->defer_free(u->defer_event);
-
-    assert(u->filename);
-    unlink(u->filename);
-    pa_xfree(u->filename);
-    
-    pa_xfree(u);
-}
diff --git a/polyp/module-pipe-source.c b/polyp/module-pipe-source.c
deleted file mode 100644 (file)
index 5a39716..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "iochannel.h"
-#include "source.h"
-#include "module.h"
-#include "util.h"
-#include "modargs.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "module-pipe-source-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("UNIX pipe source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("source_name=<name for the source> file=<path of the FIFO> format=<sample format> channels=<number of channels> rate=<sample rate>")
-
-#define DEFAULT_FIFO_NAME "/tmp/music.input"
-#define DEFAULT_SOURCE_NAME "fifo_input"
-
-#define PA_TYPEID_PIPE PA_TYPEID_MAKE('P', 'I', 'P', 'E')
-
-struct userdata {
-    struct pa_core *core;
-
-    char *filename;
-    
-    struct pa_source *source;
-    struct pa_iochannel *io;
-    struct pa_module *module;
-    struct pa_memchunk chunk;
-};
-
-static const char* const valid_modargs[] = {
-    "file",
-    "rate",
-    "channels",
-    "format",
-    "source_name",
-    NULL
-};
-
-static void do_read(struct userdata *u) {
-    ssize_t r;
-    struct pa_memchunk chunk;
-    assert(u);
-
-    if (!pa_iochannel_is_readable(u->io))
-        return;
-
-    pa_module_set_used(u->module, pa_idxset_ncontents(u->source->outputs));
-
-    if (!u->chunk.memblock) {
-        u->chunk.memblock = pa_memblock_new(1024, u->core->memblock_stat);
-        u->chunk.index = chunk.length = 0;
-    }
-
-    assert(u->chunk.memblock && u->chunk.memblock->length > u->chunk.index);
-    if ((r = pa_iochannel_read(u->io, (uint8_t*) u->chunk.memblock->data + u->chunk.index, u->chunk.memblock->length - u->chunk.index)) <= 0) {
-        pa_log(__FILE__": read() failed: %s\n", strerror(errno));
-        return;
-    }
-
-    u->chunk.length = r;
-    pa_source_post(u->source, &u->chunk);
-    u->chunk.index += r;
-
-    if (u->chunk.index >= u->chunk.memblock->length) {
-        u->chunk.index = u->chunk.length = 0;
-        pa_memblock_unref(u->chunk.memblock);
-        u->chunk.memblock = NULL;
-    }
-}
-
-static void io_callback(struct pa_iochannel *io, void*userdata) {
-    struct userdata *u = userdata;
-    assert(u);
-    do_read(u);
-}
-
-int pa__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))) {
-        pa_log(__FILE__": failed to parse module arguments\n");
-        goto fail;
-    }
-
-    ss = c->default_sample_spec;
-    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
-        pa_log(__FILE__": invalid sample format specification\n");
-        goto fail;
-    }
-    
-    mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
-
-    if ((fd = open(p, O_RDWR)) < 0) {
-        pa_log(__FILE__": open('%s'): %s\n", p, strerror(errno));
-        goto fail;
-    }
-
-    pa_fd_set_cloexec(fd, 1);
-    
-    if (fstat(fd, &st) < 0) {
-        pa_log(__FILE__": fstat('%s'): %s\n", p, strerror(errno));
-        goto fail;
-    }
-
-    if (!S_ISFIFO(st.st_mode)) {
-        pa_log(__FILE__": '%s' is not a FIFO.\n", p);
-        goto fail;
-    }
-
-    u = pa_xmalloc0(sizeof(struct userdata));
-
-    u->filename = pa_xstrdup(p);
-    u->core = c;
-    
-    if (!(u->source = pa_source_new(c, PA_TYPEID_PIPE, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss))) {
-        pa_log(__FILE__": failed to create source.\n");
-        goto fail;
-    }
-    u->source->userdata = u;
-    pa_source_set_owner(u->source, m);
-    u->source->description = pa_sprintf_malloc("Unix FIFO source '%s'", p);
-    assert(u->source->description);
-
-    u->io = pa_iochannel_new(c->mainloop, fd, -1);
-    assert(u->io);
-    pa_iochannel_set_callback(u->io, io_callback, u);
-
-    u->chunk.memblock = NULL;
-    u->chunk.index = u->chunk.length = 0;
-    
-    u->module = m;
-    m->userdata = u;
-
-    pa_modargs_free(ma);
-    
-    return 0;
-
-fail:
-    if (ma)
-        pa_modargs_free(ma);
-        
-    if (fd >= 0)
-        close(fd);
-
-    pa__done(c, m);
-
-    return -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    assert(c && m);
-
-    if (!(u = m->userdata))
-        return;
-    
-    if (u->chunk.memblock)
-        pa_memblock_unref(u->chunk.memblock);
-        
-    pa_source_disconnect(u->source);
-    pa_source_unref(u->source);
-    pa_iochannel_free(u->io);
-
-    assert(u->filename);
-    unlink(u->filename);
-    pa_xfree(u->filename);
-    
-    pa_xfree(u);
-}
diff --git a/polyp/module-protocol-stub.c b/polyp/module-protocol-stub.c
deleted file mode 100644 (file)
index be27b8e..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#include <assert.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <netinet/in.h>
-
-#include "module.h"
-#include "socket-server.h"
-#include "socket-util.h"
-#include "util.h"
-#include "modargs.h"
-#include "log.h"
-#include "native-common.h"
-#include "util.h"
-
-#ifdef USE_TCP_SOCKETS
-#define SOCKET_DESCRIPTION "(TCP sockets)"
-#define SOCKET_USAGE "port=<TCP port number> loopback=<listen on loopback device only?>"
-#elif defined(USE_TCP6_SOCKETS)
-#define SOCKET_DESCRIPTION "(TCP/IPv6 sockets)"
-#define SOCKET_USAGE "port=<TCP port number> loopback=<listen on loopback device only?>"
-#else
-#define SOCKET_DESCRIPTION "(UNIX sockets)"
-#define SOCKET_USAGE "socket=<path to UNIX socket>"
-#endif
-
-#if defined(USE_PROTOCOL_SIMPLE)
-  #include "protocol-simple.h"
-  #define protocol_new pa_protocol_simple_new
-  #define protocol_free pa_protocol_simple_free
-  #define TCPWRAP_SERVICE "polypaudio-simple"
-  #define IPV4_PORT 4711
-  #define UNIX_SOCKET "simple"
-  #define MODULE_ARGUMENTS "rate", "format", "channels", "sink", "source", "playback", "record",
-  #if defined(USE_TCP_SOCKETS)
-    #include "module-simple-protocol-tcp-symdef.h"
-  #elif defined(USE_TCP6_SOCKETS)
-    #include "module-simple-protocol-tcp6-symdef.h"
-  #else
-    #include "module-simple-protocol-unix-symdef.h"
-  #endif
-  PA_MODULE_DESCRIPTION("Simple protocol "SOCKET_DESCRIPTION)
-  PA_MODULE_USAGE("rate=<sample rate> format=<sample format> channels=<number of channels> sink=<sink to connect to> source=<source to connect to> playback=<enable playback?> record=<enable record?> "SOCKET_USAGE)
-#elif defined(USE_PROTOCOL_CLI)
-  #include "protocol-cli.h" 
-  #define protocol_new pa_protocol_cli_new
-  #define protocol_free pa_protocol_cli_free
-  #define TCPWRAP_SERVICE "polypaudio-cli"
-  #define IPV4_PORT 4712
-  #define UNIX_SOCKET "cli"
-  #define MODULE_ARGUMENTS 
-  #ifdef USE_TCP_SOCKETS
-    #include "module-cli-protocol-tcp-symdef.h"
-  #elif defined(USE_TCP6_SOCKETS)
-    #include "module-cli-protocol-tcp6-symdef.h"
-  #else
-    #include "module-cli-protocol-unix-symdef.h"
-  #endif
-  PA_MODULE_DESCRIPTION("Command line interface protocol "SOCKET_DESCRIPTION)
-  PA_MODULE_USAGE(SOCKET_USAGE)
-#elif defined(USE_PROTOCOL_HTTP)
-  #include "protocol-http.h" 
-  #define protocol_new pa_protocol_http_new
-  #define protocol_free pa_protocol_http_free
-  #define TCPWRAP_SERVICE "polypaudio-http"
-  #define IPV4_PORT 4714
-  #define UNIX_SOCKET "http"
-  #define MODULE_ARGUMENTS 
-  #ifdef USE_TCP_SOCKETS
-    #include "module-http-protocol-tcp-symdef.h"
-  #elif defined(USE_TCP6_SOCKETS)
-    #include "module-http-protocol-tcp6-symdef.h"
-  #else
-    #include "module-http-protocol-unix-symdef.h"
-  #endif
-  PA_MODULE_DESCRIPTION("HTTP "SOCKET_DESCRIPTION)
-  PA_MODULE_USAGE(SOCKET_USAGE)
-#elif defined(USE_PROTOCOL_NATIVE)
-  #include "protocol-native.h"
-  #define protocol_new pa_protocol_native_new
-  #define protocol_free pa_protocol_native_free
-  #define TCPWRAP_SERVICE "polypaudio-native"
-  #define IPV4_PORT PA_NATIVE_DEFAULT_PORT
-  #define UNIX_SOCKET PA_NATIVE_DEFAULT_UNIX_SOCKET
-  #define MODULE_ARGUMENTS "public", "cookie",
-  #ifdef USE_TCP_SOCKETS
-    #include "module-native-protocol-tcp-symdef.h"
-  #elif defined(USE_TCP6_SOCKETS)
-    #include "module-native-protocol-tcp6-symdef.h"
-  #else
-    #include "module-native-protocol-unix-symdef.h"
-  #endif
-  PA_MODULE_DESCRIPTION("Native protocol "SOCKET_DESCRIPTION)
-  PA_MODULE_USAGE("public=<don't check for cookies?> cookie=<path to cookie file> "SOCKET_USAGE)
-#elif defined(USE_PROTOCOL_ESOUND)
-  #include "protocol-esound.h"
-  #include "esound.h"
-  #define protocol_new pa_protocol_esound_new
-  #define protocol_free pa_protocol_esound_free
-  #define TCPWRAP_SERVICE "esound"
-  #define IPV4_PORT ESD_DEFAULT_PORT
-  #define UNIX_SOCKET ESD_UNIX_SOCKET_NAME
-  #define MODULE_ARGUMENTS "sink", "source", "public", "cookie",
-  #ifdef USE_TCP_SOCKETS
-    #include "module-esound-protocol-tcp-symdef.h"
-  #elif defined(USE_TCP6_SOCKETS)
-    #include "module-esound-protocol-tcp6-symdef.h"
-  #else
-    #include "module-esound-protocol-unix-symdef.h"
-  #endif
-  PA_MODULE_DESCRIPTION("EsounD protocol "SOCKET_DESCRIPTION)
-  PA_MODULE_USAGE("sink=<sink to connect to> source=<source to connect to> public=<don't check for cookies?> cookie=<path to cookie file> "SOCKET_USAGE)
-#else
-  #error "Broken build system" 
-#endif
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-
-static const char* const valid_modargs[] = {
-    MODULE_ARGUMENTS
-#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS)
-    "port",
-    "loopback",
-#else
-    "socket",
-#endif
-    NULL
-};
-
-static struct pa_socket_server *create_socket_server(struct pa_core *c, struct pa_modargs *ma) {
-    struct pa_socket_server *s;
-#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS)
-    int loopback = 1;
-    uint32_t port = IPV4_PORT;
-
-    if (pa_modargs_get_value_boolean(ma, "loopback", &loopback) < 0) {
-        pa_log(__FILE__": loopback= expects a numerical argument.\n");
-        return NULL;
-    }
-
-    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {
-        pa_log(__FILE__": port= expects a numerical argument between 1 and 65535.\n");
-        return NULL;
-    }
-
-#ifdef USE_TCP6_SOCKETS
-    if (!(s = pa_socket_server_new_ipv6(c->mainloop, loopback ? (uint8_t*) &in6addr_loopback : (uint8_t*) &in6addr_any, port)))
-        return NULL;
-#else
-    if (!(s = pa_socket_server_new_ipv4(c->mainloop, loopback ? INADDR_LOOPBACK : INADDR_ANY, port, TCPWRAP_SERVICE)))
-        return NULL;
-#endif
-    
-#else
-    int r;
-    const char *v;
-    char tmp[PATH_MAX];
-
-    v = pa_modargs_get_value(ma, "socket", UNIX_SOCKET);
-    assert(v);
-
-    pa_runtime_path(v, tmp, sizeof(tmp));
-
-    if (pa_make_secure_parent_dir(tmp) < 0) {
-        pa_log(__FILE__": Failed to create secure socket directory.\n");
-        return NULL;
-    }
-
-    if ((r = pa_unix_socket_remove_stale(tmp)) < 0) {
-        pa_log(__FILE__": Failed to remove stale UNIX socket '%s': %s\n", tmp, strerror(errno));
-        return NULL;
-    }
-    
-    if (r)
-        pa_log(__FILE__": Removed stale UNIX socket '%s'.", tmp);
-    
-    if (!(s = pa_socket_server_new_unix(c->mainloop, tmp)))
-        return NULL;
-    
-#endif
-    return s;
-}
-
-int pa__init(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))) {
-        pa_log(__FILE__": Failed to parse module arguments\n");
-        goto finish;
-    }
-
-    if (!(s = create_socket_server(c, ma)))
-        goto finish;
-
-    if (!(m->userdata = protocol_new(c, s, m, ma))) {
-        pa_socket_server_unref(s);
-        goto finish;
-    }
-
-    ret = 0;
-
-finish:
-    if (ma)
-        pa_modargs_free(ma);
-
-    return ret;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    assert(c && m);
-
-    protocol_free(m->userdata);
-}
diff --git a/polyp/module-sine.c b/polyp/module-sine.c
deleted file mode 100644 (file)
index d8f7e4e..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <math.h>
-
-#include "sink-input.h"
-#include "module.h"
-#include "modargs.h"
-#include "xmalloc.h"
-#include "namereg.h"
-#include "log.h"
-#include "module-sine-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Sine wave generator")
-PA_MODULE_USAGE("sink=<sink to connect to> frequency=<frequency in Hz>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-
-#define PA_TYPEID_SINE PA_TYPEID_MAKE('S', 'I', 'N', 'E')
-
-struct userdata {
-    struct pa_core *core;
-    struct pa_module *module;
-    struct pa_sink_input *sink_input;
-    struct pa_memblock *memblock;
-    size_t peek_index;
-};
-
-static const char* const valid_modargs[] = {
-    "sink",
-    "frequency",
-    NULL,
-};
-
-static int sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) {
-    struct userdata *u;
-    assert(i && chunk && i->userdata);
-    u = i->userdata;
-
-    chunk->memblock = pa_memblock_ref(u->memblock);
-    chunk->index = u->peek_index;
-    chunk->length = u->memblock->length - u->peek_index;
-    return 0;
-}
-
-static void sink_input_drop(struct pa_sink_input *i, const struct pa_memchunk *chunk, size_t length) {
-    struct userdata *u;
-    assert(i && chunk && length && i->userdata);
-    u = i->userdata;
-
-    assert(chunk->memblock == u->memblock && length <= u->memblock->length-u->peek_index);
-
-    u->peek_index += length;
-
-    if (u->peek_index >= u->memblock->length)
-        u->peek_index = 0;
-}
-
-static void sink_input_kill(struct pa_sink_input *i) {
-    struct userdata *u;
-    assert(i && i->userdata);
-    u = i->userdata;
-
-    pa_sink_input_disconnect(u->sink_input);
-    pa_sink_input_unref(u->sink_input);
-    u->sink_input = NULL;
-
-    pa_module_unload_request(u->module);
-}
-
-static void calc_sine(float *f, size_t l, float freq) {
-    size_t i;
-
-    l /= sizeof(float);
-    
-    for (i = 0; i < l; i++)
-        f[i] = (float) sin((double) i/l*M_PI*2*freq)/2;
-}
-
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct pa_modargs *ma = NULL;
-    struct userdata *u;
-    struct pa_sink *sink;
-    const char *sink_name;
-    struct pa_sample_spec ss;
-    uint32_t frequency;
-    char t[256];
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": Failed to parse module arguments\n");
-        goto fail;
-    }
-    
-    m->userdata = u = pa_xmalloc(sizeof(struct userdata));
-    u->core = c;
-    u->module = m;
-    u->sink_input = NULL;
-    u->memblock = NULL;
-
-    sink_name = pa_modargs_get_value(ma, "sink", NULL);
-
-    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
-        pa_log(__FILE__": No such sink.\n");
-        goto fail;
-    }
-
-    ss.format = PA_SAMPLE_FLOAT32;
-    ss.rate = sink->sample_spec.rate;
-    ss.channels = 1;
-
-    frequency = 440;
-    if (pa_modargs_get_value_u32(ma, "frequency", &frequency) < 0 || frequency < 1 || frequency > ss.rate/2) {
-        pa_log(__FILE__": Invalid frequency specification\n");
-        goto fail;
-    }
-    
-    u->memblock = pa_memblock_new(pa_bytes_per_second(&ss), c->memblock_stat);
-    calc_sine(u->memblock->data, u->memblock->length, frequency);
-
-    snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency);
-    if (!(u->sink_input = pa_sink_input_new(sink, PA_TYPEID_SINE, t, &ss, 0, -1)))
-        goto fail;
-
-    u->sink_input->peek = sink_input_peek;
-    u->sink_input->drop = sink_input_drop;
-    u->sink_input->kill = sink_input_kill;
-    u->sink_input->userdata = u;
-    u->sink_input->owner = m;
-
-    u->peek_index = 0;
-    
-    pa_modargs_free(ma);
-    return 0;
-    
-fail:
-    if (ma)
-        pa_modargs_free(ma);
-
-    pa__done(c, m);
-    return -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u = m->userdata;
-    assert(c && m);
-
-    if (!u)
-        return;
-
-    if (u->sink_input) {
-        pa_sink_input_disconnect(u->sink_input);
-        pa_sink_input_unref(u->sink_input);
-    }
-    
-    if (u->memblock)
-        pa_memblock_unref(u->memblock);
-    pa_xfree(u);
-}
-
diff --git a/polyp/module-tunnel.c b/polyp/module-tunnel.c
deleted file mode 100644 (file)
index 9da87b3..0000000
+++ /dev/null
@@ -1,691 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <unistd.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <regex.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "module.h"
-#include "util.h"
-#include "modargs.h"
-#include "log.h"
-#include "subscribe.h"
-#include "xmalloc.h"
-#include "sink-input.h"
-#include "pdispatch.h"
-#include "pstream.h"
-#include "pstream-util.h"
-#include "authkey.h"
-#include "socket-client.h"
-#include "socket-util.h"
-#include "authkey-prop.h"
-
-#ifdef TUNNEL_SINK
-#include "module-tunnel-sink-symdef.h"
-PA_MODULE_DESCRIPTION("Tunnel module for sinks")
-PA_MODULE_USAGE("server=<address> sink=<remote sink name> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> sink_name=<name for the local sink>")
-#else
-#include "module-tunnel-source-symdef.h"
-PA_MODULE_DESCRIPTION("Tunnel module for sources")
-PA_MODULE_USAGE("server=<address> source=<remote source name> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> source_name=<name for the local source>")
-#endif
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-
-#define PA_TYPEID_TUNNEL PA_TYPEID_MAKE('T', 'U', 'N', 'L')
-
-#define DEFAULT_SINK_NAME "tunnel"
-#define DEFAULT_SOURCE_NAME "tunnel"
-
-#define DEFAULT_TLENGTH (44100*2*2/10)  //(10240*8)
-#define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2)
-#define DEFAULT_MINREQ 512
-#define DEFAULT_PREBUF (DEFAULT_TLENGTH-DEFAULT_MINREQ)
-#define DEFAULT_FRAGSIZE 1024
-
-#define DEFAULT_TIMEOUT 5
-
-#define LATENCY_INTERVAL 10
-
-static const char* const valid_modargs[] = {
-    "server",
-    "cookie",
-    "format",
-    "channels",
-    "rate",
-#ifdef TUNNEL_SINK
-    "sink_name",
-    "sink",
-#else
-    "source_name",
-    "source",
-#endif
-    NULL,
-};
-
-static void command_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-
-#ifdef TUNNEL_SINK
-static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-#endif
-
-static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = {
-#ifdef TUNNEL_SINK
-    [PA_COMMAND_REQUEST] = { command_request },
-#endif    
-    [PA_COMMAND_PLAYBACK_STREAM_KILLED] = { command_stream_killed },
-    [PA_COMMAND_RECORD_STREAM_KILLED] = { command_stream_killed },
-};
-
-struct userdata {
-    struct pa_socket_client *client;
-    struct pa_pstream *pstream;
-    struct pa_pdispatch *pdispatch;
-
-    char *server_name;
-#ifdef TUNNEL_SINK
-    char *sink_name;
-    struct pa_sink *sink;
-    uint32_t requested_bytes;
-#else
-    char *source_name;
-    struct pa_source *source;
-#endif
-    
-    struct pa_module *module;
-    struct pa_core *core;
-
-    uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
-
-    uint32_t ctag;
-    uint32_t device_index;
-    uint32_t channel;
-    
-    pa_usec_t host_latency;
-
-    struct pa_time_event *time_event;
-
-    int auth_cookie_in_property;
-};
-
-static void close_stuff(struct userdata *u) {
-    assert(u);
-    
-    if (u->pstream) {
-        pa_pstream_close(u->pstream);
-        pa_pstream_unref(u->pstream);
-        u->pstream = NULL;
-    }
-
-    if (u->pdispatch) {
-        pa_pdispatch_unref(u->pdispatch);
-        u->pdispatch = NULL;
-    }
-
-    if (u->client) {
-        pa_socket_client_unref(u->client);
-        u->client = NULL;
-    }
-
-#ifdef TUNNEL_SINK
-    if (u->sink) {
-        pa_sink_disconnect(u->sink);
-        pa_sink_unref(u->sink);
-        u->sink = NULL;
-    }
-#else
-    if (u->source) {
-        pa_source_disconnect(u->source);
-        pa_source_unref(u->source);
-        u->source = NULL;
-    }
-#endif
-
-    if (u->time_event) {
-        u->core->mainloop->time_free(u->time_event);
-        u->time_event = NULL;
-    }
-}
-
-static void die(struct userdata *u) {
-    assert(u);
-    close_stuff(u);
-    pa_module_unload_request(u->module);
-}
-
-static void command_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct userdata *u = userdata;
-    assert(pd && t && u && u->pdispatch == pd);
-
-    pa_log(__FILE__": stream killed\n");
-    die(u);
-}
-
-#ifdef TUNNEL_SINK
-static void send_prebuf_request(struct userdata *u) {
-    struct pa_tagstruct *t;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_PREBUF_PLAYBACK_STREAM);
-    pa_tagstruct_putu32(t, u->ctag++);
-    pa_tagstruct_putu32(t, u->channel);
-    pa_pstream_send_tagstruct(u->pstream, t);
-}
-
-static void send_bytes(struct userdata *u) {
-    assert(u);
-
-    if (!u->pstream)
-        return;
-
-    while (u->requested_bytes > 0) {
-        struct pa_memchunk chunk;
-        if (pa_sink_render(u->sink, u->requested_bytes, &chunk) < 0) {
-
-            
-            if (u->requested_bytes >= DEFAULT_TLENGTH-DEFAULT_PREBUF) 
-                send_prebuf_request(u);
-            
-            return;
-        }
-
-        pa_pstream_send_memblock(u->pstream, u->channel, 0, &chunk);
-        pa_memblock_unref(chunk.memblock);
-
-        if (chunk.length > u->requested_bytes)
-            u->requested_bytes = 0;
-        else
-            u->requested_bytes -= chunk.length;
-    }
-}
-
-static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct userdata *u = userdata;
-    uint32_t bytes, channel;
-    assert(pd && command == PA_COMMAND_REQUEST && t && u && u->pdispatch == pd);
-
-    if (pa_tagstruct_getu32(t, &channel) < 0 ||
-        pa_tagstruct_getu32(t, &bytes) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        pa_log(__FILE__": invalid protocol reply\n");
-        die(u);
-        return;
-    }
-
-    if (channel != u->channel) {
-        pa_log(__FILE__": recieved data for invalid channel\n");
-        die(u);
-        return;
-    }
-    
-    u->requested_bytes += bytes;
-    send_bytes(u);
-}
-
-#endif
-
-static void stream_get_latency_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct userdata *u = userdata;
-    pa_usec_t buffer_usec, sink_usec, source_usec, transport_usec;
-    int playing;
-    uint32_t queue_length;
-    uint64_t counter;
-    struct timeval local, remote, now;
-    assert(pd && u);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (command == PA_COMMAND_ERROR)
-            pa_log(__FILE__": failed to get latency.\n");
-        else
-            pa_log(__FILE__": protocol error.\n");
-        die(u);
-        return;
-    }
-    
-    if (pa_tagstruct_get_usec(t, &buffer_usec) < 0 ||
-        pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
-        pa_tagstruct_get_usec(t, &source_usec) < 0 ||
-        pa_tagstruct_get_boolean(t, &playing) < 0 ||
-        pa_tagstruct_getu32(t, &queue_length) < 0 ||
-        pa_tagstruct_get_timeval(t, &local) < 0 ||
-        pa_tagstruct_get_timeval(t, &remote) < 0 ||
-        pa_tagstruct_getu64(t, &counter) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        pa_log(__FILE__": invalid reply.\n");
-        die(u);
-        return;
-    }
-
-    gettimeofday(&now, NULL);
-
-    if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
-        /* local and remote seem to have synchronized clocks */
-#ifdef TUNNEL_SINK
-        transport_usec = pa_timeval_diff(&remote, &local);
-#else
-        transport_usec = pa_timeval_diff(&now, &remote);
-#endif    
-    } else
-        transport_usec = pa_timeval_diff(&now, &local)/2;
-
-#ifdef TUNNEL_SINK
-    u->host_latency = sink_usec + transport_usec;
-#else
-    u->host_latency = source_usec + transport_usec;
-    if (u->host_latency > sink_usec)
-        u->host_latency -= sink_usec;
-    else
-        u->host_latency = 0;
-#endif
-
-/*     pa_log(__FILE__": estimated host latency: %0.0f usec\n", (double) u->host_latency); */
-}
-
-static void request_latency(struct userdata *u) {
-    struct pa_tagstruct *t;
-    struct timeval now;
-    uint32_t tag;
-    assert(u);
-
-    t = pa_tagstruct_new(NULL, 0);
-#ifdef TUNNEL_SINK    
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
-#else
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY);
-#endif
-    pa_tagstruct_putu32(t, tag = u->ctag++);
-    pa_tagstruct_putu32(t, u->channel);
-
-    gettimeofday(&now, NULL);
-    pa_tagstruct_put_timeval(t, &now);
-    pa_tagstruct_putu64(t, 0);
-    
-    pa_pstream_send_tagstruct(u->pstream, t);
-    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u);
-}
-
-static void create_stream_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct userdata *u = userdata;
-    assert(pd && u && u->pdispatch == pd);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (command == PA_COMMAND_ERROR)
-            pa_log(__FILE__": failed to create stream.\n");
-        else
-            pa_log(__FILE__": protocol error.\n");
-        die(u);
-        return;
-    }
-
-    if (pa_tagstruct_getu32(t, &u->channel) < 0 ||
-        pa_tagstruct_getu32(t, &u->device_index) < 0 ||
-#ifdef TUNNEL_SINK        
-        pa_tagstruct_getu32(t, &u->requested_bytes) < 0 ||
-#endif        
-        !pa_tagstruct_eof(t)) {
-        pa_log(__FILE__": invalid reply.\n");
-        die(u);
-        return;
-    }
-
-    request_latency(u);
-#ifdef TUNNEL_SINK
-    send_bytes(u);
-#endif
-}
-
-static void setup_complete_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct userdata *u = userdata;
-    struct pa_tagstruct *reply;
-    char name[256], un[128], hn[128];
-    assert(pd && u && u->pdispatch == pd);
-
-    if (command != PA_COMMAND_REPLY || !pa_tagstruct_eof(t)) {
-        if (command == PA_COMMAND_ERROR)
-            pa_log(__FILE__": failed to authenticate\n");
-        else
-            pa_log(__FILE__": protocol error.\n");
-        die(u);
-        return;
-    }
-#ifdef TUNNEL_SINK
-    snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', sink '%s'",
-             pa_get_host_name(hn, sizeof(hn)),
-             pa_get_user_name(un, sizeof(un)),
-             u->sink->name);
-#else
-    snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', source '%s'",
-             pa_get_host_name(hn, sizeof(hn)),
-             pa_get_user_name(un, sizeof(un)),
-             u->source->name);
-#endif
-    
-    reply = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
-    pa_tagstruct_putu32(reply, tag = u->ctag++);
-    pa_tagstruct_puts(reply, name);
-    pa_pstream_send_tagstruct(u->pstream, reply);
-    /* We ignore the server's reply here */
-
-    reply = pa_tagstruct_new(NULL, 0);
-#ifdef TUNNEL_SINK    
-    pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM);
-    pa_tagstruct_putu32(reply, tag = u->ctag++);
-    pa_tagstruct_puts(reply, name);
-    pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec);
-    pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
-    pa_tagstruct_puts(reply, u->sink_name);
-    pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
-    pa_tagstruct_put_boolean(reply, 0);
-    pa_tagstruct_putu32(reply, DEFAULT_TLENGTH);
-    pa_tagstruct_putu32(reply, DEFAULT_PREBUF);
-    pa_tagstruct_putu32(reply, DEFAULT_MINREQ);
-    pa_tagstruct_putu32(reply, PA_VOLUME_NORM);
-#else
-    pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM);
-    pa_tagstruct_putu32(reply, tag = u->ctag++);
-    pa_tagstruct_puts(reply, name);
-    pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec);
-    pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
-    pa_tagstruct_puts(reply, u->source_name);
-    pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH);
-    pa_tagstruct_put_boolean(reply, 0);
-    pa_tagstruct_putu32(reply, DEFAULT_FRAGSIZE);
-#endif
-    
-    pa_pstream_send_tagstruct(u->pstream, reply);
-    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u);
-}
-
-static void pstream_die_callback(struct pa_pstream *p, void *userdata) {
-    struct userdata *u = userdata;
-    assert(p && u);
-
-    pa_log(__FILE__": stream died.\n");
-    die(u);
-}
-
-
-static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) {
-    struct userdata *u = userdata;
-    assert(p && packet && u);
-
-    if (pa_pdispatch_run(u->pdispatch, packet, u) < 0) {
-        pa_log(__FILE__": invalid packet\n");
-        die(u);
-    }
-}
-
-#ifndef TUNNEL_SINK
-static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, uint32_t delta, const struct pa_memchunk *chunk, void *userdata) {
-    struct userdata *u = userdata;
-    assert(p && chunk && u);
-
-    if (channel != u->channel) {
-        pa_log(__FILE__": recieved memory block on bad channel.\n");
-        die(u);
-        return;
-    }
-    
-    pa_source_post(u->source, chunk);
-}
-#endif
-
-static void on_connection(struct pa_socket_client *sc, struct pa_iochannel *io, void *userdata) {
-    struct userdata *u = userdata;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(sc && u && u->client == sc);
-
-    pa_socket_client_unref(u->client);
-    u->client = NULL;
-    
-    if (!io) {
-        pa_log(__FILE__": connection failed.\n");
-        pa_module_unload_request(u->module);
-        return;
-    }
-
-    u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->memblock_stat);
-    u->pdispatch = pa_pdispatch_new(u->core->mainloop, command_table, PA_COMMAND_MAX);
-
-    pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
-    pa_pstream_set_recieve_packet_callback(u->pstream, pstream_packet_callback, u);
-#ifndef TUNNEL_SINK
-    pa_pstream_set_recieve_memblock_callback(u->pstream, pstream_memblock_callback, u);
-#endif
-    
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
-    pa_tagstruct_putu32(t, tag = u->ctag++);
-    pa_tagstruct_put_arbitrary(t, u->auth_cookie, sizeof(u->auth_cookie));
-    pa_pstream_send_tagstruct(u->pstream, t);
-    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u);
-    
-}
-
-#ifdef TUNNEL_SINK
-static void sink_notify(struct pa_sink*sink) {
-    struct userdata *u;
-    assert(sink && sink->userdata);
-    u = sink->userdata;
-
-    send_bytes(u);
-}
-
-static pa_usec_t sink_get_latency(struct pa_sink *sink) {
-    struct userdata *u;
-    uint32_t l;
-    pa_usec_t usec = 0;
-    assert(sink && sink->userdata);
-    u = sink->userdata;
-
-    l = DEFAULT_TLENGTH;
-
-    if (l > u->requested_bytes) {
-        l -= u->requested_bytes;
-        usec += pa_bytes_to_usec(l, &u->sink->sample_spec);
-    }
-
-    usec += u->host_latency;
-
-    return usec;
-}
-#else
-static pa_usec_t source_get_latency(struct pa_source *source) {
-    struct userdata *u;
-    assert(source && source->userdata);
-    u = source->userdata;
-
-    return u->host_latency;
-}
-#endif
-
-static void timeout_callback(struct pa_mainloop_api *m, struct pa_time_event*e, const struct timeval *tv, void *userdata) {
-    struct userdata *u = userdata;
-    struct timeval ntv;
-    assert(m && e && u);
-
-    request_latency(u);
-    
-    gettimeofday(&ntv, NULL);
-    ntv.tv_sec += LATENCY_INTERVAL;
-    m->time_restart(e, &ntv);
-}
-
-static int load_key(struct userdata *u, const char*fn) {
-    assert(u);
-
-    u->auth_cookie_in_property = 0;
-    
-    if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
-        pa_log_debug(__FILE__": using already loaded auth cookie.\n");
-        pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
-        u->auth_cookie_in_property = 1;
-        return 0;
-    }
-    
-    if (!fn)
-        fn = PA_NATIVE_COOKIE_FILE;
-
-    if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
-        return -1;
-
-    pa_log_debug(__FILE__": loading cookie from disk.\n");
-    
-    if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
-        u->auth_cookie_in_property = 1;
-
-    return 0;
-}
-
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct pa_modargs *ma = NULL;
-    struct userdata *u = NULL;
-    struct pa_sample_spec ss;
-    struct timeval ntv;
-    assert(c && m);
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments\n");
-        goto fail;
-    }
-
-    u = pa_xmalloc(sizeof(struct userdata));
-    m->userdata = u;
-    u->module = m;
-    u->core = c;
-    u->client = NULL;
-    u->pdispatch = NULL;
-    u->pstream = NULL;
-    u->server_name = NULL;
-#ifdef TUNNEL_SINK
-    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));;
-    u->sink = NULL;
-    u->requested_bytes = 0;
-#else
-    u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
-    u->source = NULL;
-#endif
-    u->ctag = 1;
-    u->device_index = u->channel = PA_INVALID_INDEX;
-    u->host_latency = 0;
-    u->auth_cookie_in_property = 0;
-    u->time_event = NULL;
-    
-    if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
-        goto fail;
-    
-    if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) {
-        pa_log(__FILE__": no server specified.\n");
-        goto fail;
-    }
-
-    ss = c->default_sample_spec;
-    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
-        pa_log(__FILE__": invalid sample format specification\n");
-        goto fail;
-    }
-
-    if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
-        pa_log(__FILE__": failed to connect to server '%s'\n", u->server_name);
-        goto fail;
-    }
-    
-    if (!u->client)
-        goto fail;
-
-    pa_socket_client_set_callback(u->client, on_connection, u);
-
-#ifdef TUNNEL_SINK
-    if (!(u->sink = pa_sink_new(c, PA_TYPEID_TUNNEL, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss))) {
-        pa_log(__FILE__": failed to create sink.\n");
-        goto fail;
-    }
-
-    u->sink->notify = sink_notify;
-    u->sink->get_latency = sink_get_latency;
-    u->sink->userdata = u;
-    u->sink->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->sink_name ? u->sink_name : "", u->sink_name ? "@" : "", u->server_name);
-
-    pa_sink_set_owner(u->sink, m);
-#else
-    if (!(u->source = pa_source_new(c, PA_TYPEID_TUNNEL, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss))) {
-        pa_log(__FILE__": failed to create source.\n");
-        goto fail;
-    }
-
-    u->source->get_latency = source_get_latency;
-    u->source->userdata = u;
-    u->source->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->source_name ? u->source_name : "", u->source_name ? "@" : "", u->server_name);
-
-    pa_source_set_owner(u->source, m);
-#endif
-    
-    gettimeofday(&ntv, NULL);
-    ntv.tv_sec += LATENCY_INTERVAL;
-    u->time_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, u);
-
-    pa_modargs_free(ma);
-
-    return 0;
-    
-fail:
-    pa__done(c, m);
-
-    if (ma)
-        pa_modargs_free(ma);
-    return  -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata* u;
-    assert(c && m);
-
-    if (!(u = m->userdata))
-        return;
-
-    close_stuff(u);
-
-    if (u->auth_cookie_in_property)
-        pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME);
-    
-#ifdef TUNNEL_SINK
-    pa_xfree(u->sink_name);
-#else
-    pa_xfree(u->source_name);
-#endif
-    pa_xfree(u->server_name);
-
-    pa_xfree(u);
-}
-
-
diff --git a/polyp/module-x11-bell.c b/polyp/module-x11-bell.c
deleted file mode 100644 (file)
index 084f5d4..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <X11/Xlib.h>
-#include <X11/XKBlib.h>
-
-#include "module.h"
-#include "sink.h"
-#include "scache.h"
-#include "modargs.h"
-#include "xmalloc.h"
-#include "namereg.h"
-#include "log.h"
-#include "x11wrap.h"
-#include "module-x11-bell-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("X11 Bell interceptor")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("sink=<sink to connect to> sample=<sample name> display=<X11 display>")
-
-struct userdata {
-    struct pa_core *core;
-    int xkb_event_base;
-    char *sink_name;
-    char *scache_item;
-    Display *display;
-
-    struct pa_x11_wrapper *x11_wrapper;
-    struct pa_x11_client *x11_client;
-};
-
-static const char* const valid_modargs[] = {
-    "sink",
-    "sample",
-    "display",
-    NULL
-};
-
-static int ring_bell(struct userdata *u, int percent) {
-    struct pa_sink *s;
-    assert(u);
-
-    if (!(s = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
-        pa_log(__FILE__": Invalid sink: %s\n", u->sink_name);
-        return -1;
-    }
-
-    pa_scache_play_item(u->core, u->scache_item, s, percent*2);
-    return 0;
-}
-
-static int x11_event_callback(struct pa_x11_wrapper *w, XEvent *e, void *userdata) {
-    XkbBellNotifyEvent *bne;
-    struct userdata *u = userdata;
-    assert(w && e && u && u->x11_wrapper == w);
-
-    if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify)
-        return 0;
-
-    bne = (XkbBellNotifyEvent*) e;
-
-    if (ring_bell(u, bne->percent) < 0) {
-        pa_log_info(__FILE__": Ringing bell failed, reverting to X11 device bell.\n");
-        XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);
-    }
-
-    return 1;
-}
-
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u = NULL;
-    struct pa_modargs *ma = NULL;
-    int major, minor;
-    unsigned int auto_ctrls, auto_values;
-    assert(c && m);
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments\n");
-        goto fail;
-    }
-    
-    m->userdata = u = pa_xmalloc(sizeof(struct userdata));
-    u->core = c;
-    u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "x11-bell"));
-    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
-    u->x11_client = NULL;
-
-    if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL)))) 
-        goto fail;
-
-    u->display = pa_x11_wrapper_get_display(u->x11_wrapper);
-    
-    major = XkbMajorVersion;
-    minor = XkbMinorVersion;
-    
-    if (!XkbLibraryVersion(&major, &minor)) {
-        pa_log(__FILE__": XkbLibraryVersion() failed\n");
-        goto fail;
-    }
-
-    major = XkbMajorVersion;
-    minor = XkbMinorVersion;
-
-
-    if (!XkbQueryExtension(u->display, NULL, &u->xkb_event_base, NULL, &major, &minor)) {
-        pa_log(__FILE__": XkbQueryExtension() failed\n");
-        goto fail;
-    }
-
-    XkbSelectEvents(u->display, XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask);
-    auto_ctrls = auto_values = XkbAudibleBellMask;
-    XkbSetAutoResetControls(u->display, XkbAudibleBellMask, &auto_ctrls, &auto_values);
-    XkbChangeEnabledControls(u->display, XkbUseCoreKbd, XkbAudibleBellMask, 0);
-
-    u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_callback, u);
-    
-    pa_modargs_free(ma);
-    
-    return 0;
-    
-fail:
-    if (ma)
-        pa_modargs_free(ma);
-    if (m->userdata)
-        pa__done(c, m);
-    return -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u = m->userdata;
-    assert(c && m && u);
-
-    pa_xfree(u->scache_item);
-    pa_xfree(u->sink_name);
-
-    if (u->x11_client)
-        pa_x11_client_free(u->x11_client);
-
-    if (u->x11_wrapper)
-        pa_x11_wrapper_unref(u->x11_wrapper);
-
-    pa_xfree(u);
-}
diff --git a/polyp/module-x11-publish.c b/polyp/module-x11-publish.c
deleted file mode 100644 (file)
index a47a760..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <X11/Xlib.h>
-#include <X11/Xatom.h>
-
-#include "module.h"
-#include "sink.h"
-#include "scache.h"
-#include "modargs.h"
-#include "xmalloc.h"
-#include "namereg.h"
-#include "log.h"
-#include "x11wrap.h"
-#include "util.h"
-#include "native-common.h"
-#include "module-x11-publish-symdef.h"
-#include "authkey-prop.h"
-#include "authkey.h"
-#include "x11prop.h"
-#include "strlist.h"
-#include "props.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("X11 Credential Publisher")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("display=<X11 display>")
-
-static const char* const valid_modargs[] = {
-    "display",
-    "sink",
-    "source",
-    "cookie",
-    NULL
-};
-
-struct userdata {
-    struct pa_core *core;
-    struct pa_x11_wrapper *x11_wrapper;
-    Display *display;
-    char *id;
-    uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
-    int auth_cookie_in_property;
-};
-
-static int load_key(struct userdata *u, const char*fn) {
-    assert(u);
-
-    u->auth_cookie_in_property = 0;
-    
-    if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
-        pa_log_debug(__FILE__": using already loaded auth cookie.\n");
-        pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
-        u->auth_cookie_in_property = 1;
-        return 0;
-    }
-    
-    if (!fn)
-        fn = PA_NATIVE_COOKIE_FILE;
-
-    if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
-        return -1;
-
-    pa_log_debug(__FILE__": loading cookie from disk.\n");
-    
-    if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
-        u->auth_cookie_in_property = 1;
-
-    return 0;
-}
-
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    struct pa_modargs *ma = NULL;
-    char hn[256], un[128];
-    char hx[PA_NATIVE_COOKIE_LENGTH*2+1];
-    const char *t;
-    char *s;
-    struct pa_strlist *l;
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments\n");
-        goto fail;
-    }
-
-    m->userdata = u = pa_xmalloc(sizeof(struct userdata));
-    u->core = c;
-    u->id = NULL;
-    u->auth_cookie_in_property = 0;
-
-    if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
-        goto fail;
-
-    if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL)))) 
-        goto fail;
-
-    u->display = pa_x11_wrapper_get_display(u->x11_wrapper);
-
-    if (!(l = pa_property_get(c, PA_NATIVE_SERVER_PROPERTY_NAME)))
-        goto fail;
-
-    s = pa_strlist_tostring(l);
-    pa_x11_set_prop(u->display, "POLYP_SERVER", s);
-    pa_xfree(s);
-    
-    if (!pa_get_fqdn(hn, sizeof(hn)) || !pa_get_user_name(un, sizeof(un)))
-        goto fail;
-    
-    u->id = pa_sprintf_malloc("%s@%s/%u", un, hn, (unsigned) getpid());
-    pa_x11_set_prop(u->display, "POLYP_ID", u->id);
-
-    if ((t = pa_modargs_get_value(ma, "source", NULL)))
-        pa_x11_set_prop(u->display, "POLYP_SOURCE", t);
-
-    if ((t = pa_modargs_get_value(ma, "sink", NULL)))
-        pa_x11_set_prop(u->display, "POLYP_SINK", t);
-
-    pa_x11_set_prop(u->display, "POLYP_COOKIE", pa_hexstr(u->auth_cookie, sizeof(u->auth_cookie), hx, sizeof(hx)));
-    
-    pa_modargs_free(ma);
-    return 0;
-    
-fail:
-    if (ma)
-        pa_modargs_free(ma);
-
-    pa__done(c, m);
-    return -1;
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata*u;
-    assert(c && m);
-
-    if (!(u = m->userdata))
-        return;
-    
-    if (u->x11_wrapper) {
-        char t[256];
-
-        /* Yes, here is a race condition */
-        if (!pa_x11_get_prop(u->display, "POLYP_ID", t, sizeof(t)) || strcmp(t, u->id))
-            pa_log("WARNING: Polypaudio information vanished from X11!\n");
-        else {
-            pa_x11_del_prop(u->display, "POLYP_ID");
-            pa_x11_del_prop(u->display, "POLYP_SERVER");
-            pa_x11_del_prop(u->display, "POLYP_SINK");
-            pa_x11_del_prop(u->display, "POLYP_SOURCE");
-            pa_x11_del_prop(u->display, "POLYP_COOKIE");
-            XSync(u->display, False);
-        }
-    }
-    
-    if (u->x11_wrapper)
-        pa_x11_wrapper_unref(u->x11_wrapper);
-
-    if (u->auth_cookie_in_property)
-        pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME);
-
-    pa_xfree(u->id);
-    pa_xfree(u);
-}
-
diff --git a/polyp/module-zeroconf-publish.c b/polyp/module-zeroconf-publish.c
deleted file mode 100644 (file)
index a7e2073..0000000
+++ /dev/null
@@ -1,507 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "module-zeroconf-publish-symdef.h"
-#include "howl-wrap.h"
-#include "xmalloc.h"
-#include "autoload.h"
-#include "sink.h"
-#include "source.h"
-#include "native-common.h"
-#include "util.h"
-#include "log.h"
-#include "subscribe.h"
-#include "dynarray.h"
-#include "endianmacros.h"
-#include "modargs.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-PA_MODULE_USAGE("port=<IP port number>")
-
-#define SERVICE_NAME_SINK "_polypaudio-sink._tcp"
-#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp"
-#define SERVICE_NAME_SERVER "_polypaudio-server._tcp"
-
-static const char* const valid_modargs[] = {
-    "port",
-    NULL
-};
-
-struct service {
-    sw_discovery_oid oid;
-    char *name;
-    int published; /* 0 -> not yet registered, 1 -> registered with data from real device, 2 -> registered with data from autoload device */
-
-    struct {
-        int valid;
-        enum pa_namereg_type type;
-        uint32_t index;
-    } loaded;
-
-    struct {
-        int valid;
-        enum pa_namereg_type type;
-        uint32_t index;
-    } autoload;
-};
-
-struct userdata {
-    struct pa_core *core;
-    struct pa_howl_wrapper *howl_wrapper;
-    struct pa_hashmap *services;
-    struct pa_dynarray *sink_dynarray, *source_dynarray, *autoload_dynarray;
-    struct pa_subscription *subscription;
-
-    uint16_t port;
-    sw_discovery_oid server_oid;
-};
-
-static sw_result publish_reply(sw_discovery discovery, sw_discovery_publish_status status, sw_discovery_oid oid, sw_opaque extra) {
-    return SW_OKAY;
-}
-
-static void get_service_data(struct userdata *u, struct service *s, struct pa_sample_spec *ret_ss, char **ret_description, pa_typeid_t *ret_typeid) {
-    assert(u && s && s->loaded.valid && ret_ss && ret_description && ret_typeid);
-
-    if (s->loaded.type == PA_NAMEREG_SINK) {
-        struct pa_sink *sink = pa_idxset_get_by_index(u->core->sinks, s->loaded.index);
-        assert(sink);
-        *ret_ss = sink->sample_spec;
-        *ret_description = sink->description;
-        *ret_typeid = sink->typeid;
-    } else if (s->loaded.type == PA_NAMEREG_SOURCE) {
-        struct pa_source *source = pa_idxset_get_by_index(u->core->sources, s->loaded.index);
-        assert(source);
-        *ret_ss = source->sample_spec;
-        *ret_description = source->description;
-        *ret_typeid = source->typeid;
-    } else
-        assert(0);
-}
-
-static void txt_record_server_data(struct pa_core *c, sw_text_record t) {
-    char s[256];
-    assert(c);
-
-    sw_text_record_add_key_and_string_value(t, "server-version", PACKAGE_NAME" "PACKAGE_VERSION);
-    sw_text_record_add_key_and_string_value(t, "user-name", pa_get_user_name(s, sizeof(s)));
-    sw_text_record_add_key_and_string_value(t, "fqdn", pa_get_fqdn(s, sizeof(s)));
-    snprintf(s, sizeof(s), "0x%08x", c->cookie);
-    sw_text_record_add_key_and_string_value(t, "cookie", s);
-}
-
-static int publish_service(struct userdata *u, struct service *s) {
-    char t[256];
-    char hn[256];
-    int r = -1;
-    sw_text_record txt;
-    int free_txt = 0;
-    assert(u && s);
-       
-    if ((s->published == 1 && s->loaded.valid) ||
-        (s->published == 2 && s->autoload.valid && !s->loaded.valid))
-        return 0;
-
-    if (s->published) {
-        sw_discovery_cancel(pa_howl_wrapper_get_discovery(u->howl_wrapper), s->oid);
-        s->published = 0;
-    }
-
-    snprintf(t, sizeof(t), "Networked Audio Device %s on %s", s->name, pa_get_host_name(hn, sizeof(hn)));
-
-    if (sw_text_record_init(&txt) != SW_OKAY) {
-        pa_log(__FILE__": sw_text_record_init() failed\n");
-        goto finish;
-    }
-    free_txt = 1;
-
-    sw_text_record_add_key_and_string_value(txt, "device", s->name);
-
-    txt_record_server_data(u->core, txt);
-    
-    if (s->loaded.valid) {
-        char z[64], *description;
-        pa_typeid_t typeid;
-        struct pa_sample_spec ss;
-
-        get_service_data(u, s, &ss, &description, &typeid);
-            
-        snprintf(z, sizeof(z), "%u", ss.rate);
-        sw_text_record_add_key_and_string_value(txt, "rate", z);
-        snprintf(z, sizeof(z), "%u", ss.channels);
-        sw_text_record_add_key_and_string_value(txt, "channels", z);
-        sw_text_record_add_key_and_string_value(txt, "format", pa_sample_format_to_string(ss.format));
-
-        sw_text_record_add_key_and_string_value(txt, "description", description);
-
-        snprintf(z, sizeof(z), "0x%8x", typeid);
-        sw_text_record_add_key_and_string_value(txt, "typeid", z);
-        
-        
-        if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t,
-                                 s->loaded.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE,
-                                 NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt),
-                                 publish_reply, s, &s->oid) != SW_OKAY) {
-            pa_log(__FILE__": failed to register sink on zeroconf.\n");
-            goto finish;
-        }
-
-        s->published = 1;
-    } else if (s->autoload.valid) {
-
-        if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t,
-                                 s->autoload.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE,
-                                 NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt),
-                                 publish_reply, s, &s->oid) != SW_OKAY) {
-            pa_log(__FILE__": failed to register sink on zeroconf.\n");
-            goto finish;
-        }
-
-        s->published = 2;
-    }
-
-    r = 0;
-    
-finish:
-
-    if (!s->published) {
-        /* Remove this service */
-        pa_hashmap_remove(u->services, s->name);
-        pa_xfree(s->name);
-        pa_xfree(s);
-    }
-
-    if (free_txt)
-        sw_text_record_fina(txt);
-    
-    return r;
-}
-
-struct service *get_service(struct userdata *u, const char *name) {
-    struct service *s;
-    
-    if ((s = pa_hashmap_get(u->services, name)))
-        return s;
-    
-    s = pa_xmalloc(sizeof(struct service));
-    s->published = 0;
-    s->name = pa_xstrdup(name);
-    s->loaded.valid = s->autoload.valid = 0;
-
-    pa_hashmap_put(u->services, s->name, s);
-
-    return s;
-}
-
-static int publish_sink(struct userdata *u, struct pa_sink *s) {
-    struct service *svc;
-    assert(u && s);
-
-    svc = get_service(u, s->name);
-    if (svc->loaded.valid)
-        return 0;
-
-    svc->loaded.valid = 1;
-    svc->loaded.type = PA_NAMEREG_SINK;
-    svc->loaded.index = s->index;
-
-    pa_dynarray_put(u->sink_dynarray, s->index, svc);
-
-    return publish_service(u, svc);
-}
-
-static int publish_source(struct userdata *u, struct pa_source *s) {
-    struct service *svc;
-    assert(u && s);
-
-    svc = get_service(u, s->name);
-    if (svc->loaded.valid)
-        return 0;
-
-    svc->loaded.valid = 1;
-    svc->loaded.type = PA_NAMEREG_SOURCE;
-    svc->loaded.index = s->index;
-
-    pa_dynarray_put(u->source_dynarray, s->index, svc);
-    
-    return publish_service(u, svc);
-}
-
-static int publish_autoload(struct userdata *u, struct pa_autoload_entry *s) {
-    struct service *svc;
-    assert(u && s);
-
-    svc = get_service(u, s->name);
-    if (svc->autoload.valid)
-        return 0;
-
-    svc->autoload.valid = 1;
-    svc->autoload.type = s->type;
-    svc->autoload.index = s->index;
-
-    pa_dynarray_put(u->autoload_dynarray, s->index, svc);
-    
-    return publish_service(u, svc);
-}
-
-static int remove_sink(struct userdata *u, uint32_t index) {
-    struct service *svc;
-    assert(u && index != PA_INVALID_INDEX);
-
-    if (!(svc = pa_dynarray_get(u->sink_dynarray, index)))
-        return 0;
-
-    if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SINK)
-        return 0;
-
-    svc->loaded.valid = 0;
-    pa_dynarray_put(u->sink_dynarray, index, NULL);
-    
-    return publish_service(u, svc);
-}
-
-static int remove_source(struct userdata *u, uint32_t index) {
-    struct service *svc;
-    assert(u && index != PA_INVALID_INDEX);
-    
-    if (!(svc = pa_dynarray_get(u->source_dynarray, index)))
-        return 0;
-
-    if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SOURCE)
-        return 0;
-
-    svc->loaded.valid = 0;
-    pa_dynarray_put(u->source_dynarray, index, NULL);
-
-    return publish_service(u, svc);
-}
-
-static int remove_autoload(struct userdata *u, uint32_t index) {
-    struct service *svc;
-    assert(u && index != PA_INVALID_INDEX);
-    
-    if (!(svc = pa_dynarray_get(u->autoload_dynarray, index)))
-        return 0;
-
-    if (!svc->autoload.valid)
-        return 0;
-
-    svc->autoload.valid = 0;
-    pa_dynarray_put(u->autoload_dynarray, index, NULL);
-
-    return publish_service(u, svc);
-}
-
-static void subscribe_callback(struct pa_core *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) {
-    struct userdata *u = userdata;
-    assert(u && c);
-
-    switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
-        case PA_SUBSCRIPTION_EVENT_SINK: {
-            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                struct pa_sink *sink;
-
-                if ((sink = pa_idxset_get_by_index(c->sinks, index))) {
-                    if (publish_sink(u, sink) < 0)
-                        goto fail;
-                }
-            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                if (remove_sink(u, index) < 0)
-                    goto fail;
-            }
-        
-            break;
-
-        case PA_SUBSCRIPTION_EVENT_SOURCE:
-
-            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                struct pa_source *source;
-                
-                if ((source = pa_idxset_get_by_index(c->sources, index))) {
-                    if (publish_source(u, source) < 0)
-                        goto fail;
-                }
-            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                if (remove_source(u, index) < 0)
-                    goto fail;
-            }
-            
-            break;
-
-        case PA_SUBSCRIPTION_EVENT_AUTOLOAD:
-            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                struct pa_autoload_entry *autoload;
-                    
-                if ((autoload = pa_idxset_get_by_index(c->autoload_idxset, index))) {
-                    if (publish_autoload(u, autoload) < 0)
-                        goto fail;
-                }
-            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                if (remove_autoload(u, index) < 0)
-                        goto fail;
-            }
-            
-            break;
-    }
-
-    return;
-
-fail:
-    if (u->subscription) {
-        pa_subscription_free(u->subscription);
-        u->subscription = NULL;
-    }
-}
-
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct userdata *u;
-    uint32_t index, port = PA_NATIVE_DEFAULT_PORT;
-    struct pa_sink *sink;
-    struct pa_source *source;
-    struct pa_autoload_entry *autoload;
-    struct pa_modargs *ma = NULL;
-    char t[256], hn[256];
-    int free_txt = 0;
-    sw_text_record txt;
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments.\n");
-        goto fail;
-    }
-
-    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port == 0 || port >= 0xFFFF) {
-        pa_log(__FILE__": invalid port specified.\n");
-        goto fail;
-    }
-
-    m->userdata = u = pa_xmalloc(sizeof(struct userdata));
-    u->core = c;
-    u->port = (uint16_t) port;
-
-    if (!(u->howl_wrapper = pa_howl_wrapper_get(c)))
-        goto fail;
-
-    u->services = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    u->sink_dynarray = pa_dynarray_new();
-    u->source_dynarray = pa_dynarray_new();
-    u->autoload_dynarray = pa_dynarray_new();
-    
-    u->subscription = pa_subscription_new(c,
-                                          PA_SUBSCRIPTION_MASK_SINK|
-                                          PA_SUBSCRIPTION_MASK_SOURCE|
-                                          PA_SUBSCRIPTION_MASK_AUTOLOAD, subscribe_callback, u);
-
-    for (sink = pa_idxset_first(c->sinks, &index); sink; sink = pa_idxset_next(c->sinks, &index))
-        if (publish_sink(u, sink) < 0)
-            goto fail;
-
-    for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index))
-        if (publish_source(u, source) < 0)
-            goto fail;
-
-    if (c->autoload_idxset)
-        for (autoload = pa_idxset_first(c->autoload_idxset, &index); autoload; autoload = pa_idxset_next(c->autoload_idxset, &index))
-            if (publish_autoload(u, autoload) < 0)
-                goto fail;
-
-    snprintf(t, sizeof(t), "Networked Audio Server on %s", pa_get_host_name(hn, sizeof(hn)));   
-
-    if (sw_text_record_init(&txt) != SW_OKAY) {
-        pa_log(__FILE__": sw_text_record_init() failed\n");
-        goto fail;
-    }
-    free_txt = 1;
-
-    txt_record_server_data(u->core, txt);
-    
-    if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t,
-                             SERVICE_NAME_SERVER,
-                             NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt),
-                             publish_reply, u, &u->server_oid) != SW_OKAY) {
-        pa_log(__FILE__": failed to register server on zeroconf.\n");
-        goto fail;
-    }
-    
-    sw_text_record_fina(txt);
-    pa_modargs_free(ma);
-    
-    return 0;
-    
-fail:
-    pa__done(c, m);
-
-    if (ma)
-        pa_modargs_free(ma);
-
-    if (free_txt)
-        sw_text_record_fina(txt);
-    
-    return -1;
-}
-
-static void service_free(void *p, void *userdata) {
-    struct service *s = p;
-    struct userdata *u = userdata;
-    assert(s && u);
-    sw_discovery_cancel(pa_howl_wrapper_get_discovery(u->howl_wrapper), s->oid);
-    pa_xfree(s->name);
-    pa_xfree(s);
-}
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    struct userdata*u;
-    assert(c && m);
-
-    if (!(u = m->userdata))
-        return;
-
-    if (u->services)
-        pa_hashmap_free(u->services, service_free, u);
-
-    if (u->sink_dynarray)
-        pa_dynarray_free(u->sink_dynarray, NULL, NULL);
-    if (u->source_dynarray)
-        pa_dynarray_free(u->source_dynarray, NULL, NULL);
-    if (u->autoload_dynarray)
-        pa_dynarray_free(u->autoload_dynarray, NULL, NULL);
-    
-    if (u->subscription)
-        pa_subscription_free(u->subscription);
-    
-    if (u->howl_wrapper)
-        pa_howl_wrapper_unref(u->howl_wrapper);
-
-    
-    pa_xfree(u);
-}
-
diff --git a/polyp/module.c b/polyp/module.c
deleted file mode 100644 (file)
index aedaae0..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-
-#include "module.h"
-#include "xmalloc.h"
-#include "subscribe.h"
-#include "log.h"
-
-#define PA_SYMBOL_INIT "pa__init"
-#define PA_SYMBOL_DONE "pa__done"
-
-#define UNLOAD_POLL_TIME 2
-
-static void timeout_callback(struct pa_mainloop_api *m, struct pa_time_event*e, const struct timeval *tv, void *userdata) {
-    struct pa_core *c = userdata;
-    struct timeval ntv;
-    assert(c && c->mainloop == m && c->module_auto_unload_event == e);
-
-    pa_module_unload_unused(c);
-
-    gettimeofday(&ntv, NULL);
-    ntv.tv_sec += UNLOAD_POLL_TIME;
-    m->time_restart(e, &ntv);
-}
-
-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);
-
-    if (c->disallow_module_loading)
-        goto fail;
-    
-    m = pa_xmalloc(sizeof(struct pa_module));
-
-    m->name = pa_xstrdup(name);
-    m->argument = pa_xstrdup(argument);
-    
-    if (!(m->dl = lt_dlopenext(name))) {
-        pa_log(__FILE__": Failed to open module \"%s\": %s\n", name, lt_dlerror());
-        goto fail;
-    }
-
-    if (!(m->init = (int (*)(struct pa_core *c, struct pa_module*m)) lt_dlsym(m->dl, PA_SYMBOL_INIT))) {
-        pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.\n", name);
-        goto fail;
-    }
-
-    if (!(m->done = (void (*)(struct pa_core *c, struct pa_module*m)) lt_dlsym(m->dl, PA_SYMBOL_DONE))) {
-        pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.\n", name);
-        goto fail;
-    }
-    
-    m->userdata = NULL;
-    m->core = c;
-    m->n_used = -1;
-    m->auto_unload = 0;
-    m->unload_requested = 0;
-
-    assert(m->init);
-    if (m->init(c, m) < 0) {
-        pa_log_error(__FILE__": Failed to load  module \"%s\" (argument: \"%s\"): initialization failed.\n", name, argument ? argument : "");
-        goto fail;
-    }
-
-    if (!c->modules)
-        c->modules = pa_idxset_new(NULL, NULL);
-
-    if (!c->module_auto_unload_event) {
-        struct timeval ntv;
-        gettimeofday(&ntv, NULL);
-        ntv.tv_sec += UNLOAD_POLL_TIME;
-        c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
-    }
-    assert(c->module_auto_unload_event);
-    
-    assert(c->modules);
-    r = pa_idxset_put(c->modules, m, &m->index);
-    assert(r >= 0 && m->index != PA_IDXSET_INVALID);
-
-    pa_log_info(__FILE__": Loaded \"%s\" (index: #%u; argument: \"%s\").\n", m->name, m->index, m->argument ? m->argument : ""); 
-
-    pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
-    
-    return m;
-    
-fail:
-
-    if (m) {
-        pa_xfree(m->argument);
-        pa_xfree(m->name);
-        
-        if (m->dl)
-            lt_dlclose(m->dl);
-
-        pa_xfree(m);
-    }
-
-    return NULL;
-}
-
-static void pa_module_free(struct pa_module *m) {
-    assert(m && m->done && m->core);
-
-    if (m->core->disallow_module_loading)
-        return;
-
-    pa_log_info(__FILE__": Unloading \"%s\" (index: #%u).\n", m->name, m->index); 
-
-    m->done(m->core, m);
-
-    lt_dlclose(m->dl);
-    
-    pa_log_info(__FILE__": Unloaded \"%s\" (index: #%u).\n", m->name, m->index); 
-
-    pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
-    
-    pa_xfree(m->name);
-    pa_xfree(m->argument);
-    pa_xfree(m);
-}
-
-void pa_module_unload(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;
-
-    if (c->module_auto_unload_event) {
-        c->mainloop->time_free(c->module_auto_unload_event);
-        c->module_auto_unload_event = NULL;
-    }
-
-    if (c->module_defer_unload_event) {
-        c->mainloop->defer_free(c->module_defer_unload_event);
-        c->module_defer_unload_event = NULL;
-    }
-}
-
-static int unused_callback(void *p, uint32_t index, int *del, void *userdata) {
-    struct pa_module *m = p;
-    time_t *now = userdata;
-    assert(p && del && now);
-    
-    if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) {
-        pa_module_free(m);
-        *del = 1;
-    }
-
-    return 0;
-}
-
-void pa_module_unload_unused(struct pa_core *c) {
-    time_t now;
-    assert(c);
-
-    if (!c->modules)
-        return;
-    
-    time(&now);
-    pa_idxset_foreach(c->modules, unused_callback, &now);
-}
-
-static int unload_callback(void *p, uint32_t index, int *del, void *userdata) {
-    struct pa_module *m = p;
-    assert(m);
-
-    if (m->unload_requested) {
-        pa_module_free(m);
-        *del = 1;
-    }
-
-    return 0;
-}
-
-static void defer_cb(struct pa_mainloop_api*api, struct pa_defer_event *e, void *userdata) {
-    struct pa_core *core = userdata;
-    api->defer_enable(e, 0);
-
-    if (!core->modules)
-        return;
-
-    pa_idxset_foreach(core->modules, unload_callback, NULL);
-
-}
-
-void pa_module_unload_request(struct pa_module *m) {
-    assert(m);
-
-    m->unload_requested = 1;
-
-    if (!m->core->module_defer_unload_event)
-        m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
-
-    m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
-}
-
-void pa_module_set_used(struct pa_module*m, int used) {
-    assert(m);
-
-    if (m->n_used != used)
-        pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
-    
-    if (m->n_used != used && used == 0)
-        time(&m->last_used_time);
-
-    m->n_used = used;
-}
-
-struct pa_modinfo *pa_module_get_info(struct pa_module *m) {
-    assert(m);
-
-    return pa_modinfo_get_by_handle(m->dl);
-}
diff --git a/polyp/module.h b/polyp/module.h
deleted file mode 100644 (file)
index f5daff9..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef foomodulehfoo
-#define foomodulehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include <ltdl.h>
-
-#include "core.h"
-#include "modinfo.h"
-
-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;
-
-    int n_used;
-    int auto_unload;
-    time_t last_used_time;
-
-    int unload_requested;
-};
-
-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_unused(struct pa_core *c);
-
-void pa_module_unload_request(struct pa_module *m);
-
-void pa_module_set_used(struct pa_module*m, int used);
-
-#define PA_MODULE_AUTHOR(s) const char * pa__get_author(void) { return s; }
-#define PA_MODULE_DESCRIPTION(s) const char * pa__get_description(void) { return s; }
-#define PA_MODULE_USAGE(s) const char * pa__get_usage(void) { return s; }
-#define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; }
-
-struct pa_modinfo *pa_module_get_info(struct pa_module *m);
-
-#endif
diff --git a/polyp/namereg.c b/polyp/namereg.c
deleted file mode 100644 (file)
index 0460144..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "namereg.h"
-#include "autoload.h"
-#include "source.h"
-#include "sink.h"
-#include "xmalloc.h"
-#include "subscribe.h"
-#include "util.h"
-
-struct namereg_entry {
-    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 = pa_xstrdup(name);
-    else {
-        unsigned i;
-        size_t l = strlen(name);
-        n = pa_xmalloc(l+3);
-        
-        for (i = 1; i <= 99; i++) {
-            snprintf(n, l+2, "%s%u", name, i);
-
-            if (!(e = pa_hashmap_get(c->namereg, n)))
-                break;
-        }
-
-        if (e) {
-            pa_xfree(n);
-            return NULL;
-        }
-    }
-    
-    assert(n);
-    e = pa_xmalloc(sizeof(struct namereg_entry));
-    e->type = type;
-    e->name = n;
-    e->data = data;
-
-    r = pa_hashmap_put(c->namereg, e->name, e);
-    assert (r >= 0);
-
-    return e->name;
-    
-}
-
-void pa_namereg_unregister(struct pa_core *c, const char *name) {
-    struct namereg_entry *e;
-    assert(c && name);
-
-    e = pa_hashmap_remove(c->namereg, name);
-    assert(e);
-
-    pa_xfree(e->name);
-    pa_xfree(e);
-}
-
-void* pa_namereg_get(struct pa_core *c, const char *name, enum pa_namereg_type type, int autoload) {
-    struct namereg_entry *e;
-    uint32_t index;
-    assert(c);
-    
-    if (!name) {
-        if (type == PA_NAMEREG_SOURCE)
-            name = pa_namereg_get_default_source_name(c);
-        else if (type == PA_NAMEREG_SINK)
-            name = pa_namereg_get_default_sink_name(c);
-    }
-
-    if (!name)
-        return NULL;
-    
-    if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
-        if (e->type == e->type)
-            return e->data;
-
-    if (pa_atou(name, &index) < 0) {
-
-        if (autoload) {
-            pa_autoload_request(c, name, type);
-            
-            if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
-                if (e->type == e->type)
-                    return e->data;
-        }
-        
-        return NULL;
-    }
-
-    if (type == PA_NAMEREG_SINK)
-        return pa_idxset_get_by_index(c->sinks, index);
-    else if (type == PA_NAMEREG_SOURCE)
-        return pa_idxset_get_by_index(c->sources, index);
-    else if (type == PA_NAMEREG_SAMPLE && c->scache)
-        return pa_idxset_get_by_index(c->scache, index);
-
-    return NULL;
-}
-
-void pa_namereg_set_default(struct pa_core*c, const char *name, enum pa_namereg_type type) {
-    char **s;
-    assert(c && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
-
-    s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name;
-    assert(s);
-
-    if (!name && !*s)
-        return;
-    
-    if (name && *s && !strcmp(name, *s))
-        return;
-    
-    pa_xfree(*s);
-    *s = pa_xstrdup(name);
-    pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
-}
-
-const char *pa_namereg_get_default_sink_name(struct pa_core *c) {
-    struct pa_sink *s;
-    assert(c);
-
-    if (c->default_sink_name)
-        return c->default_sink_name;
-    
-    if ((s = pa_idxset_first(c->sinks, NULL)))
-        pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK);
-
-    if (c->default_sink_name)
-        return c->default_sink_name;
-
-    return NULL;
-}
-
-const char *pa_namereg_get_default_source_name(struct pa_core *c) {
-    struct pa_source *s;
-    uint32_t index;
-    
-    assert(c);
-
-    if (c->default_source_name)
-        return c->default_source_name;
-
-    for (s = pa_idxset_first(c->sources, &index); s; s = pa_idxset_next(c->sources, &index))
-        if (!s->monitor_of) {
-            pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
-            break;
-        }
-
-    if (!c->default_source_name)
-        if ((s = pa_idxset_first(c->sources, NULL)))
-            pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
-
-    if (c->default_source_name)
-        return c->default_source_name;
-
-    return NULL;
-}
diff --git a/polyp/namereg.h b/polyp/namereg.h
deleted file mode 100644 (file)
index 99032be..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef foonamereghfoo
-#define foonamereghfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "core.h"
-
-enum pa_namereg_type {
-    PA_NAMEREG_SINK,
-    PA_NAMEREG_SOURCE,
-    PA_NAMEREG_SAMPLE
-};
-
-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, int autoload);
-void pa_namereg_set_default(struct pa_core*c, const char *name, enum pa_namereg_type type);
-
-const char *pa_namereg_get_default_sink_name(struct pa_core *c);
-const char *pa_namereg_get_default_source_name(struct pa_core *c);
-
-#endif
diff --git a/polyp/oss-util.c b/polyp/oss-util.c
deleted file mode 100644 (file)
index 8c83cbb..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <sys/soundcard.h>
-#include <sys/ioctl.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include "oss-util.h"
-#include "util.h"
-#include "log.h"
-
-int pa_oss_open(const char *device, int *mode, int* pcaps) {
-    int fd = -1;
-    assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY));
-
-    if (*mode == O_RDWR) {
-        if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) {
-            int dcaps, *tcaps;
-            ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
-
-            tcaps = pcaps ? pcaps : &dcaps;
-            
-            if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) {
-                pa_log(__FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
-                goto fail;
-            }
-
-            if (*tcaps & DSP_CAP_DUPLEX)
-                return fd;
-
-            close(fd);
-        }
-        
-        if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) {
-            if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) {
-                pa_log(__FILE__": open('%s'): %s\n", device, strerror(errno));
-                goto fail;
-            }
-        }
-    } else {
-        if ((fd = open(device, *mode|O_NDELAY)) < 0) {
-            pa_log(__FILE__": open('%s'): %s\n", device, strerror(errno));
-            goto fail;
-        }
-    } 
-
-    if (pcaps) {
-        if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
-            pa_log(__FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
-            goto fail;
-        }
-    }
-
-    pa_fd_set_cloexec(fd, 1);
-    
-    return fd;
-
-fail:
-    if (fd >= 0)
-        close(fd);
-    return 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) {
-                    pa_log(__FILE__": SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno));
-                    return -1;
-                } else
-                    ss->format = PA_SAMPLE_U8;
-            } else
-                ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
-        } else
-            ss->format = PA_SAMPLE_S16NE;
-    }
-        
-    channels = ss->channels;
-    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
-        pa_log(__FILE__": SNDCTL_DSP_CHANNELS: %s\n", strerror(errno));
-        return -1;
-    }
-    assert(channels);
-    ss->channels = channels;
-
-    speed = ss->rate;
-    if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
-        pa_log(__FILE__": SNDCTL_DSP_SPEED: %s\n", strerror(errno));
-        return -1;
-    }
-    assert(speed);
-    ss->rate = speed;
-
-    return 0;
-}
-
-static int simple_log2(int v) {
-    int k = 0;
-
-    for (;;) {
-        v >>= 1;
-        if (!v) break;
-        k++;
-    }
-    
-    return k;
-}
-
-int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
-    int arg;
-    arg = ((int) nfrags << 16) | simple_log2(frag_size);
-    
-    if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
-        pa_log(__FILE__": SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
-        return -1;
-    }
-
-    return 0;
-}
diff --git a/polyp/oss-util.h b/polyp/oss-util.h
deleted file mode 100644 (file)
index c1c6963..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef fooossutilhfoo
-#define fooossutilhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "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.c b/polyp/pacat.c
deleted file mode 100644 (file)
index 5910d13..0000000
+++ /dev/null
@@ -1,536 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <signal.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <getopt.h>
-
-#include <polyp/polyplib.h>
-#include <polyp/polyplib-error.h>
-#include <polyp/mainloop.h>
-#include <polyp/mainloop-signal.h>
-#include <polyp/polyplib-version.h>
-
-#if PA_API_VERSION != 8
-#error Invalid Polypaudio API version
-#endif
-
-static enum { RECORD, PLAYBACK } mode = PLAYBACK;
-
-static 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 struct pa_io_event* stdio_event = NULL;
-
-static char *stream_name = NULL, *client_name = NULL, *device = NULL;
-
-static int verbose = 0;
-static pa_volume_t volume = PA_VOLUME_NORM;
-
-static struct pa_sample_spec sample_spec = {
-    .format = PA_SAMPLE_S16LE,
-    .rate = 44100,
-    .channels = 2
-};
-
-/* A shortcut for terminating the application */
-static void quit(int ret) {
-    assert(mainloop_api);
-    mainloop_api->quit(mainloop_api, ret);
-}
-
-/* Write some data to the stream */
-static void do_stream_write(size_t length) {
-    size_t l;
-    assert(length);
-
-    if (!buffer || !buffer_length)
-        return;
-    
-    l = length;
-    if (l > buffer_length)
-        l = buffer_length;
-    
-    pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0);
-    buffer_length -= l;
-    buffer_index += l;
-    
-    if (!buffer_length) {
-        free(buffer);
-        buffer = NULL;
-        buffer_index = buffer_length = 0;
-    }
-}
-
-/* This is called whenever new data may be written to the stream */
-static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) {
-    assert(s && length);
-
-    if (stdio_event)
-        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
-
-    if (!buffer)
-        return;
-
-    do_stream_write(length);
-}
-
-/* This is called whenever new data may is available */
-static void stream_read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata) {
-    assert(s && data && length);
-
-    if (stdio_event)
-        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
-
-    if (buffer) {
-        fprintf(stderr, "Buffer overrun, dropping incoming data\n");
-        return;
-    }
-
-    buffer = malloc(buffer_length = length);
-    assert(buffer);
-    memcpy(buffer, data, length);
-    buffer_index = 0;
-}
-
-/* This routine is called whenever the stream state changes */
-static void stream_state_callback(struct pa_stream *s, void *userdata) {
-    assert(s);
-
-    switch (pa_stream_get_state(s)) {
-        case PA_STREAM_CREATING:
-        case PA_STREAM_TERMINATED:
-            break;
-
-        case PA_STREAM_READY:
-            if (verbose)
-                fprintf(stderr, "Stream successfully created\n");
-            break;
-            
-        case PA_STREAM_FAILED:
-        default:
-            fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
-            quit(1);
-    }
-}
-
-/* This is called whenever the context status changes */
-static void context_state_callback(struct pa_context *c, void *userdata) {
-    assert(c);
-
-    switch (pa_context_get_state(c)) {
-        case PA_CONTEXT_CONNECTING:
-        case PA_CONTEXT_AUTHORIZING:
-        case PA_CONTEXT_SETTING_NAME:
-            break;
-        
-        case PA_CONTEXT_READY:
-            
-            assert(c && !stream);
-
-            if (verbose)
-                fprintf(stderr, "Connection established.\n");
-
-            stream = pa_stream_new(c, stream_name, &sample_spec);
-            assert(stream);
-
-            pa_stream_set_state_callback(stream, stream_state_callback, NULL);
-            pa_stream_set_write_callback(stream, stream_write_callback, NULL);
-            pa_stream_set_read_callback(stream, stream_read_callback, NULL);
-
-            if (mode == PLAYBACK)
-                pa_stream_connect_playback(stream, device, NULL, 0, volume);
-            else
-                pa_stream_connect_record(stream, device, NULL, 0);
-                
-            break;
-            
-        case PA_CONTEXT_TERMINATED:
-            quit(0);
-            break;
-
-        case PA_CONTEXT_FAILED:
-        default:
-            fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
-            quit(1);
-    }
-}
-
-/* Connection draining complete */
-static void context_drain_complete(struct pa_context*c, void *userdata) {
-    pa_context_disconnect(c);
-}
-
-/* Stream draining complete */
-static void stream_drain_complete(struct pa_stream*s, int success, void *userdata) {
-    struct pa_operation *o;
-
-    if (!success) {
-        fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
-        quit(1);
-    }
-    
-    if (verbose)    
-        fprintf(stderr, "Playback stream drained.\n");
-
-    pa_stream_disconnect(stream);
-    pa_stream_unref(stream);
-    stream = NULL;
-    
-    if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
-        pa_context_disconnect(context);
-    else {
-        pa_operation_unref(o);
-
-        if (verbose)
-            fprintf(stderr, "Draining connection to server.\n");
-    }
-}
-
-/* New data on STDIN **/
-static void stdin_callback(struct pa_mainloop_api*a, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    size_t l, w = 0;
-    ssize_t r;
-    assert(a == mainloop_api && e && stdio_event == e);
-
-    if (buffer) {
-        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
-        return;
-    }
-
-    if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
-        l = 4096;
-    
-    buffer = malloc(l);
-    assert(buffer);
-    if ((r = read(fd, buffer, l)) <= 0) {
-        if (r == 0) {
-            if (verbose)
-                fprintf(stderr, "Got EOF.\n");
-            pa_operation_unref(pa_stream_drain(stream, stream_drain_complete, NULL));
-        } else {
-            fprintf(stderr, "read() failed: %s\n", strerror(errno));
-            quit(1);
-        }
-
-        mainloop_api->io_free(stdio_event);
-        stdio_event = NULL;
-        return;
-    }
-
-    buffer_length = r;
-    buffer_index = 0;
-
-    if (w)
-        do_stream_write(w);
-}
-
-/* Some data may be written to STDOUT */
-static void stdout_callback(struct pa_mainloop_api*a, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    ssize_t r;
-    assert(a == mainloop_api && e && stdio_event == e);
-
-    if (!buffer) {
-        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
-        return;
-    }
-
-    assert(buffer_length);
-    
-    if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
-        fprintf(stderr, "write() failed: %s\n", strerror(errno));
-        quit(1);
-
-        mainloop_api->io_free(stdio_event);
-        stdio_event = NULL;
-        return;
-    }
-
-    buffer_length -= r;
-    buffer_index += r;
-
-    if (!buffer_length) {
-        free(buffer);
-        buffer = NULL;
-        buffer_length = buffer_index = 0;
-    }
-}
-
-/* UNIX signal to quit recieved */
-static void exit_signal_callback(struct pa_mainloop_api*m, struct pa_signal_event *e, int sig, void *userdata) {
-    if (verbose)
-        fprintf(stderr, "Got signal, exiting.\n");
-    quit(0);
-    
-}
-
-/* Show the current latency */
-static void stream_get_latency_callback(struct pa_stream *s, const struct pa_latency_info *i, void *userdata) {
-    pa_usec_t total;
-    int negative = 0;
-    assert(s);
-
-    if (!i) {
-        fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
-        quit(1);
-        return;
-    }
-
-    total = pa_stream_get_latency(s, i, &negative);
-
-    fprintf(stderr, "Latency: buffer: %0.0f usec; sink: %0.0f usec; source: %0.0f usec; transport: %0.0f usec; total: %0.0f usec; synchronized clocks: %s.\n",
-            (float) i->buffer_usec, (float) i->sink_usec, (float) i->source_usec, (float) i->transport_usec, (float) total * (negative?-1:1),
-            i->synchronized_clocks ? "yes" : "no");
-}
-
-/* Someone requested that the latency is shown */
-static void sigusr1_signal_callback(struct pa_mainloop_api*m, struct pa_signal_event *e, int sig, void *userdata) {
-    fprintf(stderr, "Got SIGUSR1, requesting latency.\n");
-    pa_operation_unref(pa_stream_get_latency_info(stream, stream_get_latency_callback, NULL));
-}
-
-
-static void help(const char *argv0) {
-
-    printf("%s [options]\n\n"
-           "  -h, --help                            Show this help\n"
-           "      --version                         Show version\n\n"
-           "  -r, --record                          Create a connection for recording\n"
-           "  -p, --playback                        Create a connection for playback\n\n"
-           "  -v, --verbose                         Enable verbose operations\n\n"
-           "  -s, --server=SERVER                   The name of the server to connect to\n"
-           "  -d, --device=DEVICE                   The name of the sink/source to connect to\n"
-           "  -n, --client-name=NAME                How to call this client on the server\n"
-           "      --stream-name=NAME                How to call this stream on the server\n"
-           "      --volume=VOLUME                   Specify the initial (linear) volume in range 0...256\n"
-           "      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"
-           "      --format=SAMPLEFORMAT             The sample type, one of s16le, s16be, u8, float32le,\n"
-           "                                        float32be, ulaw, alaw (defaults to s16ne)\n"
-           "      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
-           "                                        (defaults to 2)\n",
-           argv0);
-}
-
-enum {
-    ARG_VERSION = 256,
-    ARG_STREAM_NAME,
-    ARG_VOLUME,
-    ARG_SAMPLERATE,
-    ARG_SAMPLEFORMAT,
-    ARG_CHANNELS
-};
-
-int main(int argc, char *argv[]) {
-    struct pa_mainloop* m = NULL;
-    int ret = 1, r, c;
-    char *bn, *server = NULL;
-
-    static const struct option long_options[] = {
-        {"record",      0, NULL, 'r'},
-        {"playback",    0, NULL, 'p'},
-        {"device",      1, NULL, 'd'},
-        {"server",      1, NULL, 's'},
-        {"client-name", 1, NULL, 'n'},
-        {"stream-name", 1, NULL, ARG_STREAM_NAME},
-        {"version",     0, NULL, ARG_VERSION},
-        {"help",        0, NULL, 'h'},
-        {"verbose",     0, NULL, 'v'},
-        {"volume",      1, NULL, ARG_VOLUME},
-        {"rate",        1, NULL, ARG_SAMPLERATE},
-        {"format",      1, NULL, ARG_SAMPLEFORMAT},
-        {"channels",    1, NULL, ARG_CHANNELS},
-        {NULL,          0, NULL, 0}
-    };
-
-    if (!(bn = strrchr(argv[0], '/')))
-        bn = argv[0];
-    else
-        bn++;
-
-    if (strstr(bn, "rec") || strstr(bn, "mon"))
-        mode = RECORD;
-    else if (strstr(bn, "cat") || strstr(bn, "play"))
-        mode = PLAYBACK;
-
-    while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
-
-        switch (c) {
-            case 'h' :
-                help(bn);
-                ret = 0;
-                goto quit;
-                
-            case ARG_VERSION:
-                printf("pacat "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
-                ret = 0;
-                goto quit;
-
-            case 'r':
-                mode = RECORD;
-                break;
-
-            case 'p':
-                mode = PLAYBACK;
-                break;
-
-            case 'd':
-                free(device);
-                device = strdup(optarg);
-                break;
-
-            case 's':
-                free(server);
-                server = strdup(optarg);
-                break;
-
-            case 'n':
-                free(client_name);
-                client_name = strdup(optarg);
-                break;
-
-            case ARG_STREAM_NAME:
-                free(stream_name);
-                stream_name = strdup(optarg);
-                break;
-
-            case 'v':
-                verbose = 1;
-                break;
-
-            case ARG_VOLUME: {
-                int v = atoi(optarg);
-                volume = v < 0 ? 0 : v;
-                break;
-            }
-
-            case ARG_CHANNELS: 
-                sample_spec.channels = atoi(optarg);
-                break;
-
-            case ARG_SAMPLEFORMAT:
-                sample_spec.format = pa_parse_sample_format(optarg);
-                break;
-
-            case ARG_SAMPLERATE:
-                sample_spec.rate = atoi(optarg);
-                break;
-
-            default:
-                goto quit;
-        }
-    }
-
-    if (!client_name)
-        client_name = strdup(bn);
-
-    if (!stream_name)
-        stream_name = strdup(client_name);
-
-    if (!pa_sample_spec_valid(&sample_spec)) {
-        fprintf(stderr, "Invalid sample specification\n");
-        goto quit;
-    }
-    
-    if (verbose) {
-        char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
-        pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
-        fprintf(stderr, "Opening a %s stream with sample specification '%s'.\n", mode == RECORD ? "recording" : "playback", t);
-    }
-
-    /* Set up a new main loop */
-    if (!(m = pa_mainloop_new())) {
-        fprintf(stderr, "pa_mainloop_new() failed.\n");
-        goto quit;
-    }
-
-    mainloop_api = pa_mainloop_get_api(m);
-
-    r = pa_signal_init(mainloop_api);
-    assert(r == 0);
-    pa_signal_new(SIGINT, exit_signal_callback, NULL);
-    pa_signal_new(SIGTERM, exit_signal_callback, NULL);
-    pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
-    signal(SIGPIPE, SIG_IGN);
-    
-    if (!(stdio_event = mainloop_api->io_new(mainloop_api,
-                                             mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
-                                             mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
-                                             mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
-        fprintf(stderr, "source_io() failed.\n");
-        goto quit;
-    }
-
-    /* Create a new connection context */
-    if (!(context = pa_context_new(mainloop_api, client_name))) {
-        fprintf(stderr, "pa_context_new() failed.\n");
-        goto quit;
-    }
-
-    pa_context_set_state_callback(context, context_state_callback, NULL);
-
-    /* Connect the context */
-    pa_context_connect(context, server, 1, NULL);
-
-    /* Run the main loop */
-    if (pa_mainloop_run(m, &ret) < 0) {
-        fprintf(stderr, "pa_mainloop_run() failed.\n");
-        goto quit;
-    }
-    
-quit:
-    if (stream)
-        pa_stream_unref(stream);
-
-    if (context)
-        pa_context_unref(context);
-
-    if (stdio_event) {
-        assert(mainloop_api);
-        mainloop_api->io_free(stdio_event);
-    }
-    
-    if (m) {
-        pa_signal_done();
-        pa_mainloop_free(m);
-    }
-
-    free(buffer);
-
-    free(server);
-    free(device);
-    free(client_name);
-    free(stream_name);
-    
-    return ret;
-}
diff --git a/polyp/packet.c b/polyp/packet.c
deleted file mode 100644 (file)
index 84ac6d6..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "packet.h"
-#include "xmalloc.h"
-
-struct pa_packet* pa_packet_new(size_t length) {
-    struct pa_packet *p;
-    assert(length);
-    p = pa_xmalloc(sizeof(struct pa_packet)+length);
-    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 = pa_xmalloc(sizeof(struct pa_packet));
-    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)
-            pa_xfree(p->data);
-        pa_xfree(p);
-    }
-}
diff --git a/polyp/pdispatch.h b/polyp/pdispatch.h
deleted file mode 100644 (file)
index 571d0fb..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef foopdispatchhfoo
-#define foopdispatchhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include "tagstruct.h"
-#include "packet.h"
-#include "mainloop-api.h"
-
-struct pa_pdispatch;
-
-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_unref(struct pa_pdispatch *pd);
-struct pa_pdispatch* pa_pdispatch_ref(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/pid.c b/polyp/pid.c
deleted file mode 100644 (file)
index 2fac687..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <signal.h>
-
-#include "pid.h"
-#include "util.h"
-#include "log.h"
-
-/* Read the PID data from the file descriptor fd, and return it. If no
- * pid could be read, return 0, on failure (pid_t) -1 */
-static pid_t read_pid(const char *fn, int fd) {
-    ssize_t r;
-    char t[20], *e;
-    uint32_t pid;
-
-    assert(fn && fd >= 0);
-
-    if ((r = pa_loop_read(fd, t, sizeof(t)-1)) < 0) {
-        pa_log(__FILE__": WARNING: failed to read PID file '%s': %s\n", fn, strerror(errno));
-        return (pid_t) -1;
-    }
-
-    if (r == 0)
-        return (pid_t) 0;
-    
-    t[r] = 0;
-    if ((e = strchr(t, '\n')))
-        *e = 0;
-
-    if (pa_atou(t, &pid) < 0) {
-        pa_log(__FILE__": WARNING: failed to parse PID file '%s'\n", fn);
-        return (pid_t) -1;
-    }
-
-    return (pid_t) pid;
-}
-
-static int open_pid_file(const char *fn, int mode) {
-    int fd = -1;
-    int lock = -1;
-    
-    for (;;) {
-        struct stat st;
-        
-        pa_make_secure_parent_dir(fn);
-        
-        if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) {
-            if (mode != O_RDONLY || errno != ENOENT)
-                pa_log(__FILE__": WARNING: failed to open PID file '%s': %s\n", fn, strerror(errno));
-            goto fail;
-        }
-
-        /* Try to lock the file. If that fails, go without */
-        if (pa_lock_fd(fd, 1) < 0)
-            goto fail;
-        
-        if (fstat(fd, &st) < 0) {
-            pa_log(__FILE__": Failed to fstat() PID file '%s': %s\n", fn, strerror(errno));
-            goto fail;
-        }
-
-        /* Does the file still exist in the file system? When ye, w're done, otherwise restart */
-        if (st.st_nlink >= 1)
-            break;
-
-        if (pa_lock_fd(fd, 0) < 0)
-            goto fail;
-
-        if (close(fd) < 0) {
-            pa_log(__FILE__": Failed to close file '%s': %s\n", fn, strerror(errno));
-            goto fail;
-        }
-
-        fd = -1;
-    }
-
-    return fd;
-
-fail:
-
-    if (fd < 0) {
-        if (lock >= 0)
-            pa_lock_fd(fd, 0);
-        
-        close(fd);
-    }
-
-    return -1;
-}
-
-/* Create a new PID file for the current process. */
-int pa_pid_file_create(void) {
-    int fd = -1;
-    int ret = -1;
-    char fn[PATH_MAX];
-    char t[20];
-    pid_t pid;
-    size_t l;
-
-    pa_runtime_path("pid", fn, sizeof(fn));
-
-    if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
-        goto fail;
-
-    if ((pid = read_pid(fn, fd)) == (pid_t) -1)
-        pa_log(__FILE__": corrupt PID file, overwriting.\n");
-    else if (pid > 0) {
-        if (kill(pid, 0) >= 0 || errno != ESRCH) {
-            pa_log(__FILE__": daemon already running.\n");
-            goto fail;
-        }
-
-        pa_log(__FILE__": stale PID file, overwriting.\n");
-    }
-
-    /* Overwrite the current PID file */
-    if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) {
-        pa_log(__FILE__": failed to truncate PID fil: %s.\n", strerror(errno));
-        goto fail;
-    }
-    
-    snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
-    l = strlen(t);
-    
-    if (pa_loop_write(fd, t, l) != (ssize_t) l) {
-        pa_log(__FILE__": failed to write PID file.\n");
-        goto fail;
-    }
-
-    ret = 0;
-    
-fail:
-    if (fd >= 0) {
-        pa_lock_fd(fd, 0);
-        close(fd);
-    }
-    
-    return ret;
-}
-
-/* Remove the PID file, if it is ours */
-int pa_pid_file_remove(void) {
-    int fd = -1;
-    char fn[PATH_MAX];
-    int ret = -1;
-    pid_t pid;
-
-    pa_runtime_path("pid", fn, sizeof(fn));
-
-    if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
-        pa_log(__FILE__": WARNING: failed to open PID file '%s': %s\n", fn, strerror(errno));
-        goto fail;
-    }
-
-    if ((pid = read_pid(fn, fd)) == (pid_t) -1)
-        goto fail;
-
-    if (pid != getpid()) {
-        pa_log(__FILE__": WARNING: PID file '%s' not mine!\n", fn);
-        goto fail;
-    }
-
-    if (ftruncate(fd, 0) < 0) {
-        pa_log(__FILE__": failed to truncate PID file '%s': %s\n", fn, strerror(errno));
-        goto fail;
-    }
-
-    if (unlink(fn) < 0) {
-        pa_log(__FILE__": failed to remove PID file '%s': %s\n", fn, strerror(errno));
-        goto fail;
-    }
-
-    ret = 0;
-    
-fail:
-
-    if (fd >= 0) {
-        pa_lock_fd(fd, 0);
-        close(fd);
-    }
-
-    return ret;
-}
-
-/* Check whether the daemon is currently running, i.e. if a PID file
- * exists and the PID therein too. Returns 0 on succcess, -1
- * otherwise. If pid is non-NULL and a running daemon was found,
- * return its PID therein */
-int pa_pid_file_check_running(pid_t *pid) {
-    return pa_pid_file_kill(0, pid);
-}
-
-/* Kill a current running daemon. Return non-zero on success, -1
- * otherwise. If successful *pid contains the PID of the daemon
- * process. */
-int pa_pid_file_kill(int sig, pid_t *pid) {
-    int fd = -1;
-    char fn[PATH_MAX];
-    int ret = -1;
-    pid_t _pid;
-
-    if (!pid)
-        pid = &_pid;
-    
-    pa_runtime_path("pid", fn, sizeof(fn));
-    
-    if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
-        goto fail;
-    
-    if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
-        goto fail;
-    
-    ret = kill(*pid, sig);
-    
-fail:
-    
-    if (fd >= 0) {
-        pa_lock_fd(fd, 0);
-        close(fd);
-    }
-
-    return ret;
-    
-}
diff --git a/polyp/play-memchunk.c b/polyp/play-memchunk.c
deleted file mode 100644 (file)
index c7a8545..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "play-memchunk.h"
-#include "sink-input.h"
-#include "xmalloc.h"
-
-#define PA_TYPEID_MEMCHUNK PA_TYPEID_MAKE('M', 'C', 'N', 'K')
-
-static void sink_input_kill(struct pa_sink_input *i) {
-    struct pa_memchunk *c;
-    assert(i && i->userdata);
-    c = i->userdata;
-
-    pa_sink_input_disconnect(i);
-    pa_sink_input_unref(i);
-
-    pa_memblock_unref(c->memblock);
-    pa_xfree(c);
-    
-}
-
-static int sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) {
-    struct pa_memchunk *c;
-    assert(i && chunk && i->userdata);
-    c = i->userdata;
-
-    if (c->length <= 0)
-        return -1;
-    
-    assert(c->memblock && c->memblock->length);
-    *chunk = *c;
-    pa_memblock_ref(c->memblock);
-
-    return 0;
-}
-
-static void si_kill(struct pa_mainloop_api *m, void *i) {
-    sink_input_kill(i);
-}
-
-static void sink_input_drop(struct pa_sink_input *i, const struct pa_memchunk*chunk, size_t length) {
-    struct pa_memchunk *c;
-    assert(i && length && i->userdata);
-    c = i->userdata;
-
-    assert(!memcmp(chunk, c, sizeof(chunk)));
-    assert(length <= c->length);
-
-    c->length -= length;
-    c->index += length;
-
-    if (c->length <= 0)
-        pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i);
-}
-
-int pa_play_memchunk(struct pa_sink *sink, const char *name, const struct pa_sample_spec *ss, const struct pa_memchunk *chunk, pa_volume_t volume) {
-    struct pa_sink_input *si;
-    struct pa_memchunk *nchunk;
-
-    assert(sink && chunk);
-
-    if (volume <= 0)
-        return 0;
-
-    if (!(si = pa_sink_input_new(sink, PA_TYPEID_MEMCHUNK, name, ss, 0, -1)))
-        return -1;
-
-    si->volume = volume;
-    si->peek = sink_input_peek;
-    si->drop = sink_input_drop;
-    si->kill = sink_input_kill;
-    
-    si->userdata = nchunk = pa_xmalloc(sizeof(struct pa_memchunk));
-    *nchunk = *chunk;
-    
-    pa_memblock_ref(chunk->memblock);
-
-    pa_sink_notify(sink);
-    
-    return 0;
-}
diff --git a/polyp/polyplib-browser.c b/polyp/polyplib-browser.c
deleted file mode 100644 (file)
index 2e75a42..0000000
+++ /dev/null
@@ -1,312 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <assert.h>
-#include <howl.h>
-
-#include "polyplib-browser.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "util.h"
-
-#define SERVICE_NAME_SINK "_polypaudio-sink._tcp."
-#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp."
-#define SERVICE_NAME_SERVER "_polypaudio-server._tcp."
-
-struct pa_browser {
-    int ref;
-    struct pa_mainloop_api *mainloop;
-
-    void (*callback)(struct pa_browser *z, enum pa_browse_opcode c, const struct pa_browse_info *i, void *userdata);
-    void *callback_userdata;
-    
-    sw_discovery discovery;
-    struct pa_io_event *io_event;
-};
-
-
-static void io_callback(struct pa_mainloop_api*a, struct pa_io_event*e, int fd, enum pa_io_event_flags events, void *userdata) {
-    struct pa_browser *b = userdata;
-    assert(a && b && b->mainloop == a);
-
-    if (events != PA_IO_EVENT_INPUT || sw_discovery_read_socket(b->discovery) != SW_OKAY) {
-        pa_log(__FILE__": connection to HOWL daemon failed.\n");
-        b->mainloop->io_free(b->io_event);
-        b->io_event = NULL;
-        return;
-    }
-}
-
-static sw_result resolve_reply(
-    sw_discovery discovery,
-    sw_discovery_oid oid,
-    sw_uint32 interface_index,
-    sw_const_string name,
-    sw_const_string type,
-    sw_const_string domain,
-    sw_ipv4_address address,
-    sw_port port,
-    sw_octets text_record,
-    sw_ulong text_record_len,
-    sw_opaque extra) {
-    
-    struct pa_browser *b = extra;
-    struct pa_browse_info i;
-    char ip[256], a[256];
-    enum pa_browse_opcode opcode;
-    int device_found = 0;
-    uint32_t cookie;
-    pa_typeid_t typeid;
-    struct pa_sample_spec ss;
-    int ss_valid = 0;
-    sw_text_record_iterator iterator;
-    int free_iterator = 0;
-    char *c = NULL;
-    
-    assert(b);
-
-    sw_discovery_cancel(discovery, oid);
-
-    memset(&i, 0, sizeof(i));
-    i.name = name;
-        
-    if (!b->callback)
-        goto fail;
-    
-    if (!strcmp(type, SERVICE_NAME_SINK))
-        opcode = PA_BROWSE_NEW_SINK;
-    else if (!strcmp(type, SERVICE_NAME_SOURCE))
-        opcode = PA_BROWSE_NEW_SOURCE;
-    else if (!strcmp(type, SERVICE_NAME_SERVER))
-        opcode = PA_BROWSE_NEW_SERVER;
-    else
-        goto fail;
-    
-
-    snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port);
-    i.server = a;
-    
-    if (text_record && text_record_len) {
-        char key[SW_TEXT_RECORD_MAX_LEN];
-        uint8_t val[SW_TEXT_RECORD_MAX_LEN];
-        uint32_t val_len;
-  
-        if (sw_text_record_iterator_init(&iterator, text_record, text_record_len) != SW_OKAY) {
-            pa_log("sw_text_record_string_iterator_init() failed.\n");
-            goto fail;
-        }
-
-        free_iterator = 1;
-        
-        while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) {
-            c = pa_xstrndup((char*) val, val_len);
-            
-            if (!strcmp(key, "device")) {
-                device_found = 1;
-                pa_xfree((char*) i.device);
-                i.device = c;
-                c = NULL;
-            } else if (!strcmp(key, "server-version")) {
-                pa_xfree((char*) i.server_version);
-                i.server_version = c;
-                c = NULL;
-            } else if (!strcmp(key, "user-name")) {
-                pa_xfree((char*) i.user_name);
-                i.user_name = c;
-                c = NULL;
-            } else if (!strcmp(key, "fqdn")) {
-                size_t l;
-                
-                pa_xfree((char*) i.fqdn);
-                i.fqdn = c;
-                c = NULL;
-                
-                l = strlen(a);
-                assert(l+1 <= sizeof(a));
-                strncat(a, " ", sizeof(a)-l-1);
-                strncat(a, i.fqdn, sizeof(a)-l-2);
-            } else if (!strcmp(key, "cookie")) {
-
-                if (pa_atou(c, &cookie) < 0)
-                    goto fail;
-                
-                i.cookie = &cookie;
-            } else if (!strcmp(key, "description")) {
-                pa_xfree((char*) i.description);
-                i.description = c;
-                c = NULL;
-            } else if (!strcmp(key, "typeid")) {
-
-                if (pa_atou(c, &typeid) < 0)
-                    goto fail;
-
-                i.typeid = &typeid;
-            } else if (!strcmp(key, "channels")) {
-                uint32_t ch;
-                
-                if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255)
-                    goto fail;
-
-                ss.channels = (uint8_t) ch;
-                ss_valid |= 1;
-
-            } else if (!strcmp(key, "rate")) {
-                if (pa_atou(c, &ss.rate) < 0)
-                    goto fail;
-                ss_valid |= 2;
-            } else if (!strcmp(key, "format")) {
-
-                if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID)
-                    goto fail;
-                
-                ss_valid |= 4;
-            }
-
-            pa_xfree(c);
-            c = NULL;
-        }
-        
-    }
-
-    /* No device txt record was sent for a sink or source service */
-    if (opcode != PA_BROWSE_NEW_SERVER && !device_found) 
-        goto fail;
-
-    if (ss_valid == 7)
-        i.sample_spec = &ss;
-    
-
-    b->callback(b, opcode, &i, b->callback_userdata);
-
-fail:
-    pa_xfree((void*) i.device);
-    pa_xfree((void*) i.fqdn);
-    pa_xfree((void*) i.server_version);
-    pa_xfree((void*) i.user_name);
-    pa_xfree((void*) i.description);
-    pa_xfree(c);
-
-    if (free_iterator)
-        sw_text_record_iterator_fina(iterator);
-
-    
-    return SW_OKAY;
-}
-
-static sw_result browse_reply(
-    sw_discovery discovery,
-    sw_discovery_oid id,
-    sw_discovery_browse_status status,
-    sw_uint32 interface_index,
-    sw_const_string name,
-    sw_const_string type,
-    sw_const_string domain,
-    sw_opaque extra) {
-    
-    struct pa_browser *b = extra;
-    assert(b);
-
-    switch (status) {
-        case SW_DISCOVERY_BROWSE_ADD_SERVICE: {
-            sw_discovery_oid oid;
-
-            if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY)
-                pa_log("sw_discovery_resolve() failed\n");
-
-            break;
-        }
-            
-        case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
-            if (b->callback) {
-                struct pa_browse_info i;
-                memset(&i, 0, sizeof(i));
-                i.name = name;
-                b->callback(b, PA_BROWSE_REMOVE, &i, b->callback_userdata);
-            }
-            break;
-
-        default:
-            ;
-    }
-
-    return SW_OKAY;
-}
-
-struct pa_browser *pa_browser_new(struct pa_mainloop_api *mainloop) {
-    struct pa_browser *b;
-    sw_discovery_oid oid;
-
-    b = pa_xmalloc(sizeof(struct pa_browser));
-    b->mainloop = mainloop;
-    b->ref = 1;
-    b->callback = NULL;
-    b->callback_userdata = NULL;
-
-    if (sw_discovery_init(&b->discovery) != SW_OKAY) {
-        pa_log("sw_discovery_init() failed.\n");
-        pa_xfree(b);
-        return NULL;
-    }
-    
-    if (sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SERVER, NULL, browse_reply, b, &oid) != SW_OKAY ||
-        sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SINK, NULL, browse_reply, b, &oid) != SW_OKAY ||
-        sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SOURCE, NULL, browse_reply, b, &oid) != SW_OKAY) {
-
-        pa_log("sw_discovery_browse() failed.\n");
-        
-        sw_discovery_fina(b->discovery);
-        pa_xfree(b);
-        return NULL;
-    }
-    
-    b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b);
-    return b;
-}
-
-static void browser_free(struct pa_browser *b) {
-    assert(b && b->mainloop);
-
-    if (b->io_event)
-        b->mainloop->io_free(b->io_event);
-    
-    sw_discovery_fina(b->discovery);
-    pa_xfree(b);
-}
-
-struct pa_browser *pa_browser_ref(struct pa_browser *b) {
-    assert(b && b->ref >= 1);
-    b->ref++;
-    return b;
-}
-
-void pa_browser_unref(struct pa_browser *b) {
-    assert(b && b->ref >= 1);
-
-    if ((-- (b->ref)) <= 0)
-        browser_free(b);
-}
-
-void pa_browser_set_callback(struct pa_browser *b, void (*cb)(struct pa_browser *z, enum pa_browse_opcode c, const struct pa_browse_info *i, void* userdata), void *userdata) {
-    assert(b);
-
-    b->callback = cb;
-    b->callback_userdata = userdata;
-}
diff --git a/polyp/polyplib-browser.h b/polyp/polyplib-browser.h
deleted file mode 100644 (file)
index c13eff2..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef foopolyplibbrowserhfoo
-#define foopolyplibbrowserhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <polyp/mainloop-api.h>
-#include <polyp/sample.h>
-#include <polyp/cdecl.h>
-#include <polyp/typeid.h>
-
-PA_C_DECL_BEGIN
-
-struct pa_browser;
-
-enum pa_browse_opcode {
-    PA_BROWSE_NEW_SERVER,
-    PA_BROWSE_NEW_SINK,
-    PA_BROWSE_NEW_SOURCE,
-    PA_BROWSE_REMOVE
-};
-
-struct pa_browser *pa_browser_new(struct pa_mainloop_api *mainloop);
-struct pa_browser *pa_browser_ref(struct pa_browser *z);
-void pa_browser_unref(struct pa_browser *z);
-
-struct pa_browse_info {
-    /* Unique service name */
-    const char *name;  /* always available */
-
-    /* Server info */
-    const char *server; /* always available */
-    const char *server_version, *user_name, *fqdn; /* optional */
-    const uint32_t *cookie;  /* optional */
-
-    /* Device info */
-    const char *device; /* always available when this information is of a sink/source */
-    const char *description;  /* optional */
-    const pa_typeid_t *typeid;  /* optional */
-    const struct pa_sample_spec *sample_spec;  /* optional */
-};
-
-void pa_browser_set_callback(struct pa_browser *z, void (*cb)(struct pa_browser *z, enum pa_browse_opcode c, const struct pa_browse_info *i, void *userdata), void *userdata);
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/polyplib-context.c b/polyp/polyplib-context.c
deleted file mode 100644 (file)
index bca7d7e..0000000
+++ /dev/null
@@ -1,852 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <sys/wait.h>
-#include <signal.h>
-
-#include "polyplib-internal.h"
-#include "polyplib-context.h"
-#include "native-common.h"
-#include "pdispatch.h"
-#include "pstream.h"
-#include "dynarray.h"
-#include "socket-client.h"
-#include "pstream-util.h"
-#include "util.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "client-conf.h"
-#include "socket-util.h"
-
-#ifdef HAVE_X11
-#include "client-conf-x11.h"
-#endif
-
-#define AUTOSPAWN_LOCK "autospawn.lock"
-
-static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = {
-    [PA_COMMAND_REQUEST] = { pa_command_request },
-    [PA_COMMAND_PLAYBACK_STREAM_KILLED] = { pa_command_stream_killed },
-    [PA_COMMAND_RECORD_STREAM_KILLED] = { pa_command_stream_killed },
-    [PA_COMMAND_SUBSCRIBE_EVENT] = { pa_command_subscribe_event },
-};
-
-static void unlock_autospawn_lock_file(struct pa_context *c) {
-    assert(c);
-    
-    if (c->autospawn_lock_fd >= 0) {
-        char lf[PATH_MAX];
-        pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
-        
-        pa_unlock_lockfile(lf, c->autospawn_lock_fd);
-        c->autospawn_lock_fd = -1;
-    }
-}
-
-struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name) {
-    struct pa_context *c;
-    assert(mainloop && name);
-    
-    c = pa_xmalloc(sizeof(struct pa_context));
-    c->ref = 1;
-    c->name = pa_xstrdup(name);
-    c->mainloop = mainloop;
-    c->client = NULL;
-    c->pstream = NULL;
-    c->pdispatch = NULL;
-    c->playback_streams = pa_dynarray_new();
-    c->record_streams = pa_dynarray_new();
-    assert(c->playback_streams && c->record_streams);
-
-    PA_LLIST_HEAD_INIT(struct pa_stream, c->streams);
-    PA_LLIST_HEAD_INIT(struct pa_operation, c->operations);
-    
-    c->error = PA_ERROR_OK;
-    c->state = PA_CONTEXT_UNCONNECTED;
-    c->ctag = 0;
-
-    c->state_callback = NULL;
-    c->state_userdata = NULL;
-
-    c->subscribe_callback = NULL;
-    c->subscribe_userdata = NULL;
-
-    c->memblock_stat = pa_memblock_stat_new();
-    c->local = -1;
-    c->server_list = NULL;
-    c->server = NULL;
-    c->autospawn_lock_fd = -1;
-    memset(&c->spawn_api, 0, sizeof(c->spawn_api));
-    c->do_autospawn = 0;
-    
-    pa_check_signal_is_blocked(SIGPIPE);
-
-    c->conf = pa_client_conf_new();
-    pa_client_conf_load(c->conf, NULL);
-#ifdef HAVE_X11
-    pa_client_conf_from_x11(c->conf, NULL);
-#endif
-    pa_client_conf_env(c->conf);
-    
-    return c;
-}
-
-static void context_free(struct pa_context *c) {
-    assert(c);
-
-    unlock_autospawn_lock_file(c);
-
-    while (c->operations)
-        pa_operation_cancel(c->operations);
-
-    while (c->streams)
-        pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
-    
-    if (c->client)
-        pa_socket_client_unref(c->client);
-    if (c->pdispatch)
-        pa_pdispatch_unref(c->pdispatch);
-    if (c->pstream) {
-        pa_pstream_close(c->pstream);
-        pa_pstream_unref(c->pstream);
-    }
-    
-    if (c->record_streams)
-        pa_dynarray_free(c->record_streams, NULL, NULL);
-    if (c->playback_streams)
-        pa_dynarray_free(c->playback_streams, NULL, NULL);
-
-    pa_memblock_stat_unref(c->memblock_stat);
-
-    if (c->conf)
-        pa_client_conf_free(c->conf);
-
-    pa_strlist_free(c->server_list);
-    
-    pa_xfree(c->name);
-    pa_xfree(c->server);
-    pa_xfree(c);
-}
-
-struct pa_context* pa_context_ref(struct pa_context *c) {
-    assert(c && c->ref >= 1);
-    c->ref++;
-    return c;
-}
-
-void pa_context_unref(struct pa_context *c) {
-    assert(c && c->ref >= 1);
-
-    if ((--(c->ref)) == 0)
-        context_free(c);
-}
-
-void pa_context_set_state(struct pa_context *c, enum pa_context_state st) {
-    assert(c);
-    
-    if (c->state == st)
-        return;
-
-    pa_context_ref(c);
-
-    if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) {
-        struct pa_stream *s;
-        
-        s = c->streams ? pa_stream_ref(c->streams) : NULL;
-        while (s) {
-            struct pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
-            pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
-            pa_stream_unref(s);
-            s = n;
-        }
-
-        if (c->pdispatch)
-            pa_pdispatch_unref(c->pdispatch);
-        c->pdispatch = NULL;
-    
-        if (c->pstream) {
-            pa_pstream_close(c->pstream);
-            pa_pstream_unref(c->pstream);
-        }
-        c->pstream = NULL;
-    
-        if (c->client)
-            pa_socket_client_unref(c->client);
-        c->client = NULL;
-    }
-
-    c->state = st;
-    if (c->state_callback)
-        c->state_callback(c, c->state_userdata);
-
-    pa_context_unref(c);
-}
-
-void pa_context_fail(struct pa_context *c, int error) {
-    assert(c);
-    c->error = error;
-    pa_context_set_state(c, PA_CONTEXT_FAILED);
-}
-
-static void pstream_die_callback(struct pa_pstream *p, void *userdata) {
-    struct pa_context *c = userdata;
-    assert(p && c);
-    pa_context_fail(c, PA_ERROR_CONNECTIONTERMINATED);
-}
-
-static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) {
-    struct pa_context *c = userdata;
-    assert(p && packet && c);
-
-    pa_context_ref(c);
-    
-    if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) {
-        pa_log(__FILE__": invalid packet.\n");
-        pa_context_fail(c, PA_ERROR_PROTOCOL);
-    }
-
-    pa_context_unref(c);
-}
-
-static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, uint32_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);
-
-    pa_context_ref(c);
-    
-    if ((s = pa_dynarray_get(c->record_streams, channel))) {
-        pa_mcalign_push(s->mcalign, chunk);
-
-        for (;;) {
-            struct pa_memchunk t;
-
-            if (pa_mcalign_pop(s->mcalign, &t) < 0)
-                break;
-        
-            if (s->read_callback) {
-                s->read_callback(s, (uint8_t*) t.memblock->data + t.index, t.length, s->read_userdata);
-                s->counter += chunk->length;
-            }
-
-            pa_memblock_unref(t.memblock);
-        }
-    }
-
-    pa_context_unref(c);
-}
-
-int pa_context_handle_error(struct pa_context *c, uint32_t command, struct pa_tagstruct *t) {
-    assert(c);
-
-    if (command == PA_COMMAND_ERROR) {
-        assert(t);
-        
-        if (pa_tagstruct_getu32(t, &c->error) < 0) {
-            pa_context_fail(c, PA_ERROR_PROTOCOL);
-            return -1;
-                
-        }
-    } else if (command == PA_COMMAND_TIMEOUT)
-        c->error = PA_ERROR_TIMEOUT;
-    else {
-        pa_context_fail(c, PA_ERROR_PROTOCOL);
-        return -1;
-    }
-
-    return 0;
-}
-
-static void setup_complete_callback(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 == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME));
-
-    pa_context_ref(c);
-    
-    if (command != PA_COMMAND_REPLY) {
-        
-        if (pa_context_handle_error(c, command, t) < 0)
-            pa_context_fail(c, PA_ERROR_PROTOCOL);
-
-        pa_context_fail(c, c->error);
-        goto finish;
-    }
-
-    switch(c->state) {
-        case PA_CONTEXT_AUTHORIZING: {
-            struct pa_tagstruct *t;
-            t = pa_tagstruct_new(NULL, 0);
-            assert(t);
-            pa_tagstruct_putu32(t, PA_COMMAND_SET_CLIENT_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);
-
-            pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
-            break;
-        }
-
-        case PA_CONTEXT_SETTING_NAME :
-            pa_context_set_state(c, PA_CONTEXT_READY);
-            break;
-            
-        default:
-            assert(0);
-    }
-
-finish:
-    pa_context_unref(c);
-}
-
-static void setup_context(struct pa_context *c, struct pa_iochannel *io) {
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && io);
-
-    pa_context_ref(c);
-    
-    assert(!c->pstream);
-    c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat);
-    assert(c->pstream);
-    
-    pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
-    pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
-    pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);
-
-    assert(!c->pdispatch);
-    c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
-    assert(c->pdispatch);
-
-    if (!c->conf->cookie_valid) {
-        pa_context_fail(c, PA_ERROR_AUTHKEY);
-        goto finish;
-    }
-
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie));
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c);
-
-    pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);
-
-finish:
-    
-    pa_context_unref(c);
-}
-
-static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata);
-
-static int context_connect_spawn(struct pa_context *c) {
-    pid_t pid;
-    int status, r;
-    int fds[2] = { -1, -1} ;
-    struct pa_iochannel *io;
-
-    pa_context_ref(c);
-    
-    if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
-        pa_log(__FILE__": socketpair() failed: %s\n", strerror(errno));
-        pa_context_fail(c, PA_ERROR_INTERNAL);
-        goto fail;
-    }
-
-    pa_fd_set_cloexec(fds[0], 1);
-    
-    pa_socket_low_delay(fds[0]);
-    pa_socket_low_delay(fds[1]);
-
-    if (c->spawn_api.prefork)
-        c->spawn_api.prefork();
-
-    if ((pid = fork()) < 0) {
-        pa_log(__FILE__": fork() failed: %s\n", strerror(errno));
-        pa_context_fail(c, PA_ERROR_INTERNAL);
-
-        if (c->spawn_api.postfork)
-            c->spawn_api.postfork();
-        
-        goto fail;
-    } else if (!pid) {
-        /* Child */
-
-        char t[128];
-        const char *state = NULL;
-#define MAX_ARGS 64
-        char *argv[MAX_ARGS+1];
-        int n;
-
-        /* Not required, since fds[0] has CLOEXEC enabled anyway */
-        close(fds[0]);
-        
-        if (c->spawn_api.atfork)
-            c->spawn_api.atfork();
-
-        /* Setup argv */
-
-        n = 0;
-        
-        argv[n++] = c->conf->daemon_binary;
-        argv[n++] = "--daemonize=yes";
-        
-        snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]);
-        argv[n++] = strdup(t);
-
-        while (n < MAX_ARGS) {
-            char *a;
-
-            if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
-                break;
-            
-            argv[n++] = a;
-        }
-
-        argv[n++] = NULL;
-
-        execv(argv[0], argv);
-        _exit(1);
-#undef MAX_ARGS
-    } 
-
-    /* Parent */
-
-    r = waitpid(pid, &status, 0);
-
-    if (c->spawn_api.postfork)
-        c->spawn_api.postfork();
-        
-    if (r < 0) {
-        pa_log(__FILE__": waitpid() failed: %s\n", strerror(errno));
-        pa_context_fail(c, PA_ERROR_INTERNAL);
-        goto fail;
-    } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-        pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED);
-        goto fail;
-    }
-
-    close(fds[1]);
-
-    c->local = 1;
-    
-    io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
-
-    setup_context(c, io);
-    unlock_autospawn_lock_file(c);
-
-    pa_context_unref(c);
-
-    return 0;
-
-fail:
-    if (fds[0] != -1)
-        close(fds[0]);
-    if (fds[1] != -1)
-        close(fds[1]);
-
-    unlock_autospawn_lock_file(c);
-
-    pa_context_unref(c);
-
-    return -1;
-}
-
-static int try_next_connection(struct pa_context *c) {
-    char *u = NULL;
-    int r = -1;
-    assert(c && !c->client);
-
-    for (;;) {
-        if (u)
-            pa_xfree(u);
-        u = NULL;
-        
-        c->server_list = pa_strlist_pop(c->server_list, &u);
-        
-        if (!u) {
-
-            if (c->do_autospawn) {
-                r = context_connect_spawn(c);
-                goto finish;
-            }
-            
-            pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED);
-            goto finish;
-        }
-        
-        pa_log_debug(__FILE__": Trying to connect to %s...\n", u);  
-
-        pa_xfree(c->server);
-        c->server = pa_xstrdup(u);
-
-        if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
-            continue;
-        
-        c->local = pa_socket_client_is_local(c->client);
-        pa_socket_client_set_callback(c->client, on_connection, c);
-        break;
-    }
-
-    r = 0;
-
-finish:
-    if (u)
-        pa_xfree(u);
-    
-    return r;
-}
-
-static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata) {
-    struct pa_context *c = userdata;
-    assert(client && c && c->state == PA_CONTEXT_CONNECTING);
-
-    pa_context_ref(c);
-
-    pa_socket_client_unref(client);
-    c->client = NULL;
-
-    if (!io) {
-        /* Try the item in the list */
-        if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) {
-            try_next_connection(c);
-            goto finish;
-        }
-
-        pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED);
-        goto finish;
-    }
-
-    unlock_autospawn_lock_file(c);
-    setup_context(c, io);
-
-finish:
-    pa_context_unref(c);
-}
-
-int pa_context_connect(struct pa_context *c, const char *server, int spawn, const struct pa_spawn_api *api) {
-    int r = -1;
-    assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED);
-
-    if (!server)
-        server = c->conf->default_server;
-
-    pa_context_ref(c);
-
-    assert(!c->server_list);
-    
-    if (server) {
-        if (!(c->server_list = pa_strlist_parse(server))) {
-            pa_context_fail(c, PA_ERROR_INVALIDSERVER);
-            goto finish;
-        }
-    } else {
-        char *d;
-        char ufn[PATH_MAX];
-
-        /* Prepend in reverse order */
-        
-        if ((d = getenv("DISPLAY"))) {
-            char *e;
-            d = pa_xstrdup(d);
-            if ((e = strchr(d, ':')))
-                *e = 0;
-
-            if (*d)
-                c->server_list = pa_strlist_prepend(c->server_list, d);
-
-            pa_xfree(d);
-        }
-        
-        c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost");
-        c->server_list = pa_strlist_prepend(c->server_list, "localhost");
-        c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn)));
-
-        /* Wrap the connection attempts in a single transaction for sane autospawn locking */
-        if (spawn && c->conf->autospawn) {
-            char lf[PATH_MAX];
-
-            pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
-            pa_make_secure_parent_dir(lf);
-            assert(c->autospawn_lock_fd <= 0);
-            c->autospawn_lock_fd = pa_lock_lockfile(lf);
-
-            if (api)
-                c->spawn_api = *api;
-            c->do_autospawn = 1;
-        }
-
-    }
-
-    pa_context_set_state(c, PA_CONTEXT_CONNECTING);
-    r = try_next_connection(c);
-    
-finish:
-    pa_context_unref(c);
-    
-    return r;
-}
-
-void pa_context_disconnect(struct pa_context *c) {
-    assert(c);
-    pa_context_set_state(c, PA_CONTEXT_TERMINATED);
-}
-
-enum pa_context_state pa_context_get_state(struct pa_context *c) {
-    assert(c && c->ref >= 1);
-    return c->state;
-}
-
-int pa_context_errno(struct pa_context *c) {
-    assert(c && c->ref >= 1);
-    return c->error;
-}
-
-void pa_context_set_state_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata) {
-    assert(c && c->ref >= 1);
-    c->state_callback = cb;
-    c->state_userdata = userdata;
-}
-
-int pa_context_is_pending(struct pa_context *c) {
-    assert(c && c->ref >= 1);
-
-/*     pa_log("pstream: %i\n", pa_pstream_is_pending(c->pstream)); */
-/*     pa_log("pdispatch: %i\n", pa_pdispatch_is_pending(c->pdispatch)); */
-    
-    return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
-        (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
-        c->client;
-}
-
-static void set_dispatch_callbacks(struct pa_operation *o);
-
-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_operation *o) {
-    int done = 1;
-    assert(o && o->context && o->context->ref >= 1 && o->ref >= 1 && o->context->state == PA_CONTEXT_READY);
-
-    pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL);
-    pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL);
-    
-    if (pa_pdispatch_is_pending(o->context->pdispatch)) {
-        pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o);
-        done = 0;
-    }
-
-    if (pa_pstream_is_pending(o->context->pstream)) {
-        pa_pstream_set_drain_callback(o->context->pstream, pstream_drain_callback, o);
-        done = 0;
-    }
-
-    if (!done)
-        pa_operation_ref(o);
-    else {
-        if (o->callback) {
-            void (*cb)(struct pa_context *c, void *userdata);
-            cb = (void (*)(struct pa_context*, void*)) o->callback;
-            cb(o->context, o->userdata);
-        }
-        
-        pa_operation_done(o);
-    }   
-
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_drain(struct pa_context *c, void (*cb) (struct pa_context*c, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    assert(c && c->ref >= 1);
-
-    if (c->state != PA_CONTEXT_READY)
-        return NULL;
-
-    if (!pa_context_is_pending(c))
-        return NULL;
-
-    o = pa_operation_new(c, NULL);
-    assert(o);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    set_dispatch_callbacks(pa_operation_ref(o));
-
-    return o;
-}
-
-void pa_context_exit_daemon(struct pa_context *c) {
-    struct pa_tagstruct *t;
-    assert(c && c->ref >= 1);
-    
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    pa_tagstruct_putu32(t, PA_COMMAND_EXIT);
-    pa_tagstruct_putu32(t, c->ctag++);
-    pa_pstream_send_tagstruct(c->pstream, t);
-}
-
-void pa_context_simple_ack_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    int success = 1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        success = 0;
-    } else if (!pa_tagstruct_eof(t)) {
-        pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-
-    if (o->callback) {
-        void (*cb)(struct pa_context *c, int success, void *userdata) = o->callback;
-        cb(o->context, success, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_send_simple_command(struct pa_context *c, uint32_t command, void (*internal_callback)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void (*cb)(), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, command);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_set_default_sink(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SINK);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_puts(t, name);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT,  pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_set_default_source(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success,  void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SOURCE);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_puts(t, name);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT,  pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-int pa_context_is_local(struct pa_context *c) {
-    assert(c);
-    return c->local;
-}
-
-struct pa_operation* pa_context_set_name(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success,  void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && name && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_SET_CLIENT_NAME);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_puts(t, name);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT,  pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-const char* pa_get_library_version(void) {
-    return PACKAGE_VERSION;
-}
-
-const char* pa_context_get_server(struct pa_context *c) {
-
-    if (!c->server)
-        return NULL;
-    
-    if (*c->server == '{') {
-        char *e = strchr(c->server+1, '}');
-        return e ? e+1 : c->server;
-    }
-    
-    return c->server;
-}
diff --git a/polyp/polyplib-context.h b/polyp/polyplib-context.h
deleted file mode 100644 (file)
index 0283312..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-#ifndef foopolyplibcontexthfoo
-#define foopolyplibcontexthfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <polyp/sample.h>
-#include <polyp/polyplib-def.h>
-#include <polyp/mainloop-api.h>
-#include <polyp/cdecl.h>
-#include <polyp/polyplib-operation.h>
-
-/** \file
- * Connection contexts for asynchrononous communication with a
- * server. A pa_context object wraps a connection to a polypaudio
- * server using its native protocol. A context may be used to issue
- * commands on the server or to create playback or recording
- * streams. Multiple playback streams may be piped through a single
- * connection context. Operations on the contect involving
- * communication with the server are executed asynchronously: i.e. the
- * client function do not implicitely wait for completion of the
- * operation on the server. Instead the caller specifies a call back
- * function that is called when the operation is completed. Currently
- * running operations may be canceled using pa_operation_cancel(). */
-
-/** \example pacat.c
- * A playback and recording tool using the asynchronous API */
-
-/** \example paplay.c
- * A sound file playback tool using the asynchronous API, based on libsndfile */
-
-PA_C_DECL_BEGIN
-
-/** \struct pa_context
- * An opaque connection context to a daemon */
-struct pa_context;
-
-/** Instantiate a new connection context with an abstract mainloop API
- * and an application name */
-struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name);
-
-/** Decrease the reference counter of the context by one */
-void pa_context_unref(struct pa_context *c);
-
-/** Increase the reference counter of the context by one */
-struct pa_context* pa_context_ref(struct pa_context *c);
-
-/** Set a callback function that is called whenever the context status changes */
-void pa_context_set_state_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata);
-
-/** Return the error number of the last failed operation */
-int pa_context_errno(struct pa_context *c);
-
-/** Return non-zero if some data is pending to be written to the connection */
-int pa_context_is_pending(struct pa_context *c);
-
-/** Return the current context status */
-enum pa_context_state pa_context_get_state(struct pa_context *c);
-
-/** Connect the context to the specified server. If server is NULL,
-connect to the default server. This routine may but will not always
-return synchronously on error. Use pa_context_set_state_callback() to
-be notified when the connection is established. If spawn is non-zero
-and no specific server is specified or accessible a new daemon is
-spawned. If api is non-NULL, the functions specified in the structure
-are used when forking a new child process. */
-int pa_context_connect(struct pa_context *c, const char *server, int spawn, const struct pa_spawn_api *api);
-
-/** Terminate the context connection immediately */
-void pa_context_disconnect(struct pa_context *c);
-
-/** Drain the context. If there is nothing to drain, the function returns NULL */
-struct pa_operation* pa_context_drain(struct pa_context *c, void (*cb) (struct pa_context*c, void *userdata), void *userdata);
-
-/** Tell the daemon to exit. No operation object is returned as the
- * connection is terminated when the daemon quits, thus this operation
- * would never complete. */
-void pa_context_exit_daemon(struct pa_context *c);
-
-/** Set the name of the default sink. \since 0.4 */
-struct pa_operation* pa_context_set_default_sink(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success, void *userdata), void *userdata);
-
-/** Set the name of the default source. \since 0.4 */
-struct pa_operation* pa_context_set_default_source(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success,  void *userdata), void *userdata);
-
-/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */
-int pa_context_is_local(struct pa_context *c);
-
-/** Set a different application name for context on the server. \since 0.5 */
-struct pa_operation* pa_context_set_name(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success,  void *userdata), void *userdata);
-
-/** Return the server name this context is connected to. \since 0.7 */
-const char* pa_context_get_server(struct pa_context *c);
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/polyplib-def.h b/polyp/polyplib-def.h
deleted file mode 100644 (file)
index 9adb68f..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-#ifndef foopolyplibdefhfoo
-#define foopolyplibdefhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include <sys/time.h>
-#include <time.h>
-
-#include <polyp/cdecl.h>
-#include <polyp/sample.h>
-
-/** \file
- * Global definitions */
-
-PA_C_DECL_BEGIN
-
-/** The state of a connection context */
-enum pa_context_state {
-    PA_CONTEXT_UNCONNECTED,    /**< The context hasn't been connected yet */
-    PA_CONTEXT_CONNECTING,     /**< A connection is being established */
-    PA_CONTEXT_AUTHORIZING,    /**< The client is authorizing itself to the daemon */
-    PA_CONTEXT_SETTING_NAME,   /**< The client is passing its application name to the daemon */
-    PA_CONTEXT_READY,          /**< The connection is established, the context is ready to execute operations */
-    PA_CONTEXT_FAILED,         /**< The connection failed or was disconnected */
-    PA_CONTEXT_TERMINATED      /**< The connection was terminated cleanly */
-};
-
-/** The state of a stream */
-enum pa_stream_state {
-    PA_STREAM_DISCONNECTED, /**< The stream is not yet connected to any sink or source */
-    PA_STREAM_CREATING,     /**< The stream is being created */
-    PA_STREAM_READY,        /**< The stream is established, you may pass audio data to it now */
-    PA_STREAM_FAILED,       /**< An error occured that made the stream invalid */
-    PA_STREAM_TERMINATED    /**< The stream has been terminated cleanly */
-};
-
-/** The state of an operation */
-enum pa_operation_state {
-    PA_OPERATION_RUNNING,      /**< The operation is still running */
-    PA_OPERATION_DONE,         /**< The operation has been completed */
-    PA_OPERATION_CANCELED      /**< The operation has been canceled */
-};
-
-/** An invalid index */
-#define PA_INVALID_INDEX ((uint32_t) -1)
-
-/** The direction of a pa_stream object */ 
-enum pa_stream_direction {
-    PA_STREAM_NODIRECTION,   /**< Invalid direction */
-    PA_STREAM_PLAYBACK,      /**< Playback stream */
-    PA_STREAM_RECORD,        /**< Record stream */
-    PA_STREAM_UPLOAD         /**< Sample upload stream */
-};
-
-/** Some special flags for stream connections. \since 0.6 */
-enum pa_stream_flags {
-    PA_STREAM_START_CORKED = 1,       /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */
-    PA_STREAM_INTERPOLATE_LATENCY = 2 /**< Interpolate the latency for
-                                       * this stream. When enabled,
-                                       * you can use
-                                       * pa_stream_interpolated_xxx()
-                                       * for synchronization. Using
-                                       * these functions instead of
-                                       * pa_stream_get_latency() has
-                                       * the advantage of not
-                                       * requiring a whole roundtrip
-                                       * for responses. Consider using
-                                       * this option when frequently
-                                       * requesting latency
-                                       * information. This is
-                                       * especially useful on long latency
-                                       * network connections. */
-};
-
-/** Playback and record buffer metrics */
-struct pa_buffer_attr{
-    uint32_t maxlength;      /**< Maximum length of the buffer */
-    uint32_t tlength;        /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */
-    uint32_t prebuf;         /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */
-    uint32_t minreq;         /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */
-    uint32_t fragsize;       /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */
-};
-
-/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */
-enum {
-    PA_ERROR_OK,                     /**< No error */
-    PA_ERROR_ACCESS,                 /**< Access failure */
-    PA_ERROR_COMMAND,                /**< Unknown command */
-    PA_ERROR_INVALID,                /**< Invalid argument */
-    PA_ERROR_EXIST,                  /**< Entity exists */
-    PA_ERROR_NOENTITY,               /**< No such entity */
-    PA_ERROR_CONNECTIONREFUSED,      /**< Connection refused */
-    PA_ERROR_PROTOCOL,               /**< Protocol error */ 
-    PA_ERROR_TIMEOUT,                /**< Timeout */
-    PA_ERROR_AUTHKEY,                /**< No authorization key */
-    PA_ERROR_INTERNAL,               /**< Internal error */
-    PA_ERROR_CONNECTIONTERMINATED,   /**< Connection terminated */
-    PA_ERROR_KILLED,                 /**< Entity killed */
-    PA_ERROR_INVALIDSERVER,          /**< Invalid server */
-    PA_ERROR_INITFAILED,             /**< Module initialization failed */
-    PA_ERROR_MAX                     /**< Not really an error but the first invalid error code */
-};
-
-/** Subscription event mask, as used by pa_context_subscribe() */
-enum pa_subscription_mask {
-    PA_SUBSCRIPTION_MASK_NULL = 0,               /**< No events */
-    PA_SUBSCRIPTION_MASK_SINK = 1,               /**< Sink events */
-    PA_SUBSCRIPTION_MASK_SOURCE = 2,             /**< Source events */
-    PA_SUBSCRIPTION_MASK_SINK_INPUT = 4,         /**< Sink input events */
-    PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 8,      /**< Source output events */
-    PA_SUBSCRIPTION_MASK_MODULE = 16,            /**< Module events */
-    PA_SUBSCRIPTION_MASK_CLIENT = 32,            /**< Client events */
-    PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64,      /**< Sample cache events */
-    PA_SUBSCRIPTION_MASK_SERVER = 128,           /**< Other global server changes. \since 0.4 */
-    PA_SUBSCRIPTION_MASK_AUTOLOAD = 256          /**< Autoload table events. \since 0.5 */
-};
-
-/** Subscription event types, as used by pa_context_subscribe() */
-enum pa_subscription_event_type {
-    PA_SUBSCRIPTION_EVENT_SINK = 0,           /**< Event type: Sink */
-    PA_SUBSCRIPTION_EVENT_SOURCE = 1,         /**< Event type: Source */
-    PA_SUBSCRIPTION_EVENT_SINK_INPUT = 2,     /**< Event type: Sink input */
-    PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 3,  /**< Event type: Source output */
-    PA_SUBSCRIPTION_EVENT_MODULE = 4,         /**< Event type: Module */
-    PA_SUBSCRIPTION_EVENT_CLIENT = 5,         /**< Event type: Client */
-    PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6,   /**< Event type: Sample cache item */
-    PA_SUBSCRIPTION_EVENT_SERVER = 7,         /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4  */
-    PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8,       /**< Event type: Autoload table changes. \since 0.5 */
-    PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */
-
-    PA_SUBSCRIPTION_EVENT_NEW = 0,            /**< A new object was created */
-    PA_SUBSCRIPTION_EVENT_CHANGE = 16,        /**< A property of the object was modified */
-    PA_SUBSCRIPTION_EVENT_REMOVE = 32,        /**< An object was removed */
-    PA_SUBSCRIPTION_EVENT_TYPE_MASK = 16+32   /**< A mask to extract the event operation from an event value */
-};
-
-/** Return one if an event type t matches an event mask bitfield */
-#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))))
-
-/** A structure for latency info. See pa_stream_get_latency(). The
- * total output latency a sample that is written with
- * pa_stream_write() takes to be played may be estimated by
- * sink_usec+buffer_usec+transport_usec. The output buffer to which
- * buffer_usec relates may be manipulated freely (with
- * pa_stream_write()'s delta argument, pa_stream_flush() and friends),
- * the buffers sink_usec/source_usec relates to is a first-in
- * first-out buffer which cannot be flushed or manipulated in any
- * way. The total input latency a sample that is recorded takes to be
- * delivered to the application is:
- * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of
- * sign issues!) When connected to a monitor source sink_usec contains
- * the latency of the owning sink.*/
-struct pa_latency_info {
-    pa_usec_t buffer_usec;    /**< Time in usecs the current buffer takes to play. For both playback and record streams. */
-    pa_usec_t sink_usec;      /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */
-    pa_usec_t source_usec;    /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/
-    pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */
-    int playing;              /**< Non-zero when the stream is currently playing. Only for playback streams. */
-    uint32_t queue_length;    /**< Queue size in bytes. For both playback and record streams. */
-    int synchronized_clocks;  /**< Non-zero if the local and the
-                               * remote machine have synchronized
-                               * clocks. If synchronized clocks are
-                               * detected transport_usec becomes much
-                               * more reliable. However, the code that
-                               * detects synchronized clocks is very
-                               * limited und unreliable itself. \since
-                               * 0.5 */
-    struct timeval timestamp; /**< The time when this latency info was current */
-    uint64_t counter;         /**< The byte counter current when the latency info was requested. \since 0.6 */
-};
-
-/** A structure for the spawn api. This may be used to integrate auto
- * spawned daemons into your application. For more information see
- * pa_context_connect(). When spawning a new child process the
- * waitpid() is used on the child's PID. The spawn routine will not
- * block or ignore SIGCHLD signals, since this cannot be done in a
- * thread compatible way. You might have to do this in
- * prefork/postfork. \since 0.4 */
-struct pa_spawn_api {
-    void (*prefork)(void);     /**< Is called just before the fork in the parent process. May be NULL. */
-    void (*postfork)(void);    /**< Is called immediately after the fork in the parent process. May be NULL.*/
-    void (*atfork)(void);      /**< Is called immediately after the
-                                * fork in the child process. May be
-                                * NULL. It is not safe to close all
-                                * file descriptors in this function
-                                * unconditionally, since a UNIX socket
-                                * (created using socketpair()) is
-                                * passed to the new process. */
-};
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/polyplib-error.c b/polyp/polyplib-error.c
deleted file mode 100644 (file)
index 50a6727..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "polyplib-error.h"
-#include "native-common.h"
-
-static const char* const errortab[PA_ERROR_MAX] = {
-    [PA_ERROR_OK] = "OK",
-    [PA_ERROR_ACCESS] = "Access denied",
-    [PA_ERROR_COMMAND] = "Unknown command",
-    [PA_ERROR_INVALID] = "Invalid argument",
-    [PA_ERROR_EXIST] = "Entity exists",
-    [PA_ERROR_NOENTITY] = "No such entity",
-    [PA_ERROR_CONNECTIONREFUSED] = "Connection refused",
-    [PA_ERROR_PROTOCOL] = "Protocol error",
-    [PA_ERROR_TIMEOUT] = "Timeout",
-    [PA_ERROR_AUTHKEY] = "No authorization key",
-    [PA_ERROR_INTERNAL] = "Internal error",
-    [PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated",
-    [PA_ERROR_KILLED] = "Entity killed",
-    [PA_ERROR_INVALIDSERVER] = "Invalid server",
-};
-
-const char*pa_strerror(uint32_t error) {
-    if (error >= PA_ERROR_MAX)
-        return NULL;
-
-    return errortab[error];
-}
diff --git a/polyp/polyplib-internal.h b/polyp/polyplib-internal.h
deleted file mode 100644 (file)
index d1b3a27..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-#ifndef foopolyplibinternalhfoo
-#define foopolyplibinternalhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "mainloop-api.h"
-#include "socket-client.h"
-#include "pstream.h"
-#include "pdispatch.h"
-#include "dynarray.h"
-
-#include "polyplib-context.h"
-#include "polyplib-stream.h"
-#include "polyplib-operation.h"
-#include "llist.h"
-#include "native-common.h"
-#include "client-conf.h"
-#include "strlist.h"
-#include "mcalign.h"
-
-#define DEFAULT_TIMEOUT (10)
-
-struct pa_context {
-    int ref;
-    
-    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;
-    PA_LLIST_HEAD(struct pa_stream, streams);
-    PA_LLIST_HEAD(struct pa_operation, operations);
-    
-    uint32_t ctag;
-    uint32_t error;
-    enum pa_context_state state;
-    
-    void (*state_callback)(struct pa_context*c, void *userdata);
-    void *state_userdata;
-
-    void (*subscribe_callback)(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata);
-    void *subscribe_userdata;
-
-    struct pa_memblock_stat *memblock_stat;
-
-    int local;
-    int do_autospawn;
-    int autospawn_lock_fd;
-    struct pa_spawn_api spawn_api;
-    
-    struct pa_strlist *server_list;
-
-    char *server;
-
-    struct pa_client_conf *conf;
-};
-
-struct pa_stream {
-    int ref;
-    struct pa_context *context;
-    struct pa_mainloop_api *mainloop;
-    PA_LLIST_FIELDS(struct pa_stream);
-
-    char *name;
-    struct pa_buffer_attr buffer_attr;
-    struct pa_sample_spec sample_spec;
-    uint32_t channel;
-    int channel_valid;
-    uint32_t device_index;
-    enum pa_stream_direction direction;
-    uint32_t requested_bytes;
-    uint64_t counter;
-    pa_usec_t previous_time;
-    enum pa_stream_state state;
-    struct pa_mcalign *mcalign;
-
-    int interpolate;
-    int corked;
-
-    uint32_t ipol_usec;
-    struct timeval ipol_timestamp;
-    struct pa_time_event *ipol_event;
-    int ipol_requested;
-    
-    void (*state_callback)(struct pa_stream*c, void *userdata);
-    void *state_userdata;
-
-    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;
-};
-
-struct pa_operation {
-    int ref;
-    struct pa_context *context;
-    struct pa_stream *stream;
-    PA_LLIST_FIELDS(struct pa_operation);
-
-    enum pa_operation_state state;
-    void *userdata;
-    void (*callback)();
-};
-
-void pa_command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-void pa_command_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-void pa_command_subscribe_event(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-
-struct pa_operation *pa_operation_new(struct pa_context *c, struct pa_stream *s);
-void pa_operation_done(struct pa_operation *o);
-
-void pa_create_stream_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-void pa_stream_disconnect_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-void pa_context_simple_ack_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-void pa_stream_simple_ack_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-
-void pa_context_fail(struct pa_context *c, int error);
-void pa_context_set_state(struct pa_context *c, enum pa_context_state st);
-int pa_context_handle_error(struct pa_context *c, uint32_t command, struct pa_tagstruct *t);
-struct pa_operation* pa_context_send_simple_command(struct pa_context *c, uint32_t command, void (*internal_callback)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void (*cb)(), void *userdata);
-
-void pa_stream_set_state(struct pa_stream *s, enum pa_stream_state st);
-
-void pa_stream_trash_ipol(struct pa_stream *s);
-
-
-#endif
diff --git a/polyp/polyplib-introspect.c b/polyp/polyplib-introspect.c
deleted file mode 100644 (file)
index 5d6d64a..0000000
+++ /dev/null
@@ -1,998 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-
-#include "polyplib-introspect.h"
-#include "polyplib-context.h"
-#include "polyplib-internal.h"
-#include "pstream-util.h"
-
-/*** Statistics ***/
-
-static void context_stat_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    struct pa_stat_info i, *p = &i;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        p = NULL;
-    } else if (pa_tagstruct_getu32(t, &i.memblock_total) < 0 ||
-               pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 ||
-               pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 ||
-               pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 ||
-               pa_tagstruct_getu32(t, &i.scache_size) < 0 ||
-               !pa_tagstruct_eof(t)) {
-        pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, const struct pa_stat_info*i, void *userdata) = o->callback;
-        cb(o->context, p, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_stat_info*i, void *userdata), void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, cb, userdata);
-}
-
-/*** Server Info ***/
-
-static void context_get_server_info_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    struct pa_server_info i, *p = &i;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        p = NULL;
-    } else if (pa_tagstruct_gets(t, &i.server_name) < 0 ||
-               pa_tagstruct_gets(t, &i.server_version) < 0 ||
-               pa_tagstruct_gets(t, &i.user_name) < 0 ||
-               pa_tagstruct_gets(t, &i.host_name) < 0 ||
-               pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
-               pa_tagstruct_gets(t, &i.default_sink_name) < 0 ||
-               pa_tagstruct_gets(t, &i.default_source_name) < 0 ||
-               pa_tagstruct_getu32(t, &i.cookie) < 0 ||
-               !pa_tagstruct_eof(t)) {
-
-        pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, const struct pa_server_info*i, void *userdata) = o->callback;
-        cb(o->context, p, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_get_server_info(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_server_info*i, void *userdata), void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_GET_SERVER_INFO, context_get_server_info_callback, cb, userdata);
-}
-
-/*** Sink Info ***/
-
-static void context_get_sink_info_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    int eof = 1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        eof = -1;
-    } else {
-        
-        while (!pa_tagstruct_eof(t)) {
-            struct pa_sink_info i;
-            
-            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
-                pa_tagstruct_gets(t, &i.name) < 0 ||
-                pa_tagstruct_gets(t, &i.description) < 0 ||
-                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
-                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
-                pa_tagstruct_getu32(t, &i.volume) < 0 ||
-                pa_tagstruct_getu32(t, &i.monitor_source) < 0 ||
-                pa_tagstruct_gets(t, &i.monitor_source_name) < 0 ||
-                pa_tagstruct_get_usec(t, &i.latency) < 0 ||
-                pa_tagstruct_getu32(t, &i._typeid) < 0) {
-                
-                pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-                goto finish;
-            }
-
-            if (o->callback) {
-                void (*cb)(struct pa_context *s, const struct pa_sink_info*i, int eof, void *userdata) = o->callback;
-                cb(o->context, &i, 0, o->userdata);
-            }
-        }
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, const struct pa_sink_info*i, int eof, void *userdata) = o->callback;
-        cb(o->context, NULL, eof, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_get_sink_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_sink_info *i, int is_last, void *userdata), void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INFO_LIST, context_get_sink_info_callback, cb, userdata);
-}
-
-struct pa_operation* pa_context_get_sink_info_by_index(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_sink_info *i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_tagstruct_puts(t, NULL);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_get_sink_info_by_name(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, const struct pa_sink_info *i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-    pa_tagstruct_puts(t, name);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-/*** Source info ***/
-
-static void context_get_source_info_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    int eof = 1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        eof = -1;
-    } else {
-        
-        while (!pa_tagstruct_eof(t)) {
-            struct pa_source_info i;
-            
-            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
-                pa_tagstruct_gets(t, &i.name) < 0 ||
-                pa_tagstruct_gets(t, &i.description) < 0 ||
-                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
-                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
-                pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 ||
-                pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 ||
-                pa_tagstruct_get_usec(t, &i.latency) < 0 ||
-                pa_tagstruct_getu32(t, &i._typeid) < 0) {
-                
-                pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-                goto finish;
-            }
-
-            if (o->callback) {
-                void (*cb)(struct pa_context *s, const struct pa_source_info*i, int eof, void *userdata) = o->callback;
-                cb(o->context, &i, 0, o->userdata);
-            }
-        }
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, const struct pa_source_info*i, int eof, void *userdata) = o->callback;
-        cb(o->context, NULL, eof, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_get_source_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_source_info *i, int is_last, void *userdata), void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_INFO_LIST, context_get_source_info_callback, cb, userdata);
-}
-
-struct pa_operation* pa_context_get_source_info_by_index(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_source_info *i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_tagstruct_puts(t, NULL);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_get_source_info_by_name(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, const struct pa_source_info *i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-    pa_tagstruct_puts(t, name);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-/*** Client info ***/
-
-static void context_get_client_info_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    int eof = 1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        eof = -1;
-    } else {
-        
-        while (!pa_tagstruct_eof(t)) {
-            struct pa_client_info i;
-            
-            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
-                pa_tagstruct_gets(t, &i.name) < 0 ||
-                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
-                pa_tagstruct_getu32(t, &i._typeid) < 0 ) {
-                pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-                goto finish;
-            }
-
-            if (o->callback) {
-                void (*cb)(struct pa_context *s, const struct pa_client_info*i, int eof, void *userdata) = o->callback;
-                cb(o->context, &i, 0, o->userdata);
-            }
-        }
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, const struct pa_client_info*i, int eof, void *userdata) = o->callback;
-        cb(o->context, NULL, eof, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_get_client_info(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_client_info*i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_CLIENT_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_client_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_get_client_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_client_info*i, int is_last, void *userdata), void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, cb, userdata);
-}
-
-/*** Module info ***/
-
-static void context_get_module_info_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    int eof = 1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        eof = -1;
-    } else {
-        
-        while (!pa_tagstruct_eof(t)) {
-            struct pa_module_info i;
-            
-            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
-                pa_tagstruct_gets(t, &i.name) < 0 ||
-                pa_tagstruct_gets(t, &i.argument) < 0 ||
-                pa_tagstruct_getu32(t, &i.n_used) < 0 ||
-                pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) {
-                pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-                goto finish;
-            }
-
-            if (o->callback) {
-                void (*cb)(struct pa_context *s, const struct pa_module_info*i, int eof, void *userdata) = o->callback;
-                cb(o->context, &i, 0, o->userdata);
-            }
-        }
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, const struct pa_module_info*i, int eof, void *userdata) = o->callback;
-        cb(o->context, NULL, eof, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_get_module_info(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_module_info*i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_MODULE_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_module_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_get_module_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_module_info*i, int is_last, void *userdata), void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_GET_MODULE_INFO_LIST, context_get_module_info_callback, cb, userdata);
-}
-
-/*** Sink input info ***/
-
-static void context_get_sink_input_info_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    int eof = 1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        eof = -1;
-    } else {
-        
-        while (!pa_tagstruct_eof(t)) {
-            struct pa_sink_input_info i;
-            
-            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
-                pa_tagstruct_gets(t, &i.name) < 0 ||
-                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
-                pa_tagstruct_getu32(t, &i.client) < 0 ||
-                pa_tagstruct_getu32(t, &i.sink) < 0 ||
-                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
-                pa_tagstruct_getu32(t, &i.volume) < 0 ||
-                pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
-                pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
-                pa_tagstruct_gets(t, &i.resample_method) < 0 ||
-                pa_tagstruct_getu32(t, &i._typeid) < 0) {
-                
-                pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-                goto finish;
-            }
-
-            if (o->callback) {
-                void (*cb)(struct pa_context *s, const struct pa_sink_input_info*i, int eof, void *userdata) = o->callback;
-                cb(o->context, &i, 0, o->userdata);
-            }
-        }
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, const struct pa_sink_input_info*i, int eof, void *userdata) = o->callback;
-        cb(o->context, NULL, eof, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_get_sink_input_info(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_sink_input_info*i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_input_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_get_sink_input_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_sink_input_info*i, int is_last, void *userdata), void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INPUT_INFO_LIST, context_get_sink_input_info_callback, cb, userdata);
-}
-
-/*** Source output info ***/
-
-static void context_get_source_output_info_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    int eof = 1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        eof = -1;
-    } else {
-        
-        while (!pa_tagstruct_eof(t)) {
-            struct pa_source_output_info i;
-            
-            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
-                pa_tagstruct_gets(t, &i.name) < 0 ||
-                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
-                pa_tagstruct_getu32(t, &i.client) < 0 ||
-                pa_tagstruct_getu32(t, &i.source) < 0 ||
-                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
-                pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
-                pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
-                pa_tagstruct_gets(t, &i.resample_method) < 0 ||
-                pa_tagstruct_getu32(t, &i._typeid) < 0) {
-                
-                pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-                goto finish;
-            }
-
-            if (o->callback) {
-                void (*cb)(struct pa_context *s, const struct pa_source_output_info*i, int eof, void *userdata) = o->callback;
-                cb(o->context, &i, 0, o->userdata);
-            }
-        }
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, const struct pa_source_output_info*i, int eof, void *userdata) = o->callback;
-        cb(o->context, NULL, eof, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_get_source_output_info(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_source_output_info*i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_OUTPUT_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_output_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_get_source_output_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_source_output_info*i, int is_last, void *userdata), void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, context_get_source_output_info_callback, cb, userdata);
-}
-
-/*** Volume manipulation ***/
-
-struct pa_operation* pa_context_set_sink_volume_by_index(struct pa_context *c, uint32_t index, pa_volume_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && index != PA_INVALID_INDEX);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_tagstruct_puts(t, NULL);
-    pa_tagstruct_putu32(t, volume);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_set_sink_volume_by_name(struct pa_context *c, const char *name, pa_volume_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && name);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-    pa_tagstruct_puts(t, name);
-    pa_tagstruct_putu32(t, volume);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_set_sink_input_volume(struct pa_context *c, uint32_t index, pa_volume_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && index != PA_INVALID_INDEX);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_tagstruct_putu32(t, volume);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-/** Sample Cache **/
-
-static void context_get_sample_info_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    int eof = 1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        eof = -1;
-    } else {
-        
-        while (!pa_tagstruct_eof(t)) {
-            struct pa_sample_info i;
-            
-            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
-                pa_tagstruct_gets(t, &i.name) < 0 ||
-                pa_tagstruct_getu32(t, &i.volume) < 0 ||
-                pa_tagstruct_get_usec(t, &i.duration) < 0 ||
-                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
-                pa_tagstruct_getu32(t, &i.bytes) < 0 ||
-                pa_tagstruct_get_boolean(t, &i.lazy) < 0 ||
-                pa_tagstruct_gets(t, &i.filename) < 0) {
-                
-                pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-                goto finish;
-            }
-
-            if (o->callback) {
-                void (*cb)(struct pa_context *s, const struct pa_sample_info*i, int eof, void *userdata) = o->callback;
-                cb(o->context, &i, 0, o->userdata);
-            }
-        }
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, const struct pa_sample_info*i, int eof, void *userdata) = o->callback;
-        cb(o->context, NULL, eof, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_get_sample_info_by_name(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, const struct pa_sample_info *i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb && name);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-    pa_tagstruct_puts(t, name);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_get_sample_info_by_index(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_sample_info *i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_tagstruct_puts(t, NULL);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_get_sample_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_sample_info *i, int is_last, void *userdata), void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, cb, userdata);
-}
-
-static struct pa_operation* command_kill(struct pa_context *c, uint32_t command, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && index != PA_INVALID_INDEX);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, command);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_kill_client(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    return command_kill(c, PA_COMMAND_KILL_CLIENT, index, cb, userdata);
-}
-                                            
-struct pa_operation* pa_context_kill_sink_input(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    return command_kill(c, PA_COMMAND_KILL_SINK_INPUT, index, cb, userdata);
-}
-
-struct pa_operation* pa_context_kill_source_output(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, index, cb, userdata);
-}
-
-static void load_module_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    uint32_t index = -1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-    } else if (pa_tagstruct_getu32(t, &index) < 0 ||
-               !pa_tagstruct_eof(t)) {
-        pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_context *c, uint32_t index, void *userdata) = o->callback;
-        cb(o->context, index, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_load_module(struct pa_context *c, const char*name, const char *argument, void (*cb)(struct pa_context *c, uint32_t index, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && name && argument);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_LOAD_MODULE);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_puts(t, name);
-    pa_tagstruct_puts(t, argument);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, load_module_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_unload_module(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    return command_kill(c, PA_COMMAND_UNLOAD_MODULE, index, cb, userdata);
-}
-
-/*** Autoload stuff ***/
-
-static void context_get_autoload_info_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    int eof = 1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        eof = -1;
-    } else {
-        
-        while (!pa_tagstruct_eof(t)) {
-            struct pa_autoload_info i;
-            
-            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
-                pa_tagstruct_gets(t, &i.name) < 0 ||
-                pa_tagstruct_getu32(t, &i.type) < 0 ||
-                pa_tagstruct_gets(t, &i.module) < 0 ||
-                pa_tagstruct_gets(t, &i.argument) < 0) {
-                pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-                goto finish;
-            }
-
-            if (o->callback) {
-                void (*cb)(struct pa_context *s, const struct pa_autoload_info*i, int eof, void *userdata) = o->callback;
-                cb(o->context, &i, 0, o->userdata);
-            }
-        }
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, const struct pa_autoload_info*i, int eof, void *userdata) = o->callback;
-        cb(o->context, NULL, eof, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_context_get_autoload_info_by_name(struct pa_context *c, const char *name, enum pa_autoload_type type, void (*cb)(struct pa_context *c, const struct pa_autoload_info *i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb && name);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_puts(t, name);
-    pa_tagstruct_putu32(t, type);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_get_autoload_info_by_index(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_autoload_info *i, int is_last, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(c && cb && index != PA_INVALID_INDEX);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_get_autoload_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_autoload_info *i, int is_last, void *userdata), void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, cb, userdata);
-}
-
-static void context_add_autoload_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    uint32_t index;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        index = PA_INVALID_INDEX;
-    } else if (pa_tagstruct_getu32(t, &index) ||
-               !pa_tagstruct_eof(t)) {
-        pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-
-    if (o->callback) {
-        void (*cb)(struct pa_context *s, uint32_t index, void *userdata) = o->callback;
-        cb(o->context, index, o->userdata);
-    }
-
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-
-struct pa_operation* pa_context_add_autoload(struct pa_context *c, const char *name, enum pa_autoload_type type, const char *module, const char*argument, void (*cb)(struct pa_context *c, int success, void *userdata), void* userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && name && module && argument);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_ADD_AUTOLOAD);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_puts(t, name);
-    pa_tagstruct_putu32(t, type);
-    pa_tagstruct_puts(t, module);
-    pa_tagstruct_puts(t, argument);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_add_autoload_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_remove_autoload_by_name(struct pa_context *c, const char *name, enum pa_autoload_type type, void (*cb)(struct pa_context *c, int success, void *userdata), void* userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && name);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_puts(t, name);
-    pa_tagstruct_putu32(t, type);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_remove_autoload_by_index(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void* userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && index != PA_INVALID_INDEX);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, index);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
diff --git a/polyp/polyplib-introspect.h b/polyp/polyplib-introspect.h
deleted file mode 100644 (file)
index 83cdba1..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-#ifndef foopolyplibintrospecthfoo
-#define foopolyplibintrospecthfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-
-#include <polyp/polyplib-operation.h>
-#include <polyp/polyplib-context.h>
-#include <polyp/cdecl.h>
-#include <polyp/typeid.h>
-
-/** \file
- *
- * Routines for daemon introspection. When enumerating all entitites
- * of a certain kind, use the pa_context_xxx_list() functions. The
- * specified callback function is called once for each entry. The
- * enumeration is finished by a call to the callback function with
- * is_last=1 and i=NULL. Strings referenced in pa_xxx_info structures
- * and the structures themselves point to internal memory that may not
- * be modified. That memory is only valid during the call to the
- * callback function. A deep copy is required if you need this data
- * outside the callback functions. An error is signalled by a call to * the callback function with i=NULL and is_last=0.
- *
- * When using the routines that ask fo a single entry only, a callback
- * with the same signature is used. However, no finishing call to the
- * routine is issued. */
-
-PA_C_DECL_BEGIN
-
-/** Stores information about sinks */
-struct pa_sink_info {
-    const char *name;                  /**< Name of the sink */
-    uint32_t index;                    /**< Index of the sink */ 
-    const char *description;           /**< Description of this sink */
-    struct pa_sample_spec sample_spec; /**< Sample spec of this sink */
-    uint32_t owner_module;             /**< Index of the owning module of this sink, or PA_INVALID_INDEX */
-    pa_volume_t volume;                /**< Volume of the sink */
-    uint32_t monitor_source;           /**< Index of the monitor source connected to this sink */
-    const char *monitor_source_name;   /**< The name of the monitor source */
-    pa_usec_t latency;                 /**< Length of filled playback buffer of this sink */
-    pa_typeid_t _typeid;                /**< Implementation type. \since 0.8 */
-};
-
-/** Get information about a sink by its name */
-struct pa_operation* pa_context_get_sink_info_by_name(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, const struct pa_sink_info *i, int is_last, void *userdata), void *userdata);
-
-/** Get information about a sink by its index */
-struct pa_operation* pa_context_get_sink_info_by_index(struct pa_context *c, uint32_t id, void (*cb)(struct pa_context *c, const struct pa_sink_info *i, int is_last, void *userdata), void *userdata);
-
-/** Get the complete sink list */
-struct pa_operation* pa_context_get_sink_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_sink_info *i, int is_last, void *userdata), void *userdata);
-
-/** Stores information about sources */
-struct pa_source_info { 
-    const char *name ;                  /**< Name of the source */
-    uint32_t index;                     /**< Index of the source */
-    const char *description;            /**< Description of this source */
-    struct pa_sample_spec sample_spec;  /**< Sample spec of this source */
-    uint32_t owner_module;              /**< Owning module index, or PA_INVALID_INDEX */
-    uint32_t monitor_of_sink;           /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */
-    const char *monitor_of_sink_name;   /**< Name of the owning sink, or PA_INVALID_INDEX */
-    pa_usec_t latency;                  /**< Length of filled record buffer of this source. \since 0.5 */
-    pa_typeid_t _typeid;                /**< Implementation type. \since 0.8 */
-};
-
-/** Get information about a source by its name */
-struct pa_operation* pa_context_get_source_info_by_name(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, const struct pa_source_info *i, int is_last, void *userdata), void *userdata);
-
-/** Get information about a source by its index */
-struct pa_operation* pa_context_get_source_info_by_index(struct pa_context *c, uint32_t id, void (*cb)(struct pa_context *c, const struct pa_source_info *i, int is_last, void *userdata), void *userdata);
-
-/** Get the complete source list */
-struct pa_operation* pa_context_get_source_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_source_info *i, int is_last, void *userdata), void *userdata);
-
-/** Server information */
-struct pa_server_info {
-    const char *user_name;              /**< User name of the daemon process */
-    const char *host_name;              /**< Host name the daemon is running on */
-    const char *server_version;         /**< Version string of the daemon */
-    const char *server_name;            /**< Server package name (usually "polypaudio") */
-    struct pa_sample_spec sample_spec;  /**< Default sample specification */
-    const char *default_sink_name;      /**< Name of default sink. \since 0.4 */
-    const char *default_source_name;    /**< Name of default sink. \since 0.4*/
-    uint32_t cookie;                    /**< A random cookie for identifying this instance of polypaudio. \since 0.8 */
-};
-
-/** Get some information about the server */
-struct pa_operation* pa_context_get_server_info(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_server_info*i, void *userdata), void *userdata);
-
-/** Stores information about modules */
-struct pa_module_info {
-    uint32_t index;                     /**< Index of the module */
-    const char*name,                    /**< Name of the module */
-        *argument;                      /**< Argument string of the module */
-    uint32_t n_used;                    /**< Usage counter or PA_INVALID_INDEX */
-    int auto_unload;                    /**< Non-zero if this is an autoloaded module */
-};
-
-/** Get some information about a module by its index */
-struct pa_operation* pa_context_get_module_info(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_module_info*i, int is_last, void *userdata), void *userdata);
-
-/** Get the complete list of currently loaded modules */
-struct pa_operation* pa_context_get_module_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_module_info*i, int is_last, void *userdata), void *userdata);
-
-/** Stores information about clients */
-struct pa_client_info {
-    uint32_t index;                      /**< Index of this client */
-    const char *name;                    /**< Name of this client */
-    uint32_t owner_module;               /**< Index of the owning module, or PA_INVALID_INDEX */
-    pa_typeid_t _typeid;                  /**< Implementation type. \since 0.8 */
-};
-
-/** Get information about a client by its index */
-struct pa_operation* pa_context_get_client_info(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_client_info*i, int is_last, void *userdata), void *userdata);
-
-/** Get the complete client list */
-struct pa_operation* pa_context_get_client_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_client_info*i, int is_last, void *userdata), void *userdata);
-
-/** Stores information about sink inputs */
-struct pa_sink_input_info {
-    uint32_t index;                      /**< Index of the sink input */  
-    const char *name;                    /**< Name of the sink input */
-    uint32_t owner_module;               /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
-    uint32_t client;                     /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
-    uint32_t sink;                       /**< Index of the connected sink */
-    struct pa_sample_spec sample_spec;   /**< The sample specification of the sink input */
-    pa_volume_t volume;                  /**< The volume of this sink input */
-    pa_usec_t buffer_usec;               /**< Latency due to buffering in sink input, see pa_latency_info for details */
-    pa_usec_t sink_usec;                 /**< Latency of the sink device, see pa_latency_info for details */
-    const char *resample_method;         /**< Thre resampling method used by this sink input. \since 0.7 */
-    pa_typeid_t _typeid;                 /**< Implementation type. \since 0.8 */
-};
-
-/** Get some information about a sink input by its index */
-struct pa_operation* pa_context_get_sink_input_info(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_sink_input_info*i, int is_last, void *userdata), void *userdata);
-
-/** Get the complete sink input list */
-struct pa_operation* pa_context_get_sink_input_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_sink_input_info*i, int is_last, void *userdata), void *userdata);
-
-/** Stores information about source outputs */
-struct pa_source_output_info {
-    uint32_t index;                      /**< Index of the sink input */ 
-    const char *name;                    /**< Name of the sink input */
-    uint32_t owner_module;               /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */ 
-    uint32_t client;                     /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */  
-    uint32_t source;                     /**< Index of the connected source */ 
-    struct pa_sample_spec sample_spec;   /**< The sample specification of the source output */
-    pa_usec_t buffer_usec;               /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */
-    pa_usec_t source_usec;               /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */
-    const char *resample_method;         /**< Thre resampling method used by this source output. \since 0.7 */
-    pa_typeid_t _typeid;                  /**< Implementation type. \since 0.8 */
-};
-
-/** Get information about a source output by its index */
-struct pa_operation* pa_context_get_source_output_info(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_source_output_info*i, int is_last, void *userdata), void *userdata);
-
-/** Get the complete list of source outputs */
-struct pa_operation* pa_context_get_source_output_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_source_output_info*i, int is_last, void *userdata), void *userdata);
-
-/** Set the volume of a sink device specified by its index */
-struct pa_operation* pa_context_set_sink_volume_by_index(struct pa_context *c, uint32_t index, pa_volume_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
-
-/** Set the volume of a sink device specified by its name */
-struct pa_operation* pa_context_set_sink_volume_by_name(struct pa_context *c, const char *name, pa_volume_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
-
-/** Set the volume of a sink input stream */
-struct pa_operation* pa_context_set_sink_input_volume(struct pa_context *c, uint32_t index, pa_volume_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
-
-/** Memory block statistics */
-struct pa_stat_info {
-    uint32_t memblock_total;           /**< Currently allocated memory blocks */
-    uint32_t memblock_total_size;      /**< Currentl total size of allocated memory blocks */
-    uint32_t memblock_allocated;       /**< Allocated memory blocks during the whole lifetime of the daemon */
-    uint32_t memblock_allocated_size;  /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */
-    uint32_t scache_size;              /**< Total size of all sample cache entries. \since 0.4 */ 
-};
-
-/** Get daemon memory block statistics */
-struct pa_operation* pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_stat_info *i, void *userdata), void *userdata);
-
-/** Stores information about sample cache entries */
-struct pa_sample_info {
-    uint32_t index;                       /**< Index of this entry */
-    const char *name;                     /**< Name of this entry */
-    pa_volume_t volume;                   /**< Default volume of this entry */
-    struct pa_sample_spec sample_spec;    /**< Sample specification of the sampel */
-    pa_usec_t duration;                   /**< Duration of this entry */
-    uint32_t bytes;                       /**< Length of this sample in bytes. \since 0.4 */
-    int lazy;                             /**< Non-zero when this is a lazy cache entry. \since 0.5 */
-    const char *filename;                 /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */
-};
-
-/** Get information about a sample by its name */
-struct pa_operation* pa_context_get_sample_info_by_name(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, const struct pa_sample_info *i, int is_last, void *userdata), void *userdata);
-
-/** Get information about a sample by its index */
-struct pa_operation* pa_context_get_sample_info_by_index(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_sample_info *i, int is_last, void *userdata), void *userdata);
-
-/** Get the complete list of samples stored in the daemon. */
-struct pa_operation* pa_context_get_sample_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_sample_info *i, int is_last, void *userdata), void *userdata);
-
-/** Kill a client. \since 0.5 */
-struct pa_operation* pa_context_kill_client(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
-                                            
-/** Kill a sink input. \since 0.5 */
-struct pa_operation* pa_context_kill_sink_input(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
-
-/** Kill a source output. \since 0.5 */
-struct pa_operation* pa_context_kill_source_output(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
-
-/** Load a module. \since 0.5 */
-struct pa_operation* pa_context_load_module(struct pa_context *c, const char*name, const char *argument, void (*cb)(struct pa_context *c, uint32_t index, void *userdata), void *userdata);
-
-/** Unload a module. \since 0.5 */
-struct pa_operation* pa_context_unload_module(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
-
-/** Type of an autoload entry. \since 0.5 */
-enum pa_autoload_type {
-    PA_AUTOLOAD_SINK = 0,
-    PA_AUTOLOAD_SOURCE = 1
-};
-
-/** Stores information about autoload entries. \since 0.5 */
-struct pa_autoload_info {
-    uint32_t index;               /**< Index of this autoload entry */
-    const char *name;             /**< Name of the sink or source */
-    enum pa_autoload_type type;   /**< Type of the autoload entry */
-    const char *module;           /**< Module name to load */
-    const char *argument;         /**< Argument string for module */
-};
-
-/** Get info about a specific autoload entry. \since 0.6 */
-struct pa_operation* pa_context_get_autoload_info_by_name(struct pa_context *c, const char *name, enum pa_autoload_type type, void (*cb)(struct pa_context *c, const struct pa_autoload_info *i, int is_last, void *userdata), void *userdata);
-
-/** Get info about a specific autoload entry. \since 0.6 */
-struct pa_operation* pa_context_get_autoload_info_by_index(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, const struct pa_autoload_info *i, int is_last, void *userdata), void *userdata);
-
-/** Get the complete list of autoload entries. \since 0.5 */
-struct pa_operation* pa_context_get_autoload_info_list(struct pa_context *c, void (*cb)(struct pa_context *c, const struct pa_autoload_info *i, int is_last, void *userdata), void *userdata);
-
-/** Add a new autoload entry. \since 0.5 */
-struct pa_operation* pa_context_add_autoload(struct pa_context *c, const char *name, enum pa_autoload_type type, const char *module, const char*argument, void (*cb)(struct pa_context *c, int index, void *userdata), void* userdata);
-
-/** Remove an autoload entry. \since 0.6 */
-struct pa_operation* pa_context_remove_autoload_by_name(struct pa_context *c, const char *name, enum pa_autoload_type type, void (*cb)(struct pa_context *c, int success, void *userdata), void* userdata);
-
-/** Remove an autoload entry. \since 0.6 */
-struct pa_operation* pa_context_remove_autoload_by_index(struct pa_context *c, uint32_t index, void (*cb)(struct pa_context *c, int success, void *userdata), void* userdata);
-
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/polyplib-operation.c b/polyp/polyplib-operation.c
deleted file mode 100644 (file)
index 77fe70f..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-
-#include "xmalloc.h"
-#include "polyplib-internal.h"
-#include "polyplib-operation.h"
-
-struct pa_operation *pa_operation_new(struct pa_context *c, struct pa_stream *s) {
-    struct pa_operation *o;
-    assert(c);
-
-    o = pa_xmalloc(sizeof(struct pa_operation));
-    o->ref = 1;
-    o->context = pa_context_ref(c);
-    o->stream = s ? pa_stream_ref(s) : NULL;
-
-    o->state = PA_OPERATION_RUNNING;
-    o->userdata = NULL;
-    o->callback = NULL;
-
-    PA_LLIST_PREPEND(struct pa_operation, o->context->operations, o);
-    return pa_operation_ref(o);
-}
-
-struct pa_operation *pa_operation_ref(struct pa_operation *o) {
-    assert(o && o->ref >= 1);
-    o->ref++;
-    return o;
-}
-
-void pa_operation_unref(struct pa_operation *o) {
-    assert(o && o->ref >= 1);
-
-    if ((--(o->ref)) == 0) {
-        assert(!o->context);
-        assert(!o->stream);
-        free(o);
-    }
-}
-
-static void operation_set_state(struct pa_operation *o, enum pa_operation_state st) {
-    assert(o && o->ref >= 1);
-
-    if (st == o->state)
-        return;
-
-    if (!o->context)
-        return;
-
-    o->state = st;
-
-    if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) {
-        PA_LLIST_REMOVE(struct pa_operation, o->context->operations, o);
-        pa_context_unref(o->context);
-        if (o->stream)
-            pa_stream_unref(o->stream);
-        o->context = NULL;
-        o->stream = NULL;
-        o->callback = NULL;
-        o->userdata = NULL;
-
-        pa_operation_unref(o);
-    }
-}
-
-void pa_operation_cancel(struct pa_operation *o) {
-    assert(o && o->ref >= 1);
-    operation_set_state(o, PA_OPERATION_CANCELED);
-}
-
-void pa_operation_done(struct pa_operation *o) {
-    assert(o && o->ref >= 1);
-    operation_set_state(o, PA_OPERATION_DONE);
-}
-
-enum pa_operation_state pa_operation_get_state(struct pa_operation *o) {
-    assert(o && o->ref >= 1);
-    return o->state;
-}
diff --git a/polyp/polyplib-scache.c b/polyp/polyplib-scache.c
deleted file mode 100644 (file)
index bce5d18..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "polyplib-scache.h"
-#include "polyplib-internal.h"
-#include "pstream-util.h"
-
-void pa_stream_connect_upload(struct pa_stream *s, size_t length) {
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    
-    assert(s && length);
-
-    pa_stream_ref(s);
-    
-    s->state = PA_STREAM_CREATING;
-    s->direction = PA_STREAM_UPLOAD;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_CREATE_UPLOAD_STREAM);
-    pa_tagstruct_putu32(t, tag = s->context->ctag++);
-    pa_tagstruct_puts(t, s->name);
-    pa_tagstruct_put_sample_spec(t, &s->sample_spec);
-    pa_tagstruct_putu32(t, length);
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s);
-
-    pa_stream_unref(s);
-}
-
-void pa_stream_finish_upload(struct pa_stream *s) {
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(s);
-
-    if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY)
-        return;
-
-    pa_stream_ref(s);
-
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    
-    pa_tagstruct_putu32(t, PA_COMMAND_FINISH_UPLOAD_STREAM);
-    pa_tagstruct_putu32(t, tag = s->context->ctag++);
-    pa_tagstruct_putu32(t, s->channel);
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s);
-
-    pa_stream_unref(s);
-}
-
-struct pa_operation * pa_context_play_sample(struct pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && name && *name && (!dev || *dev));
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    if (!dev)
-        dev = c->conf->default_sink;
-    
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    pa_tagstruct_putu32(t, PA_COMMAND_PLAY_SAMPLE);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, (uint32_t) -1);
-    pa_tagstruct_puts(t, dev);
-    pa_tagstruct_putu32(t, volume);
-    pa_tagstruct_puts(t, name);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_context_remove_sample(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c && name);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-    
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_SAMPLE);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_puts(t, name);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
diff --git a/polyp/polyplib-scache.h b/polyp/polyplib-scache.h
deleted file mode 100644 (file)
index 8ec375e..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifndef foopolyplibscachehfoo
-#define foopolyplibscachehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <sys/types.h>
-
-#include <polyp/polyplib-context.h>
-#include <polyp/polyplib-stream.h>
-#include <polyp/cdecl.h>
-
-/** \file
- * All sample cache related routines */
-
-PA_C_DECL_BEGIN
-
-/** Make this stream a sample upload stream */
-void pa_stream_connect_upload(struct pa_stream *s, size_t length);
-
-/** Finish the sample upload, the stream name will become the sample name. You cancel a sample upload by issuing pa_stream_disconnect() */
-void pa_stream_finish_upload(struct pa_stream *s);
-
-/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */
-struct pa_operation* pa_context_play_sample(struct pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
-
-/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
-struct pa_operation* pa_context_remove_sample(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/polyplib-simple.c b/polyp/polyplib-simple.c
deleted file mode 100644 (file)
index a73aacf..0000000
+++ /dev/null
@@ -1,394 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <stdlib.h>
-
-#include "polyplib-simple.h"
-#include "polyplib.h"
-#include "mainloop.h"
-#include "native-common.h"
-#include "xmalloc.h"
-#include "log.h"
-
-struct pa_simple {
-    struct pa_mainloop *mainloop;
-    struct pa_context *context;
-    struct pa_stream *stream;
-    enum pa_stream_direction direction;
-
-    int dead;
-
-    void *read_data;
-    size_t read_index, read_length;
-    pa_usec_t latency;
-};
-
-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) {
-    enum pa_context_state cst;
-    enum pa_stream_state sst;
-    assert(p);
-    
-    if ((cst = pa_context_get_state(p->context)) == PA_CONTEXT_FAILED)
-        goto fail;
-
-    assert(cst != PA_CONTEXT_TERMINATED);
-
-    if (p->stream) {
-        if ((sst = pa_stream_get_state(p->stream)) == PA_STREAM_FAILED)
-            goto fail;
-        
-        assert(sst != PA_STREAM_TERMINATED);
-    }
-    
-    return 0;
-    
-fail:
-    if (perror)
-        *perror = pa_context_errno(p->context);
-
-    p->dead = 1;
-    
-    return -1;
-}
-
-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));
-
-    
-    while (pa_mainloop_deferred_pending(p->mainloop)) {
-
-        if (pa_mainloop_iterate(p->mainloop, 0, NULL) < 0) {
-            if (perror)
-                *perror = PA_ERROR_INTERNAL;
-            return -1;
-        }
-
-        if (check_error(p, perror) < 0)
-            return -1;
-    }
-    
-    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,
-    pa_volume_t volume, 
-    int *perror) {
-    
-    struct pa_simple *p;
-    int error = PA_ERROR_INTERNAL;
-    assert(ss && (dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD));
-
-    p = pa_xmalloc(sizeof(struct pa_simple));
-    p->context = NULL;
-    p->stream = NULL;
-    p->mainloop = pa_mainloop_new();
-    assert(p->mainloop);
-    p->dead = 0;
-    p->direction = dir;
-    p->read_data = NULL;
-    p->read_index = p->read_length = 0;
-    p->latency = 0;
-
-    if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name)))
-        goto fail;
-    
-    pa_context_connect(p->context, server, 1, NULL);
-
-    /* Wait until the context is ready */
-    while (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
-        if (iterate(p, 1, &error) < 0)
-            goto fail;
-    }
-
-    if (!(p->stream = pa_stream_new(p->context, stream_name, ss)))
-        goto fail;
-
-    if (dir == PA_STREAM_PLAYBACK)
-        pa_stream_connect_playback(p->stream, dev, attr, 0, volume);
-    else
-        pa_stream_connect_record(p->stream, dev, attr, 0);
-
-    /* Wait until the stream is ready */
-    while (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
-        if (iterate(p, 1, &error) < 0)
-            goto fail;
-    }
-
-    pa_stream_set_read_callback(p->stream, read_callback, p);
-    
-    return p;
-    
-fail:
-    if (perror)
-        *perror = error;
-    pa_simple_free(p);
-    return NULL;
-}
-
-void pa_simple_free(struct pa_simple *s) {
-    assert(s);
-
-    pa_xfree(s->read_data);
-
-    if (s->stream)
-        pa_stream_unref(s->stream);
-    
-    if (s->context)
-        pa_context_unref(s->context);
-
-    if (s->mainloop)
-        pa_mainloop_free(s->mainloop);
-
-    pa_xfree(s);
-}
-
-int pa_simple_write(struct pa_simple *p, const void*data, size_t length, int *perror) {
-    assert(p && data && p->direction == PA_STREAM_PLAYBACK);
-
-    if (p->dead) {
-        if (perror)
-            *perror = pa_context_errno(p->context);
-        
-        return -1;
-    }
-
-    while (length > 0) {
-        size_t l;
-        
-        while (!(l = pa_stream_writable_size(p->stream)))
-            if (iterate(p, 1, perror) < 0)
-                return -1;
-
-        if (l > length)
-            l = length;
-
-        pa_stream_write(p->stream, data, l, NULL, 0);
-        data = (uint8_t*) 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) {
-        pa_log(__FILE__": Buffer overflow, dropping incoming memory blocks.\n");
-        pa_xfree(p->read_data);
-    }
-
-    p->read_data = pa_xmemdup(data, p->read_length = length);
-    p->read_index = 0;
-}
-
-int pa_simple_read(struct pa_simple *p, void*data, size_t length, int *perror) {
-    assert(p && data && p->direction == PA_STREAM_RECORD);
-
-    if (p->dead) {
-        if (perror)
-            *perror = pa_context_errno(p->context);
-        
-        return -1;
-    }
-    
-    while (length > 0) {
-        if (p->read_data) {
-            size_t l = length;
-
-            if (p->read_length <= l)
-                l = p->read_length;
-
-            memcpy(data, (uint8_t*) p->read_data+p->read_index, l);
-
-            data = (uint8_t*) data + l;
-            length -= l;
-            
-            p->read_index += l;
-            p->read_length -= l;
-
-            if (!p->read_length) {
-                pa_xfree(p->read_data);
-                p->read_data = NULL;
-                p->read_index = 0;
-            }
-            
-            if (!length)
-                return 0;
-
-            assert(!p->read_data);
-        }
-
-        if (iterate(p, 1, perror) < 0)
-            return -1;
-    }
-
-    return 0;
-}
-
-static void drain_or_flush_complete(struct pa_stream *s, int success, void *userdata) {
-    struct pa_simple *p = userdata;
-    assert(s && p);
-    if (!success)
-        p->dead = 1;
-}
-
-int pa_simple_drain(struct pa_simple *p, int *perror) {
-    struct pa_operation *o;
-    assert(p && p->direction == PA_STREAM_PLAYBACK);
-
-    if (p->dead) {
-        if (perror)
-            *perror = pa_context_errno(p->context);
-        
-        return -1;
-    }
-
-    o = pa_stream_drain(p->stream, drain_or_flush_complete, p);
-
-    while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
-        if (iterate(p, 1, perror) < 0) {
-            pa_operation_cancel(o);
-            pa_operation_unref(o);
-            return -1;
-        }
-    }
-
-    pa_operation_unref(o);
-
-    if (p->dead && perror)
-        *perror = pa_context_errno(p->context);
-
-    return p->dead ? -1 : 0;
-}
-
-static void latency_complete(struct pa_stream *s, const struct pa_latency_info *l, void *userdata) {
-    struct pa_simple *p = userdata;
-    assert(s && p);
-
-    if (!l)
-        p->dead = 1;
-    else {
-        int negative = 0;
-        p->latency = pa_stream_get_latency(s, l, &negative);
-        if (negative)
-            p->latency = 0;
-    }
-}
-
-pa_usec_t pa_simple_get_playback_latency(struct pa_simple *p, int *perror) {
-    struct pa_operation *o;
-    assert(p && p->direction == PA_STREAM_PLAYBACK);
-
-    if (p->dead) {
-        if (perror)
-            *perror = pa_context_errno(p->context);
-        
-        return (pa_usec_t) -1;
-    }
-
-    p->latency = 0;
-    o = pa_stream_get_latency_info(p->stream, latency_complete, p);
-    
-    while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
-
-        if (iterate(p, 1, perror) < 0) {
-            pa_operation_cancel(o);
-            pa_operation_unref(o);
-            return -1;
-        }
-    }
-
-    pa_operation_unref(o);
-    
-    if (p->dead && perror)
-        *perror = pa_context_errno(p->context);
-
-    return p->dead ? (pa_usec_t) -1 : p->latency;
-}
-
-int pa_simple_flush(struct pa_simple *p, int *perror) {
-    struct pa_operation *o;
-    assert(p && p->direction == PA_STREAM_PLAYBACK);
-
-    if (p->dead) {
-        if (perror)
-            *perror = pa_context_errno(p->context);
-        
-        return -1;
-    }
-
-    o = pa_stream_flush(p->stream, drain_or_flush_complete, p);
-
-    while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
-        if (iterate(p, 1, perror) < 0) {
-            pa_operation_cancel(o);
-            pa_operation_unref(o);
-            return -1;
-        }
-    }
-
-    pa_operation_unref(o);
-
-    if (p->dead && perror)
-        *perror = pa_context_errno(p->context);
-
-    return p->dead ? -1 : 0;
-}
diff --git a/polyp/polyplib-simple.h b/polyp/polyplib-simple.h
deleted file mode 100644 (file)
index 9abef3f..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-#ifndef foopolyplibsimplehfoo
-#define foopolyplibsimplehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <sys/types.h>
-
-#include "sample.h"
-#include "polyplib-def.h"
-#include "cdecl.h"
-
-/** \file
- * A simple but limited synchronous playback and recording
- * API. This is synchronouse, simplified wrapper around the standard
- * asynchronous API. */
-
-/** \example pacat-simple.c
- * A simple playback tool using the simple API */
-
-/** \example parec-simple.c
- * A simple recording tool using the simple API */
-
-PA_C_DECL_BEGIN
-
-/** \struct pa_simple
- * An opaque simple connection object */
-struct pa_simple;
-
-/** Create a new connection to the server */
-struct pa_simple* pa_simple_new(
-    const char *server,                 /**< Server name, or NULL for default */
-    const char *name,                   /**< A descriptive name for this client (application name, ...) */
-    enum pa_stream_direction dir,       /**< Open this stream for recording or playback? */
-    const char *dev,                    /**< Sink (resp. source) name, or NULL for default */
-    const char *stream_name,            /**< A descriptive name for this client (application name, song title, ...) */
-    const struct pa_sample_spec *ss,    /**< The sample type to use */
-    const struct pa_buffer_attr *attr,  /**< Buffering attributes, or NULL for default */
-    pa_volume_t volume,                 /**< Initial volume. Only for playback streams. \since 0.5 */
-    int *error                          /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */
-    );
-
-/** Close and free the connection to the server. The connection objects becomes invalid when this is called. */
-void pa_simple_free(struct pa_simple *s);
-
-/** Write some data to the server */
-int pa_simple_write(struct pa_simple *s, const void*data, size_t length, int *error);
-
-/** Wait until all data already written is played by the daemon */
-int pa_simple_drain(struct pa_simple *s, int *error);
-
-/** Read some data from the server */
-int pa_simple_read(struct pa_simple *s, void*data, size_t length, int *error);
-
-/** Return the playback latency. \since 0.5 */
-pa_usec_t pa_simple_get_playback_latency(struct pa_simple *s, int *perror);
-
-/** Flush the playback buffer. \since 0.5 */
-int pa_simple_flush(struct pa_simple *s, int *perror);
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/polyplib-stream.c b/polyp/polyplib-stream.c
deleted file mode 100644 (file)
index 440217e..0000000
+++ /dev/null
@@ -1,779 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "polyplib-internal.h"
-#include "xmalloc.h"
-#include "pstream-util.h"
-#include "util.h"
-#include "log.h"
-
-#define LATENCY_IPOL_INTERVAL_USEC (10000L)
-
-struct pa_stream *pa_stream_new(struct pa_context *c, const char *name, const struct pa_sample_spec *ss) {
-    struct pa_stream *s;
-    assert(c && ss);
-
-    s = pa_xmalloc(sizeof(struct pa_stream));
-    s->ref = 1;
-    s->context = c;
-    s->mainloop = c->mainloop;
-
-    s->read_callback = NULL;
-    s->read_userdata = NULL;
-    s->write_callback = NULL;
-    s->write_userdata = NULL;
-    s->state_callback = NULL;
-    s->state_userdata = NULL;
-
-    s->direction = PA_STREAM_NODIRECTION;
-    s->name = pa_xstrdup(name);
-    s->sample_spec = *ss;
-    s->channel = 0;
-    s->channel_valid = 0;
-    s->device_index = PA_INVALID_INDEX;
-    s->requested_bytes = 0;
-    s->state = PA_STREAM_DISCONNECTED;
-    memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
-
-    s->mcalign = pa_mcalign_new(pa_frame_size(ss), c->memblock_stat);
-
-    s->counter = 0;
-    s->previous_time = 0;
-
-    s->corked = 0;
-    s->interpolate = 0;
-
-    s->ipol_usec = 0;
-    memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp));
-    s->ipol_event = NULL;
-    s->ipol_requested = 0;
-
-    PA_LLIST_PREPEND(struct pa_stream, c->streams, s);
-
-    return pa_stream_ref(s);
-}
-
-static void stream_free(struct pa_stream *s) {
-    assert(s);
-
-    if (s->ipol_event) {
-        assert(s->mainloop);
-        s->mainloop->time_free(s->ipol_event);
-    }
-
-    pa_mcalign_free(s->mcalign);
-    
-    pa_xfree(s->name);
-    pa_xfree(s);
-}
-
-void pa_stream_unref(struct pa_stream *s) {
-    assert(s && s->ref >= 1);
-
-    if (--(s->ref) == 0)
-        stream_free(s);
-}
-
-struct pa_stream* pa_stream_ref(struct pa_stream *s) {
-    assert(s && s->ref >= 1);
-    s->ref++;
-    return s;
-}
-
-enum pa_stream_state pa_stream_get_state(struct pa_stream *s) {
-    assert(s && s->ref >= 1);
-    return s->state;
-}
-
-struct pa_context* pa_stream_get_context(struct pa_stream *s) {
-    assert(s && s->ref >= 1);
-    return s->context;
-}
-
-uint32_t pa_stream_get_index(struct pa_stream *s) {
-    assert(s && s->ref >= 1);
-    return s->device_index;
-}
-    
-void pa_stream_set_state(struct pa_stream *s, enum pa_stream_state st) {
-    assert(s && s->ref >= 1);
-
-    if (s->state == st)
-        return;
-    
-    pa_stream_ref(s);
-
-    s->state = st;
-    
-    if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) {
-        if (s->channel_valid)
-            pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
-
-        PA_LLIST_REMOVE(struct pa_stream, s->context->streams, s);
-        pa_stream_unref(s);
-    }
-
-    if (s->state_callback)
-        s->state_callback(s, s->state_userdata);
-
-    pa_stream_unref(s);
-}
-
-void pa_command_stream_killed(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);
-
-    pa_context_ref(c);
-    
-    if (pa_tagstruct_getu32(t, &channel) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        pa_context_fail(c, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-    
-    if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
-        goto finish;
-
-    c->error = PA_ERROR_KILLED;
-    pa_stream_set_state(s, PA_STREAM_FAILED);
-
-finish:
-    pa_context_unref(c);
-}
-
-void pa_command_request(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);
-
-    pa_context_ref(c);
-    
-    if (pa_tagstruct_getu32(t, &channel) < 0 ||
-        pa_tagstruct_getu32(t, &bytes) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        pa_context_fail(c, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-    
-    if (!(s = pa_dynarray_get(c->playback_streams, channel)))
-        goto finish;
-
-    if (s->state != PA_STREAM_READY)
-        goto finish;
-
-    pa_stream_ref(s);
-    
-    s->requested_bytes += bytes;
-
-    if (s->requested_bytes && s->write_callback)
-        s->write_callback(s, s->requested_bytes, s->write_userdata);
-
-    pa_stream_unref(s);
-
-finish:
-    pa_context_unref(c);
-}
-
-static void ipol_callback(struct pa_mainloop_api *m, struct pa_time_event *e, const struct timeval *tv, void *userdata) {
-    struct timeval tv2;
-    struct pa_stream *s = userdata;
-
-    pa_stream_ref(s);
-
-/*     pa_log("requesting new ipol data\n"); */
-    
-    if (s->state == PA_STREAM_READY && !s->ipol_requested) {
-        pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
-        s->ipol_requested = 1;
-    }
-    
-    gettimeofday(&tv2, NULL);
-    pa_timeval_add(&tv2, LATENCY_IPOL_INTERVAL_USEC);
-    
-    m->time_restart(e, &tv2);
-    
-    pa_stream_unref(s);
-}
-
-
-void pa_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 == PA_STREAM_CREATING);
-
-    pa_stream_ref(s);
-    
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(s->context, command, t) < 0)
-            goto finish;
-        
-        pa_stream_set_state(s, PA_STREAM_FAILED);
-        goto finish;
-    }
-
-    if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
-        ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) ||
-        ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0) ||
-        !pa_tagstruct_eof(t)) {
-        pa_context_fail(s->context, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-
-    s->channel_valid = 1;
-    pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
-    pa_stream_set_state(s, PA_STREAM_READY);
-
-    if (s->interpolate) {
-        struct timeval tv;
-        pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
-
-        gettimeofday(&tv, NULL);
-        tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */
-
-        assert(!s->ipol_event);
-        s->ipol_event = s->mainloop->time_new(s->mainloop, &tv, &ipol_callback, s);
-    }
-
-    if (s->requested_bytes && s->ref > 1 && s->write_callback)
-        s->write_callback(s, s->requested_bytes, s->write_userdata);
-
-finish:
-    pa_stream_unref(s);
-}
-
-static void create_stream(struct pa_stream *s, const char *dev, const struct pa_buffer_attr *attr, enum pa_stream_flags flags, pa_volume_t volume) {
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(s && s->ref >= 1 && s->state == PA_STREAM_DISCONNECTED);
-
-    pa_stream_ref(s);
-
-    s->interpolate = !!(flags & PA_STREAM_INTERPOLATE_LATENCY);
-    pa_stream_trash_ipol(s);
-    
-    if (attr)
-        s->buffer_attr = *attr;
-    else {
-        /* half a second */
-        s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2;
-        s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2;
-        s->buffer_attr.minreq = s->buffer_attr.tlength/100;
-        s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq;
-        s->buffer_attr.fragsize = s->buffer_attr.tlength/100;
-    }
-
-    pa_stream_set_state(s, PA_STREAM_CREATING);
-    
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-
-    if (!dev) {
-        if (s->direction == PA_STREAM_PLAYBACK)
-            dev = s->context->conf->default_sink;
-        else
-            dev = s->context->conf->default_source;
-    }
-    
-    pa_tagstruct_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, PA_INVALID_INDEX);
-    pa_tagstruct_puts(t, dev);
-    pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
-    pa_tagstruct_put_boolean(t, !!(flags & PA_STREAM_START_CORKED));
-    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);
-        pa_tagstruct_putu32(t, volume);
-    } else
-        pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
-
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s);
-
-    pa_stream_unref(s);
-}
-
-void pa_stream_connect_playback(struct pa_stream *s, const char *dev, const struct pa_buffer_attr *attr, enum pa_stream_flags flags, pa_volume_t volume) {
-    assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1);
-    s->direction = PA_STREAM_PLAYBACK;
-    create_stream(s, dev, attr, flags, volume);
-}
-
-void pa_stream_connect_record(struct pa_stream *s, const char *dev, const struct pa_buffer_attr *attr, enum pa_stream_flags flags) {
-    assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1);
-    s->direction = PA_STREAM_RECORD;
-    create_stream(s, dev, attr, flags, 0);
-}
-
-void pa_stream_write(struct pa_stream *s, const void *data, size_t length, void (*free_cb)(void *p), size_t delta) {
-    struct pa_memchunk chunk;
-    assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1);
-
-    if (free_cb) {
-        chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat);
-        assert(chunk.memblock && chunk.memblock->data);
-    } else {
-        chunk.memblock = pa_memblock_new(length, s->context->memblock_stat);
-        assert(chunk.memblock && chunk.memblock->data);
-        memcpy(chunk.memblock->data, data, length);
-    }
-    chunk.index = 0;
-    chunk.length = length;
-
-    pa_pstream_send_memblock(s->context->pstream, s->channel, delta, &chunk);
-    pa_memblock_unref(chunk.memblock);
-    
-    if (length < s->requested_bytes)
-        s->requested_bytes -= length;
-    else
-        s->requested_bytes = 0;
-
-    s->counter += length;
-}
-
-size_t pa_stream_writable_size(struct pa_stream *s) {
-    assert(s && s->ref >= 1);
-    return s->state == PA_STREAM_READY ? s->requested_bytes : 0;
-}
-
-struct pa_operation * pa_stream_drain(struct pa_stream *s, void (*cb) (struct pa_stream*s, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(s && s->ref >= 1 && s->state == PA_STREAM_READY);
-
-    o = pa_operation_new(s->context, s);
-    assert(o);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    pa_tagstruct_putu32(t, PA_COMMAND_DRAIN_PLAYBACK_STREAM);
-    pa_tagstruct_putu32(t, tag = s->context->ctag++);
-    pa_tagstruct_putu32(t, s->channel);
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-static void stream_get_latency_info_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    struct pa_latency_info i, *p = NULL;
-    struct timeval local, remote, now;
-    assert(pd && o && o->stream && o->context);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-    } else if (pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
-               pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
-               pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
-               pa_tagstruct_get_boolean(t, &i.playing) < 0 ||
-               pa_tagstruct_getu32(t, &i.queue_length) < 0 ||
-               pa_tagstruct_get_timeval(t, &local) < 0 ||
-               pa_tagstruct_get_timeval(t, &remote) < 0 ||
-               pa_tagstruct_getu64(t, &i.counter) < 0 ||
-               !pa_tagstruct_eof(t)) {
-        pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-        goto finish;
-    } else {
-        gettimeofday(&now, NULL);
-        
-        if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
-            /* local and remote seem to have synchronized clocks */
-            
-            if (o->stream->direction == PA_STREAM_PLAYBACK)
-                i.transport_usec = pa_timeval_diff(&remote, &local);
-            else
-                i.transport_usec = pa_timeval_diff(&now, &remote);
-            
-            i.synchronized_clocks = 1;
-            i.timestamp = remote;
-        } else {
-            /* clocks are not synchronized, let's estimate latency then */
-            i.transport_usec = pa_timeval_diff(&now, &local)/2;
-            i.synchronized_clocks = 0;
-            i.timestamp = local;
-            pa_timeval_add(&i.timestamp, i.transport_usec);
-        }
-        
-        if (o->stream->interpolate) {
-/*              pa_log("new interpol data\n");  */
-            o->stream->ipol_timestamp = i.timestamp;
-            o->stream->ipol_usec = pa_stream_get_time(o->stream, &i);
-            o->stream->ipol_requested = 0;
-        }
-
-        p = &i;
-    }
-    
-    if (o->callback) {
-        void (*cb)(struct pa_stream *s, const struct pa_latency_info *i, void *userdata) = o->callback;
-        cb(o->stream, p, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_stream_get_latency_info(struct pa_stream *s, void (*cb)(struct pa_stream *p, const struct pa_latency_info*i, void *userdata), void *userdata) {
-    uint32_t tag;
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    struct timeval now;
-    assert(s && s->direction != PA_STREAM_UPLOAD);
-
-    o = pa_operation_new(s->context, s);
-    assert(o);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY);
-    pa_tagstruct_putu32(t, tag = s->context->ctag++);
-    pa_tagstruct_putu32(t, s->channel);
-
-    gettimeofday(&now, NULL);
-    pa_tagstruct_put_timeval(t, &now);
-    pa_tagstruct_putu64(t, s->counter);
-    
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_info_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-void pa_stream_disconnect_callback(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->ref >= 1);
-
-    pa_stream_ref(s);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(s->context, command, t) < 0)
-            goto finish;
-
-        pa_stream_set_state(s, PA_STREAM_FAILED);
-        goto finish;
-    } else if (!pa_tagstruct_eof(t)) {
-        pa_context_fail(s->context, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-
-    pa_stream_set_state(s, PA_STREAM_TERMINATED);
-
-finish:
-    pa_stream_unref(s);
-}
-
-void pa_stream_disconnect(struct pa_stream *s) {
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(s && s->ref >= 1);
-    
-    if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY)
-        return;
-
-    pa_stream_ref(s);
-
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    
-    pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM :
-                        (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM));
-    pa_tagstruct_putu32(t, tag = s->context->ctag++);
-    pa_tagstruct_putu32(t, s->channel);
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s);
-
-    pa_stream_unref(s);
-}
-
-void pa_stream_set_read_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) {
-    assert(s && s->ref >= 1);
-    s->read_callback = cb;
-    s->read_userdata = userdata;
-}
-
-void pa_stream_set_write_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata) {
-    assert(s && s->ref >= 1);
-    s->write_callback = cb;
-    s->write_userdata = userdata;
-}
-
-void pa_stream_set_state_callback(struct pa_stream *s, void (*cb)(struct pa_stream *s, void *userdata), void *userdata) {
-    assert(s && s->ref >= 1);
-    s->state_callback = cb;
-    s->state_userdata = userdata;
-}
-
-void pa_stream_simple_ack_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_operation *o = userdata;
-    int success = 1;
-    assert(pd && o && o->context && o->ref >= 1);
-
-    if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
-            goto finish;
-
-        success = 0;
-    } else if (!pa_tagstruct_eof(t)) {
-        pa_context_fail(o->context, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-
-    if (o->callback) {
-        void (*cb)(struct pa_stream *s, int success, void *userdata) = o->callback;
-        cb(o->stream, success, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-struct pa_operation* pa_stream_cork(struct pa_stream *s, int b, void (*cb) (struct pa_stream*s, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(s && s->ref >= 1 && s->state == PA_STREAM_READY);
-
-    if (s->interpolate) {
-        if (!s->corked && b)
-            /* Pausing */
-            s->ipol_usec = pa_stream_get_interpolated_time(s);
-        else if (s->corked && !b)
-            /* Unpausing */
-            gettimeofday(&s->ipol_timestamp, NULL);
-    }
-
-    s->corked = b;
-    
-    o = pa_operation_new(s->context, s);
-    assert(o);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM);
-    pa_tagstruct_putu32(t, tag = s->context->ctag++);
-    pa_tagstruct_putu32(t, s->channel);
-    pa_tagstruct_put_boolean(t, !!b);
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
-
-    pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
-    
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_stream_send_simple_command(struct pa_stream *s, uint32_t command, void (*cb)(struct pa_stream *s, int success, void *userdata), void *userdata) {
-    struct pa_tagstruct *t;
-    struct pa_operation *o;
-    uint32_t tag;
-    assert(s && s->ref >= 1 && s->state == PA_STREAM_READY);
-    
-    o = pa_operation_new(s->context, s);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, command);
-    pa_tagstruct_putu32(t, tag = s->context->ctag++);
-    pa_tagstruct_putu32(t, s->channel);
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-struct pa_operation* pa_stream_flush(struct pa_stream *s, void (*cb)(struct pa_stream *s, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    o = pa_stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata);
-    pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
-    return o;
-}
-
-struct pa_operation* pa_stream_prebuf(struct pa_stream *s, void (*cb)(struct pa_stream *s, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    o = pa_stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata);
-    pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
-    return o;
-}
-
-struct pa_operation* pa_stream_trigger(struct pa_stream *s, void (*cb)(struct pa_stream *s, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    o = pa_stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata);
-    pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
-    return o;
-}
-
-struct pa_operation* pa_stream_set_name(struct pa_stream *s, const char *name, void(*cb)(struct pa_stream*c, int success,  void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(s && s->ref >= 1 && s->state == PA_STREAM_READY && name && s->direction != PA_STREAM_UPLOAD);
-
-    o = pa_operation_new(s->context, s);
-    assert(o);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    pa_tagstruct_putu32(t, s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME);
-    pa_tagstruct_putu32(t, tag = s->context->ctag++);
-    pa_tagstruct_putu32(t, s->channel);
-    pa_tagstruct_puts(t, name);
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-uint64_t pa_stream_get_counter(struct pa_stream *s) {
-    assert(s);
-    return s->counter;
-}
-
-pa_usec_t pa_stream_get_time(struct pa_stream *s, const struct pa_latency_info *i) {
-    pa_usec_t usec;
-    assert(s);
-    
-    usec = pa_bytes_to_usec(i->counter, &s->sample_spec);
-
-    if (i) {
-        if (s->direction == PA_STREAM_PLAYBACK) {
-            pa_usec_t latency = i->transport_usec + i->buffer_usec + i->sink_usec;
-            if (usec < latency)
-                usec = 0;
-            else
-                usec -= latency;
-                
-        } else if (s->direction == PA_STREAM_RECORD) {
-            usec += i->source_usec + i->buffer_usec + i->transport_usec;
-
-            if (usec > i->sink_usec)
-                usec -= i->sink_usec;
-            else
-                usec = 0;
-        }
-    }
-
-    if (usec < s->previous_time)
-        usec = s->previous_time;
-
-    s->previous_time = usec;
-    
-    return usec;
-}
-
-static pa_usec_t time_counter_diff(struct pa_stream *s, pa_usec_t t, pa_usec_t c, int *negative) {
-    assert(s);
-    
-    if (negative)
-        *negative = 0;
-
-    if (c < t) {
-        if (s->direction == PA_STREAM_RECORD) {
-            if (negative)
-                *negative = 1;
-
-            return t-c;
-        } else
-            return 0;
-    } else
-        return c-t;
-}
-
-pa_usec_t pa_stream_get_latency(struct pa_stream *s, const struct pa_latency_info *i, int *negative) {
-    pa_usec_t t, c;
-    assert(s && i);
-
-    t = pa_stream_get_time(s, i);
-    c = pa_bytes_to_usec(s->counter, &s->sample_spec);
-
-    return time_counter_diff(s, t, c, negative);
-}
-
-const struct pa_sample_spec* pa_stream_get_sample_spec(struct pa_stream *s) {
-    assert(s);
-    return &s->sample_spec;
-}
-
-void pa_stream_trash_ipol(struct pa_stream *s) {
-    assert(s);
-
-    if (!s->interpolate)
-        return;
-
-    memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp));
-    s->ipol_usec = 0;
-}
-
-pa_usec_t pa_stream_get_interpolated_time(struct pa_stream *s) {
-    pa_usec_t usec;
-    assert(s && s->interpolate);
-
-    if (s->corked)
-        usec = s->ipol_usec;
-    else {
-        if (s->ipol_timestamp.tv_sec == 0)
-            usec = 0;
-        else
-            usec = s->ipol_usec + pa_timeval_age(&s->ipol_timestamp);
-    }
-    
-    if (usec < s->previous_time)
-        usec = s->previous_time;
-
-    s->previous_time = usec;
-    return usec;
-}
-
-pa_usec_t pa_stream_get_interpolated_latency(struct pa_stream *s, int *negative) {
-    pa_usec_t t, c;
-    assert(s && s->interpolate);
-
-    t = pa_stream_get_interpolated_time(s);
-    c = pa_bytes_to_usec(s->counter, &s->sample_spec);
-    return time_counter_diff(s, t, c, negative);
-}
diff --git a/polyp/polyplib-stream.h b/polyp/polyplib-stream.h
deleted file mode 100644 (file)
index 939b2ae..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-#ifndef foopolyplibstreamhfoo
-#define foopolyplibstreamhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <sys/types.h>
-
-#include <polyp/sample.h>
-#include <polyp/polyplib-def.h>
-#include <polyp/cdecl.h>
-#include <polyp/polyplib-operation.h>
-
-/** \file
- * Audio streams for input, output and sample upload */
-
-PA_C_DECL_BEGIN
-
-/** \struct pa_stream
- * An opaque stream for playback or recording */
-struct pa_stream;
-
-/** Create a new, unconnected stream with the specified name and sample type */
-struct pa_stream* pa_stream_new(struct pa_context *c, const char *name, const struct pa_sample_spec *ss);
-
-/** Decrease the reference counter by one */
-void pa_stream_unref(struct pa_stream *s);
-
-/** Increase the reference counter by one */
-struct pa_stream *pa_stream_ref(struct pa_stream *s);
-
-/** Return the current state of the stream */
-enum pa_stream_state pa_stream_get_state(struct pa_stream *p);
-
-/** Return the context this stream is attached to */
-struct pa_context* pa_stream_get_context(struct pa_stream *p);
-
-/** Return the device (sink input or source output) index this stream is connected to */
-uint32_t pa_stream_get_index(struct pa_stream *s);
-
-/** Connect the stream to a sink */
-void pa_stream_connect_playback(struct pa_stream *s, const char *dev, const struct pa_buffer_attr *attr, enum pa_stream_flags flags, pa_volume_t volume);
-
-/** Connect the stream to a source */
-void pa_stream_connect_record(struct pa_stream *s, const char *dev, const struct pa_buffer_attr *attr, enum pa_stream_flags flags);
-
-/** Disconnect a stream from a source/sink */
-void pa_stream_disconnect(struct pa_stream *s);
-
-/** Write some data to the server (for playback sinks), if free_cb is
- * non-NULL this routine is called when all data has been written out
- * and an internal reference to the specified data is kept, the data
- * is not copied. If NULL, the data is copied into an internal
- * buffer. */ 
-void pa_stream_write(struct pa_stream *p      /**< The stream to use */,
-                     const void *data         /**< The data to write */,
-                     size_t length            /**< The length of the data to write */,
-                     void (*free_cb)(void *p) /**< A cleanup routine for the data or NULL to request an internal copy */,
-                     size_t delta             /**< Drop this many
-                                                 bytes in the playback
-                                                 buffer before writing
-                                                 this data. Use
-                                                 (size_t) -1 for
-                                                 clearing the whole
-                                                 playback
-                                                 buffer. Normally you
-                                                 will specify 0 here,
-                                                 i.e. append to the
-                                                 playback buffer. If
-                                                 the value given here
-                                                 is greater than the
-                                                 buffered data length
-                                                 the buffer is cleared
-                                                 and the data is
-                                                 written to the
-                                                 buffer's start. This
-                                                 value is ignored on
-                                                 upload streams. */);
-
-/** Return the amount of bytes that may be written using pa_stream_write() */
-size_t pa_stream_writable_size(struct pa_stream *p);
-
-/** Drain a playback stream */
-struct pa_operation* pa_stream_drain(struct pa_stream *s, void (*cb) (struct pa_stream*s, int success, void *userdata), void *userdata);
-
-/** Get the playback latency of a stream */
-struct pa_operation* pa_stream_get_latency_info(struct pa_stream *p, void (*cb)(struct pa_stream *p, const struct pa_latency_info *i, void *userdata), void *userdata);
-
-/** Set the callback function that is called whenever the state of the stream changes */
-void pa_stream_set_state_callback(struct pa_stream *s, void (*cb)(struct pa_stream *s, void *userdata), void *userdata);
-
-/** Set the callback function that is called when new data may be
- * written to the stream. */
-void pa_stream_set_write_callback(struct pa_stream *p, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata);
-
-/** Set the callback function that is called when new data is available from the stream */
-void pa_stream_set_read_callback(struct pa_stream *p, void (*cb)(struct pa_stream *p, const void*data, size_t length, void *userdata), void *userdata);
-
-/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */
-struct pa_operation* pa_stream_cork(struct pa_stream *s, int b, void (*cb) (struct pa_stream*s, int success, void *userdata), void *userdata);
-
-/** Flush the playback buffer of this stream. Most of the time you're
- * better off using the parameter delta of pa_stream_write() instead of this
- * function. Available on both playback and recording streams. \since 0.3 */
-struct pa_operation* pa_stream_flush(struct pa_stream *s, void (*cb)(struct pa_stream *s, int success, void *userdata), void *userdata);
-
-/** Reenable prebuffering. Available for playback streams only. \since 0.6 */
-struct pa_operation* pa_stream_prebuf(struct pa_stream *s, void (*cb)(struct pa_stream *s, int success, void *userdata), void *userdata);
-
-/** Request immediate start of playback on this stream. This disables
- * prebuffering as specified in the pa_buffer_attr structure. Available for playback streams only. \since
- * 0.3 */
-struct pa_operation* pa_stream_trigger(struct pa_stream *s, void (*cb)(struct pa_stream *s, int success, void *userdata), void *userdata);
-
-/** Rename the stream. \since 0.5 */
-struct pa_operation* pa_stream_set_name(struct pa_stream *s, const char *name, void(*cb)(struct pa_stream*c, int success,  void *userdata), void *userdata);
-
-/** Return the total number of bytes written to/read from the
- * stream. This counter is not reset on pa_stream_flush(), you may do
- * this yourself using pa_stream_reset_counter(). \since 0.6 */
-uint64_t pa_stream_get_counter(struct pa_stream *s);
-
-/** Return the current playback/recording time. This is based on the
- * counter accessible with pa_stream_get_counter(). This function
- * requires a pa_latency_info structure as argument, which should be
- * acquired using pa_stream_get_latency(). \since 0.6 */
-pa_usec_t pa_stream_get_time(struct pa_stream *s, const struct pa_latency_info *i);
-
-/** Return the total stream latency. Thus function requires a
- * pa_latency_info structure as argument, which should be aquired
- * using pa_stream_get_latency(). In case the stream is a monitoring
- * stream the result can be negative, i.e. the captured samples are
- * not yet played. In this case *negative is set to 1. \since 0.6 */
-pa_usec_t pa_stream_get_latency(struct pa_stream *s, const struct pa_latency_info *i, int *negative);
-
-/** Return the interpolated playback/recording time. Requires the
- *  PA_STREAM_INTERPOLATE_LATENCY bit set when creating the stream. In
- *  contrast to pa_stream_get_latency() this function doesn't require
- *  a whole roundtrip for response. \since 0.6 */
-pa_usec_t pa_stream_get_interpolated_time(struct pa_stream *s);
-
-/** Return the interpolated playback/recording latency. Requires the
- * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the
- * stream. \since 0.6 */
-pa_usec_t pa_stream_get_interpolated_latency(struct pa_stream *s, int *negative);
-
-/** Return a pointer to the streams sample specification. \since 0.6 */
-const struct pa_sample_spec* pa_stream_get_sample_spec(struct pa_stream *s);
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/polyplib-subscribe.c b/polyp/polyplib-subscribe.c
deleted file mode 100644 (file)
index d7e8e7c..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdio.h>
-
-#include "polyplib-subscribe.h"
-#include "polyplib-internal.h"
-#include "pstream-util.h"
-
-void pa_command_subscribe_event(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct pa_context *c = userdata;
-    enum pa_subscription_event_type e;
-    uint32_t index;
-    assert(pd && command == PA_COMMAND_SUBSCRIBE_EVENT && t && c);
-
-    pa_context_ref(c);
-
-    if (pa_tagstruct_getu32(t, &e) < 0 ||
-        pa_tagstruct_getu32(t, &index) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        pa_context_fail(c, PA_ERROR_PROTOCOL);
-        goto finish;
-    }
-
-    if (c->subscribe_callback)
-        c->subscribe_callback(c, e, index, c->subscribe_userdata);
-
-finish:
-    pa_context_unref(c);
-}
-
-
-struct pa_operation* pa_context_subscribe(struct pa_context *c, enum pa_subscription_mask m, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) {
-    struct pa_operation *o;
-    struct pa_tagstruct *t;
-    uint32_t tag;
-    assert(c);
-
-    o = pa_operation_new(c, NULL);
-    o->callback = cb;
-    o->userdata = userdata;
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
-    pa_tagstruct_putu32(t, tag = c->ctag++);
-    pa_tagstruct_putu32(t, m);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o);
-
-    return pa_operation_ref(o);
-}
-
-void pa_context_set_subscribe_callback(struct pa_context *c, void (*cb)(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata), void *userdata) {
-    assert(c);
-    c->subscribe_callback = cb;
-    c->subscribe_userdata = userdata;
-}
diff --git a/polyp/polyplib-subscribe.h b/polyp/polyplib-subscribe.h
deleted file mode 100644 (file)
index 5c697b7..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef foopolyplibsubscribehfoo
-#define foopolyplibsubscribehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-
-#include <polyp/polyplib-def.h>
-#include <polyp/polyplib-context.h>
-#include <polyp/cdecl.h>
-
-/** \file
- * Daemon introspection event subscription subsystem. Use this
- * to be notified whenever the internal layout of daemon changes:
- * i.e. entities such as sinks or sources are create, removed or
- * modified. */
-
-PA_C_DECL_BEGIN
-
-/** Enable event notification */
-struct pa_operation* pa_context_subscribe(struct pa_context *c, enum pa_subscription_mask m, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata);
-
-/** Set the context specific call back function that is called whenever the state of the daemon changes */
-void pa_context_set_subscribe_callback(struct pa_context *c, void (*cb)(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata), void *userdata);
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/polyplib.h b/polyp/polyplib.h
deleted file mode 100644 (file)
index b9b9b44..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#ifndef foopolyplibhfoo
-#define foopolyplibhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <polyp/cdecl.h>
-#include <polyp/mainloop-api.h>
-#include <polyp/sample.h>
-#include <polyp/polyplib-def.h>
-#include <polyp/polyplib-context.h>
-#include <polyp/polyplib-stream.h>
-#include <polyp/polyplib-introspect.h>
-#include <polyp/polyplib-subscribe.h>
-#include <polyp/polyplib-scache.h>
-#include <polyp/polyplib-version.h>
-
-/** \file
- * Include all polyplib header file at once. The following files are included: \ref mainloop-api.h, \ref sample.h,
- * \ref polyplib-def.h, \ref polyplib-context.h, \ref polyplib-stream.h,
- * \ref polyplib-introspect.h, \ref polyplib-subscribe.h and \ref polyplib-scache.h \ref polyplib-version.h
- * at once */
-
-/** \mainpage
- *
- * \section intro_sec Introduction
- * 
- * This document describes the client API for the polypaudio sound
- * server. The API comes in two flavours:
- * 
- * \li The complete but somewhat complicated to use asynchronous API
- * \li And the simplified, easy to use, but limited synchronous API
- *
- * The polypaudio client libraries are thread safe as long as all
- * objects created by any library function are accessed from the thread
- * that created them only.
- * 
- * \section simple_sec Simple API
- *
- * Use this if you develop your program in synchronous style and just
- * need a way to play or record data on the sound server. See
- * \ref polyplib-simple.h for more details.
- *
- * \section async_api Asynchronous API
- *
- * Use this if you develop your programs in asynchronous, main loop
- * based style or want to use advanced features of the polypaudio
- * API. A good starting point is \ref polyplib-context.h
- *
- * The asynchronous API relies on an abstract main loop API that is
- * described in \ref mainloop-api.h. Two distinct implementations are
- * available:
- * 
- * \li \ref mainloop.h: a minimal but fast implementation based on poll()
- * \li \ref glib-mainloop.h: a wrapper around GLIB's main loop
- *
- * UNIX signals may be hooked to a main loop using the functions from
- * \ref mainloop-signal.h
- *
- * \section pkgconfig pkg-config
- *
- * The polypaudio libraries provide pkg-config snippets for the different modules. To use the
- * asynchronous API use "polyplib" as pkg-config file. GLIB main loop
- * support is available as "polyplib-glib-mainloop". The simple
- * synchronous API is available as "polyplib-simple".
- */
-
-#endif
diff --git a/polyp/protocol-cli.c b/polyp/protocol-cli.c
deleted file mode 100644 (file)
index 7122d23..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "protocol-cli.h"
-#include "cli.h"
-#include "xmalloc.h"
-#include "log.h"
-
-/* Don't allow more than this many concurrent connections */
-#define MAX_CONNECTIONS 10
-
-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);
-
-    if (pa_idxset_ncontents(p->connections)+1 > MAX_CONNECTIONS) {
-        pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
-        pa_iochannel_free(io);
-        return;
-    }
-    
-    c = pa_cli_new(p->core, io, p->module);
-    assert(c);
-    pa_cli_set_eof_callback(c, cli_eof_cb, p);
-
-    pa_idxset_put(p->connections, c, NULL);
-}
-
-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 = pa_xmalloc(sizeof(struct pa_protocol_cli));
-    p->module = m;
-    p->core = core;
-    p->server = server;
-    p->connections = pa_idxset_new(NULL, NULL);
-
-    pa_socket_server_set_callback(p->server, on_connection, p);
-    
-    return p;
-}
-
-static void free_connection(void *p, 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_unref(p->server);
-    pa_xfree(p);
-}
diff --git a/polyp/protocol-cli.h b/polyp/protocol-cli.h
deleted file mode 100644 (file)
index e0fe4bc..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef fooprotocolclihfoo
-#define fooprotocolclihfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "core.h"
-#include "socket-server.h"
-#include "module.h"
-#include "modargs.h"
-
-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
deleted file mode 100644 (file)
index d99b721..0000000
+++ /dev/null
@@ -1,1159 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <limits.h>
-
-#include "protocol-esound.h"
-#include "esound.h"
-#include "memblock.h"
-#include "client.h"
-#include "sink-input.h"
-#include "sink.h"
-#include "source-output.h"
-#include "source.h"
-#include "sample.h"
-#include "scache.h"
-#include "sample-util.h"
-#include "authkey.h"
-#include "namereg.h"
-#include "xmalloc.h"
-#include "log.h"
-
-/* Don't accept more connection than this */
-#define MAX_CONNECTIONS 10
-
-/* Kick a client if it doesn't authenticate within this time */
-#define AUTH_TIMEOUT 5
-
-#define DEFAULT_COOKIE_FILE ".esd_auth"
-
-#define PLAYBACK_BUFFER_SECONDS (.5)
-#define PLAYBACK_BUFFER_FRAGMENTS (10)
-#define RECORD_BUFFER_SECONDS (5)
-#define RECORD_BUFFER_FRAGMENTS (100)
-
-#define MAX_CACHE_SAMPLE_SIZE (1024000)
-
-#define SCACHE_PREFIX "esound."
-
-#define PA_TYPEID_ESOUND PA_TYPEID_MAKE('E', 'S', 'D', 'P')
-
-/* This is heavily based on esound's code */
-
-struct connection {
-    uint32_t index;
-    int dead;
-    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;
-    struct pa_defer_event *defer_event;
-    
-    struct {
-        struct pa_memblock *current_memblock;
-        size_t memblock_index, fragment_size;
-    } playback;
-
-    struct {
-        struct pa_memchunk memchunk;
-        char *name;
-        struct pa_sample_spec sample_spec;
-    } scache;
-
-    struct pa_time_event *auth_timeout_event;
-};
-
-struct pa_protocol_esound {
-    int public;
-    struct pa_module *module;
-    struct pa_core *core;
-    struct pa_socket_server *server;
-    struct pa_idxset *connections;
-    char *sink_name, *source_name;
-    unsigned n_player;
-    uint8_t esd_key[ESD_KEY_LEN];
-};
-
-typedef struct proto_handler {
-    size_t data_length;
-    int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length);
-    const char *description;
-} esd_proto_handler_info_t;
-
-static void sink_input_drop_cb(struct pa_sink_input *i, const struct pa_memchunk *chunk, 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 pa_usec_t sink_input_get_latency_cb(struct pa_sink_input *i);
-static pa_usec_t source_output_get_latency_cb(struct pa_source_output *o);
-
-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);
-static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length);
-static int esd_proto_standby_or_resume(struct connection *c, esd_proto_t request, const void *data, size_t length);
-
-/* the big map of protocol handler info */
-static struct proto_handler proto_map[ESD_PROTO_MAX] = {
-    { ESD_KEY_LEN + sizeof(int),      esd_proto_connect, "connect" },
-    { ESD_KEY_LEN + sizeof(int),      NULL, "lock" },
-    { ESD_KEY_LEN + sizeof(int),      NULL, "unlock" },
-
-    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" },
-    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" },
-    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" },
-
-    { ESD_NAME_MAX + 3 * sizeof(int), esd_proto_sample_cache, "sample cache" },                      /* 6 */
-    { sizeof(int),                    esd_proto_sample_free_or_play, "sample free" },
-    { sizeof(int),                    esd_proto_sample_free_or_play, "sample play" },                /* 8 */
-    { sizeof(int),                    NULL, "sample loop" },
-    { sizeof(int),                    NULL, "sample stop" },
-    { -1,                             NULL, "TODO: sample kill" },
-
-    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "standby" },  /* NOOP! */
-    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "resume" },   /* NOOP! */         /* 13 */
-
-    { ESD_NAME_MAX,                   esd_proto_sample_get_id, "sample getid" },                     /* 14 */
-    { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
-
-    { sizeof(int),                    esd_proto_server_info, "server info" },
-    { sizeof(int),                    esd_proto_all_info, "all info" },
-    { -1,                             NULL, "TODO: subscribe" },
-    { -1,                             NULL, "TODO: unsubscribe" },
-
-    { 3 * sizeof(int),                esd_proto_stream_pan, "stream pan"},
-    { 3 * sizeof(int),                NULL, "sample pan" },
-     
-    { sizeof(int),                    NULL, "standby mode" },
-    { 0,                              esd_proto_get_latency, "get latency" }
-};
-
-
-static void connection_free(struct connection *c) {
-    assert(c);
-    pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
-
-    if (c->state == ESD_STREAMING_DATA)
-        c->protocol->n_player--;
-    
-    pa_client_free(c->client);
-
-    if (c->sink_input) {
-        pa_sink_input_disconnect(c->sink_input);
-        pa_sink_input_unref(c->sink_input);
-    }
-    
-    if (c->source_output) {
-        pa_source_output_disconnect(c->source_output);
-        pa_source_output_unref(c->source_output);
-    }
-    
-    if (c->input_memblockq)
-        pa_memblockq_free(c->input_memblockq);
-    if (c->output_memblockq)
-        pa_memblockq_free(c->output_memblockq);
-
-    if (c->playback.current_memblock)
-        pa_memblock_unref(c->playback.current_memblock);
-    
-    pa_xfree(c->read_data);
-    pa_xfree(c->write_data);
-
-    if (c->io)
-        pa_iochannel_free(c->io);
-    
-    if (c->defer_event)
-        c->protocol->core->mainloop->defer_free(c->defer_event);
-
-    if (c->scache.memchunk.memblock)
-        pa_memblock_unref(c->scache.memchunk.memblock);
-    pa_xfree(c->scache.name);
-
-    if (c->auth_timeout_event)
-        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
-    
-    pa_xfree(c);
-}
-
-static void* connection_write(struct connection *c, size_t length) {
-    size_t t, i;
-    assert(c);
-
-    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
-    c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
-
-    t = c->write_data_length+length;
-    
-    if (c->write_data_alloc < t)
-        c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t);
-
-    assert(c->write_data);
-
-    i = c->write_data_length;
-    c->write_data_length += length;
-    
-    return (uint8_t*) c->write_data+i;
-}
-
-static void format_esd2native(int format, struct pa_sample_spec *ss) {
-    assert(ss);
-
-    ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
-    ss->format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
-}
-
-static int format_native2esd(struct pa_sample_spec *ss) {
-    int format = 0;
-    
-    format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
-    format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO;
-
-    return format;
-}
-
-/*** esound commands ***/
-
-static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length) {
-    uint32_t ekey;
-    int *ok;
-    assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
-
-    if (!c->authorized) {
-        if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) {
-            pa_log(__FILE__": kicked client with invalid authorization key.\n");
-            return -1;
-        }
-
-        c->authorized = 1;
-        if (c->auth_timeout_event) {
-            c->protocol->core->mainloop->time_free(c->auth_timeout_event);
-            c->auth_timeout_event = NULL;
-        }
-    }
-    
-    ekey = *(uint32_t*)((uint8_t*) data+ESD_KEY_LEN);
-    if (ekey == ESD_ENDIAN_KEY)
-        c->swap_byte_order = 0;
-    else if (ekey == ESD_SWAP_ENDIAN_KEY)
-        c->swap_byte_order = 1;
-    else {
-        pa_log(__FILE__": client sent invalid endian key\n");
-        return -1;
-    }
-
-    ok = connection_write(c, sizeof(int));
-    assert(ok);
-    *ok = 1;
-    return 0;
-}
-
-static int esd_proto_stream_play(struct connection *c, 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;
-    format_esd2native(format, &ss);
-
-    if (!pa_sample_spec_valid(&ss)) {
-        pa_log(__FILE__": invalid sample specification\n");
-        return -1;
-    }
-
-    if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
-        pa_log(__FILE__": no such sink\n");
-        return -1;
-    }
-    
-    strncpy(name, (char*) data + sizeof(int)*2, sizeof(name));
-    name[sizeof(name)-1] = 0;
-
-    pa_client_set_name(c->client, name);
-
-    assert(!c->sink_input && !c->input_memblockq);
-
-    if (!(c->sink_input = pa_sink_input_new(sink, PA_TYPEID_ESOUND, name, &ss, 0, -1))) {
-        pa_log(__FILE__": failed to create sink input.\n");
-        return -1;
-    }
-
-    l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS); 
-    c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS, c->protocol->core->memblock_stat);
-    pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
-    c->playback.fragment_size = l/10;
-
-    c->sink_input->owner = c->protocol->module;
-    c->sink_input->client = c->client;
-    c->sink_input->peek = sink_input_peek_cb;
-    c->sink_input->drop = sink_input_drop_cb;
-    c->sink_input->kill = sink_input_kill_cb;
-    c->sink_input->get_latency = sink_input_get_latency_cb;
-    c->sink_input->userdata = c;
-
-    c->state = ESD_STREAMING_DATA;
-
-    c->protocol->n_player++;
-    
-    return 0;
-}
-
-static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) {
-    char name[ESD_NAME_MAX];
-    int format, rate;
-    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;
-    format_esd2native(format, &ss);
-
-    if (!pa_sample_spec_valid(&ss)) {
-        pa_log(__FILE__": invalid sample specification.\n");
-        return -1;
-    }
-
-    if (request == ESD_PROTO_STREAM_MON) {
-        struct pa_sink* sink;
-
-        if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
-            pa_log(__FILE__": no such sink.\n");
-            return -1;
-        }
-
-        if (!(source = sink->monitor_source)) {
-            pa_log(__FILE__": no such monitor source.\n");
-            return -1;
-        }
-    } else {
-        assert(request == ESD_PROTO_STREAM_REC);
-        
-        if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) {
-            pa_log(__FILE__": no such source.\n");
-            return -1;
-        }
-    }
-    
-    strncpy(name, (char*) data + sizeof(int)*2, sizeof(name));
-    name[sizeof(name)-1] = 0;
-
-    pa_client_set_name(c->client, name);
-
-    assert(!c->output_memblockq && !c->source_output);
-
-    if (!(c->source_output = pa_source_output_new(source, PA_TYPEID_ESOUND, name, &ss, -1))) {
-        pa_log(__FILE__": failed to create source output\n");
-        return -1;
-    }
-
-    l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS); 
-    c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), 0, 0, c->protocol->core->memblock_stat);
-    pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
-    
-    c->source_output->owner = c->protocol->module;
-    c->source_output->client = c->client;
-    c->source_output->push = source_output_push_cb;
-    c->source_output->kill = source_output_kill_cb;
-    c->source_output->get_latency = source_output_get_latency_cb;
-    c->source_output->userdata = c;
-
-    c->state = ESD_STREAMING_DATA;
-
-    c->protocol->n_player++;
-    
-    return 0;
-}
-
-static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length) {
-    struct pa_sink *sink;
-    int latency, *lag;
-    assert(c && !data && length == 0);
-
-    if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
-        latency = 0;
-    else {
-        double usec = pa_sink_get_latency(sink);
-        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 = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
-        rate = sink->sample_spec.rate;
-        format = format_native2esd(&sink->sample_spec);
-    }
-    
-    response = connection_write(c, sizeof(int)*3);
-    assert(response);
-    *(response++) = 0;
-    *(response++) = maybe_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) {
-    uint8_t *response;
-    size_t t, k, s;
-    struct connection *conn;
-    size_t index = PA_IDXSET_INVALID;
-    unsigned nsamples;
-    assert(c && data && length == sizeof(int));
-    
-    if (esd_proto_server_info(c, request, data, length) < 0)
-        return -1;
-
-    k = sizeof(int)*5+ESD_NAME_MAX;
-    s = sizeof(int)*6+ESD_NAME_MAX;
-    nsamples = c->protocol->core->scache ? pa_idxset_ncontents(c->protocol->core->scache) : 0;
-    response = connection_write(c, (t = s*(nsamples+1) + k*(c->protocol->n_player+1)));
-    assert(k);
-
-    for (conn = pa_idxset_first(c->protocol->connections, &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 = format_native2esd(&conn->sink_input->sample_spec);
-        }
-        
-        /* id */
-        *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, (int) (conn->index+1));
-        response += sizeof(int);
-
-        /* name */
-        assert(conn->client);
-        strncpy((char*) response, conn->client->name, ESD_NAME_MAX);
-        response += ESD_NAME_MAX;
-
-        /* rate */
-        *((int*) response) = maybe_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*(nsamples+1)+k);
-    memset(response, 0, k);
-    response += k;
-    t -= k;
-
-    if (nsamples) {
-        struct pa_scache_entry *ce;
-        
-        index = PA_IDXSET_INVALID;
-        for (ce = pa_idxset_first(c->protocol->core->scache, &index); ce; ce = pa_idxset_next(c->protocol->core->scache, &index)) {
-            assert(t >= s*2);
-            
-            /* id */
-            *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, (int) (ce->index+1));
-            response += sizeof(int);
-            
-            /* name */
-            if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0)
-                strncpy((char*) response, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);
-            else
-                snprintf((char*) response, ESD_NAME_MAX, "native.%s", ce->name);
-            response += ESD_NAME_MAX;
-            
-            /* rate */
-            *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, ce->sample_spec.rate);
-            response += sizeof(int);
-            
-            /* left */
-            *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, (ce->volume*0xFF)/0x100);
-            response += sizeof(int);
-            
-            /*right*/
-            *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, (ce->volume*0xFF)/0x100);
-            response += sizeof(int);
-            
-            /*format*/
-            *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, format_native2esd(&ce->sample_spec));
-            response += sizeof(int);
-
-            /*length*/
-            *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, (int) ce->memchunk.length);
-            response += sizeof(int);
-
-            t -= s;
-        }
-    }
-
-    assert(t == s);
-    memset(response, 0, s);
-
-    return 0;
-}
-
-static int esd_proto_stream_pan(struct connection *c, 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)-1;
-    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;
-}
-
-static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length) {
-    struct pa_sample_spec ss;
-    int format, rate;
-    size_t sc_length;
-    uint32_t index;
-    int *ok;
-    char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
-    assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int)));
-
-    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;
-    format_esd2native(format, &ss);
-
-    sc_length = (size_t) maybe_swap_endian_32(c->swap_byte_order, (*((int*)data + 2)));
-
-    if (sc_length >= MAX_CACHE_SAMPLE_SIZE)
-        return -1;
-
-    strcpy(name, SCACHE_PREFIX);
-    strncpy(name+sizeof(SCACHE_PREFIX)-1, (char*) data+3*sizeof(int), ESD_NAME_MAX);
-    name[sizeof(name)-1] = 0;
-    
-    assert(!c->scache.memchunk.memblock);
-    c->scache.memchunk.memblock = pa_memblock_new(sc_length, c->protocol->core->memblock_stat);
-    c->scache.memchunk.index = 0;
-    c->scache.memchunk.length = sc_length;
-    c->scache.sample_spec = ss;
-    assert(!c->scache.name);
-    c->scache.name = pa_xstrdup(name);
-
-    c->state = ESD_CACHING_SAMPLE;
-
-    pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, &index);
-
-    ok = connection_write(c, sizeof(int));
-    assert(ok);
-    
-    *ok = index+1;
-    
-    return 0;
-}
-
-static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length) {
-    int *ok;
-    uint32_t index;
-    char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
-    assert(c && data && length == ESD_NAME_MAX);
-
-    ok = connection_write(c, sizeof(int));
-    assert(ok);
-
-    *ok = -1;
-
-    strcpy(name, SCACHE_PREFIX);
-    strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
-    name[sizeof(name)-1] = 0;
-
-    if ((index = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID)
-        *ok = (int) index +1;
-
-    return 0;
-}
-
-static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) {
-    int *ok;
-    const char *name;
-    uint32_t index;
-    assert(c && data && length == sizeof(int));
-
-    index = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *(int*)data)-1;
-
-    ok = connection_write(c, sizeof(int));
-    assert(ok);
-
-    *ok = 0;
-    
-    if ((name = pa_scache_get_name_by_id(c->protocol->core, index))) {
-        if (request == ESD_PROTO_SAMPLE_PLAY) {
-            struct pa_sink *sink;
-        
-            if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
-                if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0)
-                    *ok = (int) index+1;
-        } else {
-            assert(request == ESD_PROTO_SAMPLE_FREE);
-
-            if (pa_scache_remove_item(c->protocol->core, name) >= 0)
-                *ok = (int) index+1;
-        }
-    }
-    
-    return 0;
-}
-
-static int esd_proto_standby_or_resume(struct connection *c, esd_proto_t request, const void *data, size_t length) {
-    int *ok;
-    ok = connection_write(c, sizeof(int)*2);
-    assert(ok);
-    ok[0] = 1;
-    ok[1] = 1;
-    return 0;
-}
-
-/*** client callbacks ***/
-
-static void client_kill_cb(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);
-
-/*      pa_log("READ\n");  */
-    
-    if (c->state == ESD_NEXT_REQUEST) {
-        ssize_t r;
-        assert(c->read_data_length < sizeof(c->request));
-
-        if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
-            if (r != 0)
-                pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno));
-            return -1;
-        }
-
-        if ((c->read_data_length+= r) >= sizeof(c->request)) {
-            struct proto_handler *handler;
-            
-            if (c->swap_byte_order)
-                c->request = swap_endian_32(c->request);
-
-            if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
-                pa_log(__FILE__": recieved invalid request.\n");
-                return -1;
-            }
-
-            handler = proto_map+c->request;
-
-/*             pa_log(__FILE__": executing request #%u\n", c->request); */
-
-            if (!handler->proc) {
-                pa_log(__FILE__": recieved unimplemented request #%u.\n", c->request);
-                return -1;
-            }
-            
-            if (handler->data_length == 0) {
-                c->read_data_length = 0;
-
-                if (handler->proc(c, c->request, NULL, 0) < 0)
-                    return -1;
-                
-            } else {
-                if (c->read_data_alloc < handler->data_length)
-                    c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length);
-                assert(c->read_data);
-                
-                c->state = ESD_NEEDS_REQDATA;
-                c->read_data_length = 0;
-            }
-        }
-
-    } else if (c->state == ESD_NEEDS_REQDATA) {
-        ssize_t r;
-        struct proto_handler *handler = proto_map+c->request;
-
-        assert(handler->proc);
-        
-        assert(c->read_data && c->read_data_length < handler->data_length);
-
-        if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
-            if (r != 0)
-                pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno));
-            return -1;
-        }
-
-        if ((c->read_data_length+= r) >= handler->data_length) {
-            size_t l = c->read_data_length;
-            assert(handler->proc);
-
-            c->state = ESD_NEXT_REQUEST;
-            c->read_data_length = 0;
-            
-            if (handler->proc(c, c->request, c->read_data, l) < 0)
-                return -1;
-        }
-    } else if (c->state == ESD_CACHING_SAMPLE) {
-        ssize_t r;
-
-        assert(c->scache.memchunk.memblock && c->scache.name && c->scache.memchunk.index < c->scache.memchunk.length);
-        
-        if ((r = pa_iochannel_read(c->io, (uint8_t*) c->scache.memchunk.memblock->data+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) {
-            if (r!= 0)
-                pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno));
-            return -1;
-        }
-
-        c->scache.memchunk.index += r;
-        assert(c->scache.memchunk.index <= c->scache.memchunk.length);
-        
-        if (c->scache.memchunk.index == c->scache.memchunk.length) {
-            uint32_t index;
-            int *ok;
-            
-            c->scache.memchunk.index = 0;
-            pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, &c->scache.memchunk, &index);
-
-            pa_memblock_unref(c->scache.memchunk.memblock);
-            c->scache.memchunk.memblock = NULL;
-            c->scache.memchunk.index = c->scache.memchunk.length = 0;
-
-            pa_xfree(c->scache.name);
-            c->scache.name = NULL;
-
-            c->state = ESD_NEXT_REQUEST;
-
-            ok = connection_write(c, sizeof(int));
-            assert(ok);
-            *ok = index+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);
-
-/*         pa_log("STREAMING_DATA\n"); */
-
-        if (!(l = pa_memblockq_missing(c->input_memblockq)))
-            return 0;
-
-        if (l > c->playback.fragment_size)
-            l = c->playback.fragment_size;
-
-        if (c->playback.current_memblock) 
-            if (c->playback.current_memblock->length - c->playback.memblock_index < l) {
-                pa_memblock_unref(c->playback.current_memblock);
-                c->playback.current_memblock = NULL;
-                c->playback.memblock_index = 0;
-            }
-        
-        if (!c->playback.current_memblock) {
-            c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2, c->protocol->core->memblock_stat);
-            assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
-            c->playback.memblock_index = 0;
-        }
-
-        if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
-            if (r != 0)
-                pa_log(__FILE__": read() failed: %s\n", strerror(errno));
-            return -1;
-        }
-        
-/*         pa_log(__FILE__": read %u\n", r);  */
-        
-        chunk.memblock = c->playback.current_memblock;
-        chunk.index = c->playback.memblock_index;
-        chunk.length = r;
-        assert(chunk.memblock);
-
-        c->playback.memblock_index += r;
-        
-        assert(c->input_memblockq);
-        pa_memblockq_push_align(c->input_memblockq, &chunk, 0);
-        assert(c->sink_input);
-        pa_sink_notify(c->sink_input->sink);
-    }
-    
-    return 0;
-}
-
-static int do_write(struct connection *c) {
-    assert(c && c->io);
-
-/*     pa_log("WRITE\n"); */
-    
-    if (c->write_data_length) {
-        ssize_t r;
-        
-        assert(c->write_data_index < c->write_data_length);
-        if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
-            pa_log(__FILE__": write() failed: %s\n", strerror(errno));
-            return -1;
-        }
-        
-        if ((c->write_data_index +=r) >= c->write_data_length)
-            c->write_data_length = c->write_data_index = 0;
-        
-    } else if (c->state == ESD_STREAMING_DATA && c->source_output) {
-        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, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) {
-            pa_memblock_unref(chunk.memblock);
-            pa_log(__FILE__": write(): %s\n", strerror(errno));
-            return -1;
-        }
-
-        pa_memblockq_drop(c->output_memblockq, &chunk, r);
-        pa_memblock_unref(chunk.memblock);
-    }
-    
-    return 0;
-}
-
-static void do_work(struct connection *c) {
-    assert(c);
-
-    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
-    c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
-
-/*     pa_log("DOWORK %i\n", pa_iochannel_is_hungup(c->io));   */
-
-    if (!c->dead && pa_iochannel_is_readable(c->io))
-        if (do_read(c) < 0)
-            goto fail;
-
-    if (!c->dead && pa_iochannel_is_writable(c->io))
-        if (do_write(c) < 0)
-            goto fail;
-
-    /* In case the line was hungup, make sure to rerun this function
-       as soon as possible, until all data has been read. */
-
-    if (!c->dead && pa_iochannel_is_hungup(c->io))
-        c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
-    
-    return;
-
-fail:
-
-    if (c->state == ESD_STREAMING_DATA && c->sink_input) {
-        c->dead = 1;
-        pa_memblockq_prebuf_disable(c->input_memblockq);
-
-        pa_iochannel_free(c->io);
-        c->io = NULL;
-        
-    } else
-        connection_free(c);
-}
-
-static void io_callback(struct pa_iochannel*io, void *userdata) {
-    struct connection *c = userdata;
-    assert(io && c && c->io == io);
-
-/*     pa_log("IO\n");  */
-    
-    do_work(c);
-}
-
-/*** defer callback ***/
-
-static void defer_callback(struct pa_mainloop_api*a, struct pa_defer_event *e, void *userdata) {
-    struct connection *c = userdata;
-    assert(a && c && c->defer_event == e);
-
-/*     pa_log("DEFER\n"); */
-    
-    do_work(c);
-}
-
-/*** sink_input callbacks ***/
-
-static int sink_input_peek_cb(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) {
-
-        if (c->dead)
-            connection_free(c);
-        
-        return -1;
-    }
-
-    return 0;
-}
-
-static void sink_input_drop_cb(struct pa_sink_input *i, const struct pa_memchunk *chunk, size_t length) {
-    struct connection*c = i->userdata;
-    assert(i && c && length);
-
-/*     pa_log("DROP\n"); */
-    
-    pa_memblockq_drop(c->input_memblockq, chunk, length);
-
-    /* do something */
-    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
-
-    if (!c->dead)
-        c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
-
-/*     assert(pa_memblockq_get_length(c->input_memblockq) > 2048); */
-}
-
-static void sink_input_kill_cb(struct pa_sink_input *i) {
-    assert(i && i->userdata);
-    connection_free((struct connection *) i->userdata);
-}
-
-static pa_usec_t sink_input_get_latency_cb(struct pa_sink_input *i) {
-    struct connection*c = i->userdata;
-    assert(i && c);
-    return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
-}
-
-/*** source_output callbacks ***/
-
-static void source_output_push_cb(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->defer_enable);
-
-    if (!c->dead)
-        c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
-}
-
-static void source_output_kill_cb(struct pa_source_output *o) {
-    assert(o && o->userdata);
-    connection_free((struct connection *) o->userdata);
-}
-
-static pa_usec_t source_output_get_latency_cb(struct pa_source_output *o) {
-    struct connection*c = o->userdata;
-    assert(o && c);
-    return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
-}
-
-/*** socket server callback ***/
-
-static void auth_timeout(struct pa_mainloop_api*m, struct pa_time_event *e, const struct timeval *tv, void *userdata) {
-    struct connection *c = userdata;
-    assert(m && tv && c && c->auth_timeout_event == e);
-
-    if (!c->authorized)
-        connection_free(c);
-}
-
-static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
-    struct connection *c;
-    struct pa_protocol_esound *p = userdata;
-    char cname[256];
-    assert(s && io && p);
-
-    if (pa_idxset_ncontents(p->connections)+1 > MAX_CONNECTIONS) {
-        pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
-        pa_iochannel_free(io);
-        return;
-    }
-    
-    c = pa_xmalloc(sizeof(struct connection));
-    c->protocol = p;
-    c->io = io;
-    pa_iochannel_set_callback(c->io, io_callback, c);
-
-    pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
-    assert(p->core);
-    c->client = pa_client_new(p->core, PA_TYPEID_ESOUND, cname);
-    assert(c->client);
-    c->client->owner = p->module;
-    c->client->kill = client_kill_cb;
-    c->client->userdata = c;
-    
-    c->authorized = p->public;
-    c->swap_byte_order = 0;
-    c->dead = 0;
-
-    c->read_data_length = 0;
-    c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
-
-    c->write_data_length = c->write_data_index = c->write_data_alloc = 0;
-    c->write_data = NULL;
-
-    c->state = ESD_NEEDS_REQDATA;
-    c->request = ESD_PROTO_CONNECT;
-
-    c->sink_input = NULL;
-    c->input_memblockq = NULL;
-
-    c->source_output = NULL;
-    c->output_memblockq = NULL;
-
-    c->playback.current_memblock = NULL;
-    c->playback.memblock_index = 0;
-    c->playback.fragment_size = 0;
-
-    c->scache.memchunk.length = c->scache.memchunk.index = 0;
-    c->scache.memchunk.memblock = NULL;
-    c->scache.name = NULL;
-
-    if (!c->authorized) {
-        struct timeval tv;
-        gettimeofday(&tv, NULL);
-        tv.tv_sec += AUTH_TIMEOUT;
-        c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
-    } else
-        c->auth_timeout_event = NULL;
-    
-    c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
-    assert(c->defer_event);
-    p->core->mainloop->defer_enable(c->defer_event, 0);
-
-    pa_idxset_put(p->connections, c, &c->index);
-}
-
-/*** entry points ***/
-
-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) {
-    struct pa_protocol_esound *p;
-    int public = 0;
-    assert(core && server && ma);
-
-    p = pa_xmalloc(sizeof(struct pa_protocol_esound));
-
-    if (pa_modargs_get_value_boolean(ma, "public", &public) < 0) {
-        pa_log(__FILE__": public= expects a boolean argument.\n");
-        return NULL;
-    }
-
-    if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0) {
-        pa_xfree(p);
-        return NULL;
-    }
-
-    p->module = m;
-    p->public = public;
-    p->server = server;
-    pa_socket_server_set_callback(p->server, on_connection, p);
-    p->core = core;
-    p->connections = pa_idxset_new(NULL, NULL);
-    assert(p->connections);
-
-    p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
-    p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
-    p->n_player = 0;
-
-    return p;
-}
-
-void pa_protocol_esound_free(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_unref(p->server);
-    pa_xfree(p);
-}
diff --git a/polyp/protocol-esound.h b/polyp/protocol-esound.h
deleted file mode 100644 (file)
index dda6977..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef fooprotocolesoundhfoo
-#define fooprotocolesoundhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "core.h"
-#include "socket-server.h"
-#include "module.h"
-#include "modargs.h"
-
-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-http.h b/polyp/protocol-http.h
deleted file mode 100644 (file)
index 3c9b8d7..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef fooprotocolhttphfoo
-#define fooprotocolhttphfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "core.h"
-#include "socket-server.h"
-#include "module.h"
-#include "modargs.h"
-
-struct pa_protocol_http;
-
-struct pa_protocol_http* pa_protocol_http_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma);
-void pa_protocol_http_free(struct pa_protocol_http *n);
-
-#endif
diff --git a/polyp/protocol-native.c b/polyp/protocol-native.c
deleted file mode 100644 (file)
index d5619ef..0000000
+++ /dev/null
@@ -1,2176 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-
-#include "protocol-native.h"
-#include "native-common.h"
-#include "packet.h"
-#include "client.h"
-#include "source-output.h"
-#include "sink-input.h"
-#include "pstream.h"
-#include "tagstruct.h"
-#include "pdispatch.h"
-#include "pstream-util.h"
-#include "authkey.h"
-#include "namereg.h"
-#include "scache.h"
-#include "xmalloc.h"
-#include "util.h"
-#include "subscribe.h"
-#include "log.h"
-#include "autoload.h"
-#include "authkey-prop.h"
-#include "strlist.h"
-#include "props.h"
-
-/* Kick a client if it doesn't authenticate within this time */
-#define AUTH_TIMEOUT 5
-
-/* Don't accept more connection than this */
-#define MAX_CONNECTIONS 10
-
-#define PA_TYPEID_NATIVE PA_TYPEID_MAKE('N', 'A', 'T', 'V')
-
-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 {
-    int type;
-    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 upload_stream {
-    int type;
-    struct connection *connection;
-    uint32_t index;
-    struct pa_memchunk memchunk;
-    size_t length;
-    char *name;
-    struct pa_sample_spec sample_spec;
-};
-
-struct output_stream {
-    int type;
-};
-
-enum {
-    UPLOAD_STREAM,
-    PLAYBACK_STREAM
-};
-
-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, *output_streams;
-    uint32_t rrobin_index;
-    struct pa_subscription *subscription;
-    struct pa_time_event *auth_timeout_event;
-};
-
-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];
-    int auth_cookie_in_property;
-};
-
-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, const struct pa_memchunk *chunk, size_t length);
-static void sink_input_kill_cb(struct pa_sink_input *i);
-static pa_usec_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 pa_usec_t source_output_get_latency_cb(struct pa_source_output *o);
-
-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_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_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_client_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 void command_get_record_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_create_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_finish_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_play_sample(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_remove_sample(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_get_info(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_get_info_list(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_get_server_info(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_subscribe(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_set_volume(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_cork_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_flush_or_trigger_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_set_default_sink_or_source(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_set_stream_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_kill(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_load_module(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_unload_module(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_add_autoload(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_remove_autoload(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_get_autoload_info(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_get_autoload_info_list(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_cork_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
-static void command_flush_record_stream(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_stream },
-    [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = { command_drain_playback_stream },
-    [PA_COMMAND_CREATE_RECORD_STREAM] = { command_create_record_stream },
-    [PA_COMMAND_DELETE_RECORD_STREAM] = { command_delete_stream },
-    [PA_COMMAND_AUTH] = { command_auth },
-    [PA_COMMAND_REQUEST] = { NULL },
-    [PA_COMMAND_EXIT] = { command_exit },
-    [PA_COMMAND_SET_CLIENT_NAME] = { command_set_client_name },
-    [PA_COMMAND_LOOKUP_SINK] = { command_lookup },
-    [PA_COMMAND_LOOKUP_SOURCE] = { command_lookup },
-    [PA_COMMAND_STAT] = { command_stat },
-    [PA_COMMAND_GET_PLAYBACK_LATENCY] = { command_get_playback_latency },
-    [PA_COMMAND_GET_RECORD_LATENCY] = { command_get_record_latency },
-    [PA_COMMAND_CREATE_UPLOAD_STREAM] = { command_create_upload_stream },
-    [PA_COMMAND_DELETE_UPLOAD_STREAM] = { command_delete_stream },
-    [PA_COMMAND_FINISH_UPLOAD_STREAM] = { command_finish_upload_stream },
-    [PA_COMMAND_PLAY_SAMPLE] = { command_play_sample },
-    [PA_COMMAND_REMOVE_SAMPLE] = { command_remove_sample },
-    [PA_COMMAND_GET_SINK_INFO] = { command_get_info },
-    [PA_COMMAND_GET_SOURCE_INFO] = { command_get_info },
-    [PA_COMMAND_GET_CLIENT_INFO] = { command_get_info },
-    [PA_COMMAND_GET_MODULE_INFO] = { command_get_info },
-    [PA_COMMAND_GET_SINK_INPUT_INFO] = { command_get_info },
-    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = { command_get_info },
-    [PA_COMMAND_GET_SAMPLE_INFO] = { command_get_info },
-    [PA_COMMAND_GET_SINK_INFO_LIST] = { command_get_info_list },
-    [PA_COMMAND_GET_SOURCE_INFO_LIST] = { command_get_info_list },
-    [PA_COMMAND_GET_MODULE_INFO_LIST] = { command_get_info_list },
-    [PA_COMMAND_GET_CLIENT_INFO_LIST] = { command_get_info_list },
-    [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = { command_get_info_list },
-    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = { command_get_info_list },
-    [PA_COMMAND_GET_SAMPLE_INFO_LIST] = { command_get_info_list },
-    [PA_COMMAND_GET_SERVER_INFO] = { command_get_server_info },
-    [PA_COMMAND_SUBSCRIBE] = { command_subscribe },
-
-    [PA_COMMAND_SET_SINK_VOLUME] = { command_set_volume },
-    [PA_COMMAND_SET_SINK_INPUT_VOLUME] = { command_set_volume },
-    
-    [PA_COMMAND_CORK_PLAYBACK_STREAM] = { command_cork_playback_stream },
-    [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = { command_flush_or_trigger_playback_stream },
-    [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = { command_flush_or_trigger_playback_stream },
-    [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = { command_flush_or_trigger_playback_stream },
-    
-    [PA_COMMAND_CORK_RECORD_STREAM] = { command_cork_record_stream },
-    [PA_COMMAND_FLUSH_RECORD_STREAM] = { command_flush_record_stream },
-    
-    [PA_COMMAND_SET_DEFAULT_SINK] = { command_set_default_sink_or_source },
-    [PA_COMMAND_SET_DEFAULT_SOURCE] = { command_set_default_sink_or_source },
-    [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = { command_set_stream_name }, 
-    [PA_COMMAND_SET_RECORD_STREAM_NAME] = { command_set_stream_name },
-    [PA_COMMAND_KILL_CLIENT] = { command_kill },
-    [PA_COMMAND_KILL_SINK_INPUT] = { command_kill },
-    [PA_COMMAND_KILL_SOURCE_OUTPUT] = { command_kill },
-    [PA_COMMAND_LOAD_MODULE] = { command_load_module },
-    [PA_COMMAND_UNLOAD_MODULE] = { command_unload_module },
-    [PA_COMMAND_GET_AUTOLOAD_INFO] = { command_get_autoload_info },
-    [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = { command_get_autoload_info_list },
-    [PA_COMMAND_ADD_AUTOLOAD] = { command_add_autoload },
-    [PA_COMMAND_REMOVE_AUTOLOAD] = { command_remove_autoload },
-
-};
-
-/* structure management */
-
-static struct upload_stream* upload_stream_new(struct connection *c, const struct pa_sample_spec *ss, const char *name, size_t length) {
-    struct upload_stream *s;
-    assert(c && ss && name && length);
-    
-    s = pa_xmalloc(sizeof(struct upload_stream));
-    s->type = UPLOAD_STREAM;
-    s->connection = c;
-    s->sample_spec = *ss;
-    s->name = pa_xstrdup(name);
-
-    s->memchunk.memblock = NULL;
-    s->memchunk.index = 0;
-    s->memchunk.length = 0;
-
-    s->length = length;
-    
-    pa_idxset_put(c->output_streams, s, &s->index);
-    return s;
-}
-
-static void upload_stream_free(struct upload_stream *o) {
-    assert(o && o->connection);
-
-    pa_idxset_remove_by_data(o->connection->output_streams, o, NULL);
-
-    pa_xfree(o->name);
-    
-    if (o->memchunk.memblock)
-        pa_memblock_unref(o->memchunk.memblock);
-    
-    pa_xfree(o);
-}
-
-static struct record_stream* record_stream_new(struct connection *c, struct pa_source *source, const 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, PA_TYPEID_NATIVE, name, ss, -1)))
-        return NULL;
-
-    s = pa_xmalloc(sizeof(struct record_stream));
-    s->connection = c;
-    s->source_output = source_output;
-    s->source_output->push = source_output_push_cb;
-    s->source_output->kill = source_output_kill_cb;
-    s->source_output->get_latency = source_output_get_latency_cb;
-    s->source_output->userdata = s;
-    s->source_output->owner = c->protocol->module;
-    s->source_output->client = c->client;
-
-    s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_frame_size(ss), 0, 0, c->protocol->core->memblock_stat);
-    assert(s->memblockq);
-
-    s->fragment_size = (fragment_size/base)*base;
-    if (!s->fragment_size)
-        s->fragment_size = base;
-
-    pa_idxset_put(c->record_streams, s, &s->index);
-    return s;
-}
-
-static void record_stream_free(struct record_stream* r) {
-    assert(r && r->connection);
-
-    pa_idxset_remove_by_data(r->connection->record_streams, r, NULL);
-    pa_source_output_disconnect(r->source_output);
-    pa_source_output_unref(r->source_output);
-    pa_memblockq_free(r->memblockq);
-    pa_xfree(r);
-}
-
-static struct playback_stream* playback_stream_new(struct connection *c, struct pa_sink *sink, const struct pa_sample_spec *ss, const char *name,
-                                                   size_t maxlength,
-                                                   size_t tlength,
-                                                   size_t prebuf,
-                                                   size_t minreq,
-                                                   pa_volume_t volume) {
-    struct playback_stream *s;
-    struct pa_sink_input *sink_input;
-    assert(c && sink && ss && name && maxlength);
-
-    if (!(sink_input = pa_sink_input_new(sink, PA_TYPEID_NATIVE, name, ss, 0, -1)))
-        return NULL;
-    
-    s = pa_xmalloc(sizeof(struct playback_stream));
-    s->type = PLAYBACK_STREAM;
-    s->connection = c;
-    s->sink_input = sink_input;
-    
-    s->sink_input->peek = sink_input_peek_cb;
-    s->sink_input->drop = sink_input_drop_cb;
-    s->sink_input->kill = sink_input_kill_cb;
-    s->sink_input->get_latency = sink_input_get_latency_cb;
-    s->sink_input->userdata = s;
-    s->sink_input->owner = c->protocol->module;
-    s->sink_input->client = c->client;
-    
-    s->memblockq = pa_memblockq_new(maxlength, tlength, pa_frame_size(ss), prebuf, minreq, c->protocol->core->memblock_stat);
-    assert(s->memblockq);
-
-    s->requested_bytes = 0;
-    s->drain_request = 0;
-
-    s->sink_input->volume = volume;
-    
-    pa_idxset_put(c->output_streams, s, &s->index);
-    return s;
-}
-
-static void playback_stream_free(struct playback_stream* p) {
-    assert(p && p->connection);
-
-    if (p->drain_request)
-        pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERROR_NOENTITY);
-
-    pa_idxset_remove_by_data(p->connection->output_streams, p, NULL);
-    pa_sink_input_disconnect(p->sink_input);
-    pa_sink_input_unref(p->sink_input);
-    pa_memblockq_free(p->memblockq);
-    pa_xfree(p);
-}
-
-static void connection_free(struct connection *c) {
-    struct record_stream *r;
-    struct output_stream *o;
-    assert(c && c->protocol);
-
-    pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
-    while ((r = pa_idxset_first(c->record_streams, NULL)))
-        record_stream_free(r);
-    pa_idxset_free(c->record_streams, NULL, NULL);
-
-    while ((o = pa_idxset_first(c->output_streams, NULL)))
-        if (o->type == PLAYBACK_STREAM)
-            playback_stream_free((struct playback_stream*) o);
-        else
-            upload_stream_free((struct upload_stream*) o);
-    pa_idxset_free(c->output_streams, NULL, NULL);
-
-    pa_pdispatch_unref(c->pdispatch);
-    pa_pstream_close(c->pstream);
-    pa_pstream_unref(c->pstream);
-    pa_client_free(c->client);
-
-    if (c->subscription)
-        pa_subscription_free(c->subscription);
-
-    if (c->auth_timeout_event)
-        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
-    
-    pa_xfree(c);
-}
-
-static void request_bytes(struct playback_stream *s) {
-    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);
-
-/*     pa_log(__FILE__": Requesting %u bytes\n", l); */
-}
-
-static void send_memblock(struct connection *c) {
-    uint32_t start;
-    struct record_stream *r;
-
-    start = PA_IDXSET_INVALID;
-    for (;;) {
-        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) {
-            struct pa_memchunk schunk = chunk;
-            
-            if (schunk.length > r->fragment_size)
-                schunk.length = r->fragment_size;
-
-            pa_pstream_send_memblock(c->pstream, r->index, 0, &schunk);
-            pa_memblockq_drop(r->memblockq, &chunk, schunk.length);
-            pa_memblock_unref(schunk.memblock);
-            
-            return;
-        }
-    }
-}
-
-static void send_playback_stream_killed(struct playback_stream *p) {
-    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, const struct pa_memchunk *chunk, size_t length) {
-    struct playback_stream *s;
-    assert(i && i->userdata && length);
-    s = i->userdata;
-
-    pa_memblockq_drop(s->memblockq, chunk, length);
-    request_bytes(s);
-
-    if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) {
-        pa_pstream_send_simple_ack(s->connection->pstream, s->drain_tag);
-        s->drain_request = 0;
-    }
-
-/*     pa_log(__FILE__": after_drop: %u\n", pa_memblockq_get_length(s->memblockq)); */
-}
-
-static void sink_input_kill_cb(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 pa_usec_t sink_input_get_latency_cb(struct pa_sink_input *i) {
-    struct playback_stream *s;
-    assert(i && i->userdata);
-    s = i->userdata;
-
-    /*pa_log(__FILE__": get_latency: %u\n", pa_memblockq_get_length(s->memblockq));*/
-    
-    return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
-}
-
-/*** source_output callbacks ***/
-
-static void source_output_push_cb(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_align(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);
-}
-
-static pa_usec_t source_output_get_latency_cb(struct pa_source_output *o) {
-    struct record_stream *s;
-    assert(o && o->userdata);
-    s = o->userdata;
-
-    /*pa_log(__FILE__": get_latency: %u\n", pa_memblockq_get_length(s->memblockq));*/
-    
-    return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
-}
-
-/*** pdispatch callbacks ***/
-
-static void protocol_error(struct connection *c) {
-    pa_log(__FILE__": protocol error, kicking client\n");
-    connection_free(c);
-}
-
-static void command_create_playback_stream(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, *sink_name;
-    struct pa_sample_spec ss;
-    struct pa_tagstruct *reply;
-    struct pa_sink *sink;
-    pa_volume_t volume;
-    int corked;
-    assert(c && t && c->protocol && c->protocol->core);
-    
-    if (pa_tagstruct_gets(t, &name) < 0 || !name ||
-        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
-        pa_tagstruct_getu32(t, &sink_index) < 0 ||
-        pa_tagstruct_gets(t, &sink_name) < 0 ||
-        pa_tagstruct_getu32(t, &maxlength) < 0 ||
-        pa_tagstruct_get_boolean(t, &corked) < 0 ||
-        pa_tagstruct_getu32(t, &tlength) < 0 ||
-        pa_tagstruct_getu32(t, &prebuf) < 0 ||
-        pa_tagstruct_getu32(t, &minreq) < 0 ||
-        pa_tagstruct_getu32(t, &volume) < 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_idxset_get_by_index(c->protocol->core->sinks, sink_index);
-    else
-        sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1);
-
-    if (!sink) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-    
-    if (!(s = playback_stream_new(c, sink, &ss, name, maxlength, tlength, prebuf, minreq, volume))) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
-        return;
-    }
-
-    pa_sink_input_cork(s->sink_input, corked);
-    
-    reply = pa_tagstruct_new(NULL, 0);
-    assert(reply);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-    pa_tagstruct_putu32(reply, s->index);
-    assert(s->sink_input);
-    pa_tagstruct_putu32(reply, s->sink_input->index);
-    pa_tagstruct_putu32(reply, s->requested_bytes = pa_memblockq_missing(s->memblockq));
-    pa_pstream_send_tagstruct(c->pstream, reply);
-    request_bytes(s);
-}
-
-static void command_delete_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t channel;
-    assert(c && t);
-    
-    if (pa_tagstruct_getu32(t, &channel) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (command == PA_COMMAND_DELETE_PLAYBACK_STREAM) {
-        struct playback_stream *s;
-        if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != PLAYBACK_STREAM)) {
-            pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
-            return;
-        }
-
-        playback_stream_free(s);
-    } else if (command == PA_COMMAND_DELETE_RECORD_STREAM) {
-        struct record_stream *s;
-        if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
-            pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
-            return;
-        }
-
-        record_stream_free(s);
-    } else {
-        struct upload_stream *s;
-        assert(command == PA_COMMAND_DELETE_UPLOAD_STREAM);
-        if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) {
-            pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
-            return;
-        }
-
-        upload_stream_free(s);
-    }
-            
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_create_record_stream(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, *source_name;
-    struct pa_sample_spec ss;
-    struct pa_tagstruct *reply;
-    struct pa_source *source;
-    int corked;
-    assert(c && t && c->protocol && c->protocol->core);
-    
-    if (pa_tagstruct_gets(t, &name) < 0 || !name ||
-        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
-        pa_tagstruct_getu32(t, &source_index) < 0 ||
-        pa_tagstruct_gets(t, &source_name) < 0 ||
-        pa_tagstruct_getu32(t, &maxlength) < 0 ||
-        pa_tagstruct_get_boolean(t, &corked) < 0 ||
-        pa_tagstruct_getu32(t, &fragment_size) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (source_index != (uint32_t) -1)
-        source = pa_idxset_get_by_index(c->protocol->core->sources, source_index);
-    else
-        source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1);
-
-    if (!source) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-    
-    if (!(s = record_stream_new(c, source, &ss, name, maxlength, fragment_size))) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
-        return;
-    }
-
-    pa_source_output_cork(s->source_output, corked);
-    
-    reply = pa_tagstruct_new(NULL, 0);
-    assert(reply);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-    pa_tagstruct_putu32(reply, s->index);
-    assert(s->source_output);
-    pa_tagstruct_putu32(reply, s->source_output->index);
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_exit(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 (!c->authorized) {
-        if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) {
-            pa_log(__FILE__": Denied access to client with invalid authorization key.\n");
-            pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-            return;
-        }
-        
-        c->authorized = 1;
-        if (c->auth_timeout_event) {
-            c->protocol->core->mainloop->time_free(c->auth_timeout_event);
-            c->auth_timeout_event = NULL;
-        }
-    }
-    
-    pa_pstream_send_simple_ack(c->pstream, tag);
-    return;
-}
-
-static void command_set_client_name(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 || !name ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    pa_client_set_name(c->client, name);
-    pa_pstream_send_simple_ack(c->pstream, tag);
-    return;
-}
-
-static void command_lookup(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 || !name ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (command == PA_COMMAND_LOOKUP_SINK) {
-        struct pa_sink *sink;
-        if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1)))
-            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, 1)))
-            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->output_streams, index)) || s->type != PLAYBACK_STREAM) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    s->drain_request = 0;
-
-    pa_memblockq_prebuf_disable(s->memblockq);
-    
-    if (!pa_memblockq_is_readable(s->memblockq)) {
-/*         pa_log("immediate drain: %u\n", pa_memblockq_get_length(s->memblockq)); */
-        pa_pstream_send_simple_ack(c->pstream, tag);
-    } else {
-/*         pa_log("slow drain triggered\n"); */
-        s->drain_request = 1;
-        s->drain_tag = tag;
-
-        pa_sink_notify(s->sink_input->sink);
-    }
-} 
-
-static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    struct pa_tagstruct *reply;
-    assert(c && t);
-
-    if (!pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    reply = pa_tagstruct_new(NULL, 0);
-    assert(reply);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-    pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->total);
-    pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->total_size);
-    pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->allocated);
-    pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->allocated_size);
-    pa_tagstruct_putu32(reply, pa_scache_total_size(c->protocol->core));
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    struct pa_tagstruct *reply;
-    struct playback_stream *s;
-    struct timeval tv, now;
-    uint64_t counter;
-    uint32_t index;
-    assert(c && t);
-    
-    if (pa_tagstruct_getu32(t, &index) < 0 ||
-        pa_tagstruct_get_timeval(t, &tv) < 0 ||
-        pa_tagstruct_getu64(t, &counter) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (!(s = pa_idxset_get_by_index(c->output_streams, index)) || s->type != PLAYBACK_STREAM) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    reply = pa_tagstruct_new(NULL, 0);
-    assert(reply);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-    pa_tagstruct_put_usec(reply, pa_sink_input_get_latency(s->sink_input));
-    pa_tagstruct_put_usec(reply, pa_sink_get_latency(s->sink_input->sink));
-    pa_tagstruct_put_usec(reply, 0);
-    pa_tagstruct_put_boolean(reply, pa_memblockq_is_readable(s->memblockq));
-    pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq));
-    pa_tagstruct_put_timeval(reply, &tv);
-    gettimeofday(&now, NULL);
-    pa_tagstruct_put_timeval(reply, &now);
-    pa_tagstruct_putu64(reply, counter);
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_get_record_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    struct pa_tagstruct *reply;
-    struct record_stream *s;
-    struct timeval tv, now;
-    uint64_t counter;
-    uint32_t index;
-    assert(c && t);
-
-    if (pa_tagstruct_getu32(t, &index) < 0 ||
-        pa_tagstruct_get_timeval(t, &tv) < 0 ||
-        pa_tagstruct_getu64(t, &counter) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (!(s = pa_idxset_get_by_index(c->record_streams, index))) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    reply = pa_tagstruct_new(NULL, 0);
-    assert(reply);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-    pa_tagstruct_put_usec(reply, pa_source_output_get_latency(s->source_output));
-    pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
-    pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
-    pa_tagstruct_put_boolean(reply, 0);
-    pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq));
-    pa_tagstruct_put_timeval(reply, &tv);
-    gettimeofday(&now, NULL);
-    pa_tagstruct_put_timeval(reply, &now);
-    pa_tagstruct_putu64(reply, counter);
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-
-static void command_create_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    struct upload_stream *s;
-    size_t length;
-    const char *name;
-    struct pa_sample_spec ss;
-    struct pa_tagstruct *reply;
-    assert(c && t && c->protocol && c->protocol->core);
-    
-    if (pa_tagstruct_gets(t, &name) < 0 || !name ||
-        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
-        pa_tagstruct_getu32(t, &length) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if ((length % pa_frame_size(&ss)) != 0 || length <= 0 || !*name) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
-        return;
-    }
-    
-    if (!(s = upload_stream_new(c, &ss, name, length))) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID);
-        return;
-    }
-    
-    reply = pa_tagstruct_new(NULL, 0);
-    assert(reply);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-    pa_tagstruct_putu32(reply, s->index);
-    pa_tagstruct_putu32(reply, length);
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_finish_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t channel;
-    struct upload_stream *s;
-    uint32_t index;
-    assert(c && t);
-    
-    if (pa_tagstruct_getu32(t, &channel) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
-        return;
-    }
-
-    pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->memchunk, &index);
-    pa_pstream_send_simple_ack(c->pstream, tag);
-    upload_stream_free(s);
-}
-
-static void command_play_sample(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t sink_index, volume;
-    struct pa_sink *sink;
-    const char *name, *sink_name;
-    assert(c && t);
-
-    if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
-        pa_tagstruct_gets(t, &sink_name) < 0 ||
-        pa_tagstruct_getu32(t, &volume) < 0 ||
-        pa_tagstruct_gets(t, &name) < 0 || !name || 
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (sink_index != (uint32_t) -1)
-        sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
-    else
-        sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1);
-
-    if (!sink) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    if (pa_scache_play_item(c->protocol->core, name, sink, volume) < 0) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_remove_sample(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 || !name || 
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (pa_scache_remove_item(c->protocol->core, name) < 0) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void sink_fill_tagstruct(struct pa_tagstruct *t, struct pa_sink *sink) {
-    assert(t && sink);
-    pa_tagstruct_putu32(t, sink->index);
-    pa_tagstruct_puts(t, sink->name);
-    pa_tagstruct_puts(t, sink->description);
-    pa_tagstruct_put_sample_spec(t, &sink->sample_spec);
-    pa_tagstruct_putu32(t, sink->owner ? sink->owner->index : (uint32_t) -1);
-    pa_tagstruct_putu32(t, sink->volume);
-    pa_tagstruct_putu32(t, sink->monitor_source->index);
-    pa_tagstruct_puts(t, sink->monitor_source->name);
-    pa_tagstruct_put_usec(t, pa_sink_get_latency(sink));
-    pa_tagstruct_putu32(t, sink->typeid);
-}
-
-static void source_fill_tagstruct(struct pa_tagstruct *t, struct pa_source *source) {
-    assert(t && source);
-    pa_tagstruct_putu32(t, source->index);
-    pa_tagstruct_puts(t, source->name);
-    pa_tagstruct_puts(t, source->description);
-    pa_tagstruct_put_sample_spec(t, &source->sample_spec);
-    pa_tagstruct_putu32(t, source->owner ? source->owner->index : (uint32_t) -1);
-    pa_tagstruct_putu32(t, source->monitor_of ? source->monitor_of->index : (uint32_t) -1);
-    pa_tagstruct_puts(t, source->monitor_of ? source->monitor_of->name : NULL);
-    pa_tagstruct_put_usec(t, pa_source_get_latency(source));
-    pa_tagstruct_putu32(t, source->typeid);
-}
-
-static void client_fill_tagstruct(struct pa_tagstruct *t, struct pa_client *client) {
-    assert(t && client);
-    pa_tagstruct_putu32(t, client->index);
-    pa_tagstruct_puts(t, client->name);
-    pa_tagstruct_putu32(t, client->owner ? client->owner->index : (uint32_t) -1);
-    pa_tagstruct_putu32(t, client->typeid);
-}
-
-static void module_fill_tagstruct(struct pa_tagstruct *t, struct pa_module *module) {
-    assert(t && module);
-    pa_tagstruct_putu32(t, module->index);
-    pa_tagstruct_puts(t, module->name);
-    pa_tagstruct_puts(t, module->argument);
-    pa_tagstruct_putu32(t, module->n_used);
-    pa_tagstruct_put_boolean(t, module->auto_unload);
-}
-
-static void sink_input_fill_tagstruct(struct pa_tagstruct *t, struct pa_sink_input *s) {
-    assert(t && s);
-    pa_tagstruct_putu32(t, s->index);
-    pa_tagstruct_puts(t, s->name);
-    pa_tagstruct_putu32(t, s->owner ? s->owner->index : (uint32_t) -1);
-    pa_tagstruct_putu32(t, s->client ? s->client->index : (uint32_t) -1);
-    pa_tagstruct_putu32(t, s->sink->index);
-    pa_tagstruct_put_sample_spec(t, &s->sample_spec);
-    pa_tagstruct_putu32(t, s->volume);
-    pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s));
-    pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink));
-    pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
-    pa_tagstruct_putu32(t, s->typeid);
-}
-
-static void source_output_fill_tagstruct(struct pa_tagstruct *t, struct pa_source_output *s) {
-    assert(t && s);
-    pa_tagstruct_putu32(t, s->index);
-    pa_tagstruct_puts(t, s->name);
-    pa_tagstruct_putu32(t, s->owner ? s->owner->index : (uint32_t) -1);
-    pa_tagstruct_putu32(t, s->client ? s->client->index : (uint32_t) -1);
-    pa_tagstruct_putu32(t, s->source->index);
-    pa_tagstruct_put_sample_spec(t, &s->sample_spec);
-    pa_tagstruct_put_usec(t, pa_source_output_get_latency(s));
-    pa_tagstruct_put_usec(t, pa_source_get_latency(s->source));
-    pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s)));
-    pa_tagstruct_putu32(t, s->typeid);
-}
-
-static void scache_fill_tagstruct(struct pa_tagstruct *t, struct pa_scache_entry *e) {
-    assert(t && e);
-    pa_tagstruct_putu32(t, e->index);
-    pa_tagstruct_puts(t, e->name);
-    pa_tagstruct_putu32(t, e->volume);
-    pa_tagstruct_put_usec(t, pa_bytes_to_usec(e->memchunk.length, &e->sample_spec));
-    pa_tagstruct_put_sample_spec(t, &e->sample_spec);
-    pa_tagstruct_putu32(t, e->memchunk.length);
-    pa_tagstruct_put_boolean(t, e->lazy);
-    pa_tagstruct_puts(t, e->filename);
-}
-
-static void command_get_info(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t index;
-    struct pa_sink *sink = NULL;
-    struct pa_source *source = NULL;
-    struct pa_client *client = NULL;
-    struct pa_module *module = NULL;
-    struct pa_sink_input *si = NULL;
-    struct pa_source_output *so = NULL;
-    struct pa_scache_entry *sce = NULL;
-    const char *name;
-    struct pa_tagstruct *reply;
-    assert(c && t);
-
-    
-    if (pa_tagstruct_getu32(t, &index) < 0 ||
-        (command != PA_COMMAND_GET_CLIENT_INFO &&
-         command != PA_COMMAND_GET_MODULE_INFO &&
-         command != PA_COMMAND_GET_SINK_INPUT_INFO &&
-         command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO &&
-         pa_tagstruct_gets(t, &name) < 0) ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (command == PA_COMMAND_GET_SINK_INFO) {
-        if (index != (uint32_t) -1)
-            sink = pa_idxset_get_by_index(c->protocol->core->sinks, index);
-        else
-            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
-    } else if (command == PA_COMMAND_GET_SOURCE_INFO) {
-        if (index != (uint32_t) -1)
-            source = pa_idxset_get_by_index(c->protocol->core->sources, index);
-        else
-            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
-    } else if (command == PA_COMMAND_GET_CLIENT_INFO)
-        client = pa_idxset_get_by_index(c->protocol->core->clients, index);
-    else if (command == PA_COMMAND_GET_MODULE_INFO) 
-        module = pa_idxset_get_by_index(c->protocol->core->modules, index);
-    else if (command == PA_COMMAND_GET_SINK_INPUT_INFO)
-        si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, index);
-    else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO)
-        so = pa_idxset_get_by_index(c->protocol->core->source_outputs, index);
-    else {
-        assert(command == PA_COMMAND_GET_SAMPLE_INFO);
-        if (index != (uint32_t) -1)
-            sce = pa_idxset_get_by_index(c->protocol->core->scache, index);
-        else
-            sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE, 0);
-    }
-            
-    if (!sink && !source && !client && !module && !si && !so && !sce) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    reply = pa_tagstruct_new(NULL, 0);
-    assert(reply);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag); 
-    if (sink)
-        sink_fill_tagstruct(reply, sink);
-    else if (source)
-        source_fill_tagstruct(reply, source);
-    else if (client)
-        client_fill_tagstruct(reply, client);
-    else if (module)
-        module_fill_tagstruct(reply, module);
-    else if (si)
-        sink_input_fill_tagstruct(reply, si);
-    else if (so)
-        source_output_fill_tagstruct(reply, so);
-    else
-        scache_fill_tagstruct(reply, sce);
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_get_info_list(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    struct pa_idxset *i;
-    uint32_t index;
-    void *p;
-    struct pa_tagstruct *reply;
-    assert(c && t);
-
-    if (!pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    reply = pa_tagstruct_new(NULL, 0);
-    assert(reply);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-
-    if (command == PA_COMMAND_GET_SINK_INFO_LIST)
-        i = c->protocol->core->sinks;
-    else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
-        i = c->protocol->core->sources;
-    else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
-        i = c->protocol->core->clients;
-    else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
-        i = c->protocol->core->modules;
-    else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
-        i = c->protocol->core->sink_inputs;
-    else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
-        i = c->protocol->core->source_outputs;
-    else {
-        assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
-        i = c->protocol->core->scache;
-    }
-
-    if (i) {
-        for (p = pa_idxset_first(i, &index); p; p = pa_idxset_next(i, &index)) {
-            if (command == PA_COMMAND_GET_SINK_INFO_LIST)
-                sink_fill_tagstruct(reply, p);
-            else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
-                source_fill_tagstruct(reply, p);
-            else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
-                client_fill_tagstruct(reply, p);
-            else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
-                module_fill_tagstruct(reply, p);
-            else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
-                sink_input_fill_tagstruct(reply, p);
-            else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST) 
-                source_output_fill_tagstruct(reply, p);
-            else {
-                assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
-                scache_fill_tagstruct(reply, p);
-            }
-        }
-    }
-    
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_get_server_info(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    struct pa_tagstruct *reply;
-    char txt[256];
-    const char *n;
-    assert(c && t);
-
-    if (!pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    reply = pa_tagstruct_new(NULL, 0);
-    assert(reply);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-    pa_tagstruct_puts(reply, PACKAGE_NAME);
-    pa_tagstruct_puts(reply, PACKAGE_VERSION);
-    pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
-    pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt)));
-    pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec);
-
-    n = pa_namereg_get_default_sink_name(c->protocol->core);
-    pa_tagstruct_puts(reply, n);
-    n = pa_namereg_get_default_source_name(c->protocol->core);
-    pa_tagstruct_puts(reply, n);
-
-    pa_tagstruct_putu32(reply, c->protocol->core->cookie);
-    
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void subscription_cb(struct pa_core *core, enum pa_subscription_event_type e, uint32_t index, void *userdata) {
-    struct pa_tagstruct *t;
-    struct connection *c = userdata;
-    assert(c && core);
-
-    t = pa_tagstruct_new(NULL, 0);
-    assert(t);
-    pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
-    pa_tagstruct_putu32(t, (uint32_t) -1);
-    pa_tagstruct_putu32(t, e);
-    pa_tagstruct_putu32(t, index);
-    pa_pstream_send_tagstruct(c->pstream, t);
-}
-
-static void command_subscribe(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    enum pa_subscription_mask m;
-    assert(c && t);
-
-    if (pa_tagstruct_getu32(t, &m) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (c->subscription)
-        pa_subscription_free(c->subscription);
-
-    if (m != 0) {
-        c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c);
-        assert(c->subscription);
-    } else
-        c->subscription = NULL;
-
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_set_volume(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t index, volume;
-    struct pa_sink *sink = NULL;
-    struct pa_sink_input *si = NULL;
-    const char *name = NULL;
-    assert(c && t);
-
-    if (pa_tagstruct_getu32(t, &index) < 0 ||
-        (command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
-        pa_tagstruct_getu32(t, &volume) ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (command == PA_COMMAND_SET_SINK_VOLUME) {
-        if (index != (uint32_t) -1)
-            sink = pa_idxset_get_by_index(c->protocol->core->sinks, index);
-        else
-            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
-    }  else {
-        assert(command == PA_COMMAND_SET_SINK_INPUT_VOLUME);
-        si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, index);
-    }
-
-    if (!si && !sink) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    if (sink)
-        pa_sink_set_volume(sink, volume);
-    else if (si)
-        pa_sink_input_set_volume(si, volume);
-
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_cork_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t index;
-    int b;
-    struct playback_stream *s;
-    assert(c && t);
-
-    if (pa_tagstruct_getu32(t, &index) < 0 ||
-        pa_tagstruct_get_boolean(t, &b) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (!(s = pa_idxset_get_by_index(c->output_streams, index)) || s->type != PLAYBACK_STREAM) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    pa_sink_input_cork(s->sink_input, b);
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_flush_or_trigger_playback_stream(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->output_streams, index)) || s->type != PLAYBACK_STREAM) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    if (command == PA_COMMAND_PREBUF_PLAYBACK_STREAM)
-        pa_memblockq_prebuf_reenable(s->memblockq);
-    else if (command == PA_COMMAND_TRIGGER_PLAYBACK_STREAM)
-        pa_memblockq_prebuf_disable(s->memblockq);
-    else {
-        assert(command == PA_COMMAND_FLUSH_PLAYBACK_STREAM);
-        pa_memblockq_flush(s->memblockq);
-        /*pa_log(__FILE__": flush: %u\n", pa_memblockq_get_length(s->memblockq));*/
-    }
-
-    pa_sink_notify(s->sink_input->sink);
-    pa_pstream_send_simple_ack(c->pstream, tag);
-    request_bytes(s);
-}
-
-static void command_cork_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t index;
-    struct record_stream *s;
-    int b;
-    assert(c && t);
-
-    if (pa_tagstruct_getu32(t, &index) < 0 ||
-        pa_tagstruct_get_boolean(t, &b) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (!(s = pa_idxset_get_by_index(c->record_streams, index))) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    pa_source_output_cork(s->source_output, b);
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_flush_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t index;
-    struct record_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->record_streams, index))) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    pa_memblockq_flush(s->memblockq);
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_set_default_sink_or_source(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t index;
-    const char *s;
-    assert(c && t);
-
-    if (pa_tagstruct_getu32(t, &index) < 0 ||
-        pa_tagstruct_gets(t, &s) < 0 || !s ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK);
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_set_stream_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t index;
-    const char *name;
-    assert(c && t);
-
-    if (pa_tagstruct_getu32(t, &index) < 0 ||
-        pa_tagstruct_gets(t, &name) < 0 || !name || 
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) {
-        struct playback_stream *s;
-        
-        if (!(s = pa_idxset_get_by_index(c->output_streams, index)) || s->type != PLAYBACK_STREAM) {
-            pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-            return;
-        }
-
-        pa_sink_input_set_name(s->sink_input, name);
-        
-    } else {
-        struct record_stream *s;
-        
-        if (!(s = pa_idxset_get_by_index(c->record_streams, index))) {
-            pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-            return;
-        }
-
-        pa_source_output_set_name(s->source_output, name);
-    }
-
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_kill(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t index;
-    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 (command == PA_COMMAND_KILL_CLIENT) {
-        struct pa_client *client;
-        
-        if (!(client = pa_idxset_get_by_index(c->protocol->core->clients, index))) {
-            pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-            return;
-        }
-
-        pa_client_kill(client);
-    } else if (command == PA_COMMAND_KILL_SINK_INPUT) {
-        struct pa_sink_input *s;
-        
-        if (!(s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, index))) {
-            pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-            return;
-        }
-
-        pa_sink_input_kill(s);
-    } else {
-        struct pa_source_output *s;
-
-        assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);
-        
-        if (!(s = pa_idxset_get_by_index(c->protocol->core->source_outputs, index))) {
-            pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-            return;
-        }
-
-        pa_source_output_kill(s);
-    }
-
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_load_module(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    struct pa_module *m;
-    const char *name, *argument;
-    struct pa_tagstruct *reply;
-    assert(c && t);
-
-    if (pa_tagstruct_gets(t, &name) < 0 || !name ||
-        pa_tagstruct_gets(t, &argument) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (!(m = pa_module_load(c->protocol->core, name, argument))) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_INITFAILED);
-        return;
-    }
-
-    reply = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-    pa_tagstruct_putu32(reply, m->index);
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_unload_module(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    uint32_t index;
-    struct pa_module *m;
-    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 (!(m = pa_idxset_get_by_index(c->protocol->core->modules, index))) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    pa_module_unload_request(m);
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void command_add_autoload(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    const char *name, *module, *argument;
-    uint32_t type;
-    uint32_t index;
-    struct pa_tagstruct *reply;
-    assert(c && t);
-
-    if (pa_tagstruct_gets(t, &name) < 0 || !name ||
-        pa_tagstruct_getu32(t, &type) < 0 || type > 1 ||
-        pa_tagstruct_gets(t, &module) < 0 || !module ||
-        pa_tagstruct_gets(t, &argument) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (pa_autoload_add(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, module, argument, &index) < 0) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST);
-        return;
-    }
-
-    reply = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-    pa_tagstruct_putu32(reply, index);
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_remove_autoload(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    const char *name = NULL;
-    uint32_t type, index = PA_IDXSET_INVALID;
-    int r;
-    assert(c && t);
-
-    if ((pa_tagstruct_getu32(t, &index) < 0 &&
-        (pa_tagstruct_gets(t, &name) < 0 ||
-         pa_tagstruct_getu32(t, &type) < 0)) ||
-        (!name && index == PA_IDXSET_INVALID) ||
-        (name && type > 1) ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    if (name) 
-        r = pa_autoload_remove_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
-    else
-        r = pa_autoload_remove_by_index(c->protocol->core, index);
-
-    if (r < 0) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void autoload_fill_tagstruct(struct pa_tagstruct *t, const struct pa_autoload_entry *e) {
-    assert(t && e);
-
-    pa_tagstruct_putu32(t, e->index);
-    pa_tagstruct_puts(t, e->name);
-    pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0 : 1);
-    pa_tagstruct_puts(t, e->module);
-    pa_tagstruct_puts(t, e->argument);
-}
-
-static void command_get_autoload_info(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    const struct pa_autoload_entry *a = NULL;
-    uint32_t type, index;
-    const char *name;
-    struct pa_tagstruct *reply;
-    assert(c && t);
-
-    if ((pa_tagstruct_getu32(t, &index) < 0 &&
-        (pa_tagstruct_gets(t, &name) < 0 ||
-         pa_tagstruct_getu32(t, &type) < 0)) ||
-        (!name && index == PA_IDXSET_INVALID) ||
-        (name && type > 1) ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-
-    if (name)
-        a = pa_autoload_get_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
-    else
-        a = pa_autoload_get_by_index(c->protocol->core, index);
-
-    if (!a) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY);
-        return;
-    }
-
-    reply = pa_tagstruct_new(NULL, 0);
-    assert(reply);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-    autoload_fill_tagstruct(reply, a);
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_get_autoload_info_list(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
-    struct connection *c = userdata;
-    struct pa_tagstruct *reply;
-    assert(c && t);
-
-    if (!pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-    
-    if (!c->authorized) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS);
-        return;
-    }
-
-    reply = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
-    pa_tagstruct_putu32(reply, tag);
-
-    if (c->protocol->core->autoload_hashmap) {
-        struct pa_autoload_entry *a;
-        void *state = NULL;
-
-        while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state, NULL)))
-            autoload_fill_tagstruct(reply, a);
-    }
-    
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-/*** pstream callbacks ***/
-
-static void pstream_packet_callback(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) {
-        pa_log(__FILE__": invalid packet.\n");
-        connection_free(c);
-    }
-}
-
-static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, uint32_t delta, const struct pa_memchunk *chunk, void *userdata) {
-    struct connection *c = userdata;
-    struct output_stream *stream;
-    assert(p && chunk && userdata);
-    
-    if (!(stream = pa_idxset_get_by_index(c->output_streams, channel))) {
-        pa_log(__FILE__": client sent block for invalid stream.\n");
-        connection_free(c);
-        return;
-    }
-
-    if (stream->type == PLAYBACK_STREAM) {
-        struct playback_stream *p = (struct playback_stream*) stream;
-        if (chunk->length >= p->requested_bytes)
-            p->requested_bytes = 0;
-        else
-            p->requested_bytes -= chunk->length;
-        
-        pa_memblockq_push_align(p->memblockq, chunk, delta);
-        assert(p->sink_input);
-/*         pa_log(__FILE__": after_recv: %u\n", pa_memblockq_get_length(p->memblockq)); */
-
-        pa_sink_notify(p->sink_input->sink);
-/*          pa_log(__FILE__": Recieved %u bytes.\n", chunk->length);  */
-
-    } else {
-        struct upload_stream *u = (struct upload_stream*) stream;
-        size_t l;
-        assert(u->type == UPLOAD_STREAM);
-
-        if (!u->memchunk.memblock) {
-            if (u->length == chunk->length) {
-                u->memchunk = *chunk;
-                pa_memblock_ref(u->memchunk.memblock);
-                u->length = 0;
-            } else {
-                u->memchunk.memblock = pa_memblock_new(u->length, c->protocol->core->memblock_stat);
-                u->memchunk.index = u->memchunk.length = 0;
-            }
-        }
-        
-        assert(u->memchunk.memblock);
-        
-        l = u->length; 
-        if (l > chunk->length)
-            l = chunk->length;
-
-        if (l > 0) {
-            memcpy((uint8_t*) u->memchunk.memblock->data + u->memchunk.index + u->memchunk.length,
-                   (uint8_t*) chunk->memblock->data+chunk->index, l);
-            u->memchunk.length += l;
-            u->length -= l;
-        }
-    }
-}
-
-static void pstream_die_callback(struct pa_pstream *p, void *userdata) {
-    struct connection *c = userdata;
-    assert(p && c);
-    connection_free(c);
-
-/*    pa_log(__FILE__": connection died.\n");*/
-}
-
-
-static void pstream_drain_callback(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 auth_timeout(struct pa_mainloop_api*m, struct pa_time_event *e, const struct timeval *tv, void *userdata) {
-    struct connection *c = userdata;
-    assert(m && tv && c && c->auth_timeout_event == e);
-
-    if (!c->authorized)
-        connection_free(c);
-}
-
-static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
-    struct pa_protocol_native *p = userdata;
-    struct connection *c;
-    assert(io && p);
-
-    if (pa_idxset_ncontents(p->connections)+1 > MAX_CONNECTIONS) {
-        pa_log_warn(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
-        pa_iochannel_free(io);
-        return;
-    }
-
-    c = pa_xmalloc(sizeof(struct connection));
-
-    c->authorized =!! p->public;
-
-    if (!c->authorized) {
-        struct timeval tv;
-        gettimeofday(&tv, NULL);
-        tv.tv_sec += AUTH_TIMEOUT;
-        c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
-    } else
-        c->auth_timeout_event = NULL;
-    
-    c->protocol = p;
-    assert(p->core);
-    c->client = pa_client_new(p->core, PA_TYPEID_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, p->core->memblock_stat);
-    assert(c->pstream);
-
-    pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
-    pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c);
-    pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
-    pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c);
-
-    c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX);
-    assert(c->pdispatch);
-
-    c->record_streams = pa_idxset_new(NULL, NULL);
-    c->output_streams = pa_idxset_new(NULL, NULL);
-    assert(c->record_streams && c->output_streams);
-
-    c->rrobin_index = PA_IDXSET_INVALID;
-    c->subscription = NULL;
-
-    pa_idxset_put(p->connections, c, NULL);
-}
-
-/*** module entry points ***/
-
-static int load_key(struct pa_protocol_native*p, const char*fn) {
-    assert(p);
-
-    p->auth_cookie_in_property = 0;
-    
-    if (!fn && pa_authkey_prop_get(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) {
-        pa_log_info(__FILE__": using already loaded auth cookie.\n");
-        pa_authkey_prop_ref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
-        p->auth_cookie_in_property = 1;
-        return 0;
-    }
-    
-    if (!fn)
-        fn = PA_NATIVE_COOKIE_FILE;
-
-    if (pa_authkey_load_auto(fn, p->auth_cookie, sizeof(p->auth_cookie)) < 0)
-        return -1;
-
-    pa_log_info(__FILE__": loading cookie from disk.\n");
-
-    if (pa_authkey_prop_put(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0)
-        p->auth_cookie_in_property = 1;
-        
-    return 0;
-}
-
-static struct pa_protocol_native* protocol_new_internal(struct pa_core *c, struct pa_module *m, struct pa_modargs *ma) {
-    struct pa_protocol_native *p;
-    int public = 0;
-    assert(c && ma);
-
-    if (pa_modargs_get_value_boolean(ma, "public", &public) < 0) {
-        pa_log(__FILE__": public= expects a boolean argument.\n");
-        return NULL;
-    }
-    
-    p = pa_xmalloc(sizeof(struct pa_protocol_native));
-    p->core = c;
-    p->module = m;
-    p->public = public;
-    p->server = NULL;
-
-    if (load_key(p, pa_modargs_get_value(ma, "cookie", NULL)) < 0) {
-        pa_xfree(p);
-        return NULL;
-    }
-
-    p->connections = pa_idxset_new(NULL, NULL);
-    assert(p->connections);
-
-    return p;
-}
-
-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) {
-    char t[256];
-    struct pa_protocol_native *p;
-
-    if (!(p = protocol_new_internal(core, m, ma)))
-        return NULL;
-    
-    p->server = server;
-    pa_socket_server_set_callback(p->server, on_connection, p);
-
-    if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
-        struct pa_strlist *l;
-        l = pa_property_get(core, PA_NATIVE_SERVER_PROPERTY_NAME);
-        l = pa_strlist_prepend(l, t);
-        pa_property_replace(core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
-    }
-    
-    return p;
-}
-
-void pa_protocol_native_free(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);
-
-    if (p->server) {
-        char t[256];
-        
-        if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
-            struct pa_strlist *l;
-            l = pa_property_get(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
-            l = pa_strlist_remove(l, t);
-
-            if (l)
-                pa_property_replace(p->core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
-            else
-                pa_property_remove(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
-        }
-        
-        pa_socket_server_unref(p->server);
-    }
-
-    if (p->auth_cookie_in_property)
-        pa_authkey_prop_unref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
-
-    pa_xfree(p);
-}
-
-struct pa_protocol_native* pa_protocol_native_new_iochannel(struct pa_core*core, struct pa_iochannel *io, struct pa_module *m, struct pa_modargs *ma) {
-    struct pa_protocol_native *p;
-
-    if (!(p = protocol_new_internal(core, m, ma)))
-        return NULL;
-
-    on_connection(NULL, io, p);
-    
-    return p;
-}
diff --git a/polyp/protocol-native.h b/polyp/protocol-native.h
deleted file mode 100644 (file)
index 57c29e3..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef fooprotocolnativehfoo
-#define fooprotocolnativehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "core.h"
-#include "socket-server.h"
-#include "module.h"
-#include "modargs.h"
-
-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);
-
-struct pa_protocol_native* pa_protocol_native_new_iochannel(struct pa_core*core, struct pa_iochannel *io, struct pa_module *m, struct pa_modargs *ma);
-
-#endif
diff --git a/polyp/protocol-simple.c b/polyp/protocol-simple.c
deleted file mode 100644 (file)
index 8d05a7f..0000000
+++ /dev/null
@@ -1,455 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-
-#include "sink-input.h"
-#include "source-output.h"
-#include "protocol-simple.h"
-#include "client.h"
-#include "sample-util.h"
-#include "namereg.h"
-#include "xmalloc.h"
-#include "log.h"
-
-/* Don't allow more than this many concurrent connections */
-#define MAX_CONNECTIONS 10
-
-#define PA_TYPEID_SIMPLE PA_TYPEID_MAKE('S', 'M', 'P', 'L')
-
-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;
-    struct pa_defer_event *defer_event;
-
-    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;
-    char *source_name, *sink_name;
-};
-
-#define PLAYBACK_BUFFER_SECONDS (.5)
-#define PLAYBACK_BUFFER_FRAGMENTS (10)
-#define RECORD_BUFFER_SECONDS (5)
-#define RECORD_BUFFER_FRAGMENTS (100)
-
-static void connection_free(struct connection *c) {
-    assert(c);
-
-    pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
-
-    if (c->playback.current_memblock)
-        pa_memblock_unref(c->playback.current_memblock);
-    if (c->sink_input) {
-        pa_sink_input_disconnect(c->sink_input);
-        pa_sink_input_unref(c->sink_input);
-    }
-    if (c->source_output) {
-        pa_source_output_disconnect(c->source_output);
-        pa_source_output_unref(c->source_output);
-    }
-    if (c->client)
-        pa_client_free(c->client);
-    if (c->io)
-        pa_iochannel_free(c->io);
-    if (c->input_memblockq)
-        pa_memblockq_free(c->input_memblockq);
-    if (c->output_memblockq)
-        pa_memblockq_free(c->output_memblockq);
-    if (c->defer_event)
-        c->protocol->core->mainloop->defer_free(c->defer_event);
-    pa_xfree(c);
-}
-
-static int do_read(struct connection *c) {
-    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, c->protocol->core->memblock_stat);
-        assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
-        c->playback.memblock_index = 0;
-    }
-    
-    if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
-        pa_log(__FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
-        return -1;
-    }
-
-    chunk.memblock = c->playback.current_memblock;
-    chunk.index = c->playback.memblock_index;
-    chunk.length = r;
-    assert(chunk.memblock);
-
-    c->playback.memblock_index += r;
-    
-    assert(c->input_memblockq);
-    pa_memblockq_push_align(c->input_memblockq, &chunk, 0);
-    assert(c->sink_input);
-    pa_sink_notify(c->sink_input->sink);
-    
-    return 0;
-}
-
-static int do_write(struct connection *c) {
-    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, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) {
-        pa_memblock_unref(chunk.memblock);
-        pa_log(__FILE__": write(): %s\n", strerror(errno));
-        return -1;
-    }
-    
-    pa_memblockq_drop(c->output_memblockq, &chunk, r);
-    pa_memblock_unref(chunk.memblock);
-    
-    return 0;
-}
-
-
-static void do_work(struct connection *c) {
-    assert(c);
-
-    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
-    c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
-
-    if (pa_iochannel_is_writable(c->io))
-        if (do_write(c) < 0)
-            goto fail;
-    
-    if (pa_iochannel_is_readable(c->io))
-        if (do_read(c) < 0)
-            goto fail;
-
-    if (pa_iochannel_is_hungup(c->io))
-        c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
-
-    return;
-
-fail:
-    connection_free(c);
-}
-
-/*** sink_input callbacks ***/
-
-static int sink_input_peek_cb(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, const struct pa_memchunk *chunk, size_t length) {
-    struct connection*c = i->userdata;
-    assert(i && c && length);
-
-    pa_memblockq_drop(c->input_memblockq, chunk, length);
-
-    /* do something */
-    assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
-    c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
-}
-
-static void sink_input_kill_cb(struct pa_sink_input *i) {
-    assert(i && i->userdata);
-    connection_free((struct connection *) i->userdata);
-}
-
-
-static pa_usec_t sink_input_get_latency_cb(struct pa_sink_input *i) {
-    struct connection*c = i->userdata;
-    assert(i && c);
-    return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
-}
-
-/*** source_output callbacks ***/
-
-static void source_output_push_cb(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->defer_enable);
-    c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
-}
-
-static void source_output_kill_cb(struct pa_source_output *o) {
-    assert(o && o->userdata);
-    connection_free((struct connection *) o->userdata);
-}
-
-static pa_usec_t source_output_get_latency_cb(struct pa_source_output *o) {
-    struct connection*c = o->userdata;
-    assert(o && c);
-    return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
-}
-
-/*** client callbacks ***/
-
-static void client_kill_cb(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 defer_callback(struct pa_mainloop_api*a, struct pa_defer_event *e, void *userdata) {
-    struct connection *c = userdata;
-    assert(a && c && c->defer_event == e);
-
-    do_work(c);
-}
-
-/*** socket_server callbacks ***/
-
-static void on_connection(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);
-
-    if (pa_idxset_ncontents(p->connections)+1 > MAX_CONNECTIONS) {
-        pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
-        pa_iochannel_free(io);
-        return;
-    }
-
-    c = pa_xmalloc(sizeof(struct connection));
-    c->io = io;
-    c->sink_input = NULL;
-    c->source_output = NULL;
-    c->defer_event = NULL;
-    c->input_memblockq = c->output_memblockq = NULL;
-    c->protocol = p;
-    c->playback.current_memblock = NULL;
-    c->playback.memblock_index = 0;
-    c->playback.fragment_size = 0;
-    
-    pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
-    c->client = pa_client_new(p->core, PA_TYPEID_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_namereg_get(p->core, p->sink_name, PA_NAMEREG_SINK, 1))) {
-            pa_log(__FILE__": Failed to get sink.\n");
-            goto fail;
-        }
-
-        if (!(c->sink_input = pa_sink_input_new(sink, PA_TYPEID_SIMPLE, c->client->name, &p->sample_spec, 0, -1))) {
-            pa_log(__FILE__": Failed to create sink input.\n");
-            goto fail;
-        }
-        
-        c->sink_input->owner = p->module;
-        c->sink_input->client = c->client;
-        
-        c->sink_input->peek = sink_input_peek_cb;
-        c->sink_input->drop = sink_input_drop_cb;
-        c->sink_input->kill = sink_input_kill_cb;
-        c->sink_input->get_latency = sink_input_get_latency_cb;
-        c->sink_input->userdata = c;
-
-        l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
-        c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS, p->core->memblock_stat);
-        assert(c->input_memblockq);
-        pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
-        c->playback.fragment_size = l/10;
-    }
-
-    if (p->mode & RECORD) {
-        struct pa_source *source;
-        size_t l;
-
-        if (!(source = pa_namereg_get(p->core, p->source_name, PA_NAMEREG_SOURCE, 1))) {
-            pa_log(__FILE__": Failed to get source.\n");
-            goto fail;
-        }
-
-        c->source_output = pa_source_output_new(source, PA_TYPEID_SIMPLE, c->client->name, &p->sample_spec, -1);
-        if (!c->source_output) {
-            pa_log(__FILE__": Failed to create source output.\n");
-            goto fail;
-        }
-        c->source_output->owner = p->module;
-        c->source_output->client = c->client;
-        
-        c->source_output->push = source_output_push_cb;
-        c->source_output->kill = source_output_kill_cb;
-        c->source_output->get_latency = source_output_get_latency_cb;
-        c->source_output->userdata = c;
-
-        l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
-        c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), 0, 0, p->core->memblock_stat);
-        pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
-    }
-
-    pa_iochannel_set_callback(c->io, io_callback, c);
-    pa_idxset_put(p->connections, c, NULL);
-
-    c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
-    assert(c->defer_event);
-    p->core->mainloop->defer_enable(c->defer_event, 0);
-    
-    return;
-    
-fail:
-    if (c)
-        connection_free(c);
-}
-
-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;
-    int enable;
-    assert(core && server && ma);
-
-    p = pa_xmalloc0(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) {
-        pa_log(__FILE__": Failed to parse sample type specification.\n");
-        goto fail;
-    }
-
-    p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
-    p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
-    
-    enable = 0;
-    if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) {
-        pa_log(__FILE__": record= expects a numeric argument.\n");
-        goto fail;
-    }
-    p->mode = enable ? RECORD : 0;
-
-    enable = 1;
-    if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
-        pa_log(__FILE__": playback= expects a numeric argument.\n");
-        goto fail;
-    }
-    p->mode |= enable ? PLAYBACK : 0;
-
-    if ((p->mode & (RECORD|PLAYBACK)) == 0) {
-        pa_log(__FILE__": neither playback nor recording enabled for protocol.\n");
-        goto fail;
-    }
-    
-    pa_socket_server_set_callback(p->server, on_connection, p);
-    
-    return p;
-
-fail:
-    if (p)
-        pa_protocol_simple_free(p);
-    return NULL;
-}
-
-
-void pa_protocol_simple_free(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_unref(p->server);
-    pa_xfree(p);
-}
-
diff --git a/polyp/protocol-simple.h b/polyp/protocol-simple.h
deleted file mode 100644 (file)
index 7b15ffc..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef fooprotocolsimplehfoo
-#define fooprotocolsimplehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "socket-server.h"
-#include "module.h"
-#include "core.h"
-#include "modargs.h"
-
-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.h b/polyp/pstream-util.h
deleted file mode 100644 (file)
index 9560bfe..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef foopstreamutilhfoo
-#define foopstreamutilhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include "pstream.h"
-#include "tagstruct.h"
-
-/* The tagstruct is freed!*/
-void pa_pstream_send_tagstruct(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
deleted file mode 100644 (file)
index c081c24..0000000
+++ /dev/null
@@ -1,489 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <unistd.h>
-#include <netinet/in.h>
-
-#include "pstream.h"
-#include "queue.h"
-#include "xmalloc.h"
-#include "log.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*500) /* half a megabyte */
-
-struct item_info {
-    enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type;
-
-    /* memblock info */
-    struct pa_memchunk chunk;
-    uint32_t channel;
-    uint32_t delta;
-
-    /* packet info */
-    struct pa_packet *packet;
-};
-
-struct pa_pstream {
-    int ref;
-    
-    struct pa_mainloop_api *mainloop;
-    struct pa_defer_event *defer_event;
-    struct pa_iochannel *io;
-    struct pa_queue *send_queue;
-
-    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, uint32_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;
-
-    struct pa_memblock_stat *memblock_stat;
-};
-
-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->mainloop->defer_enable(p->defer_event, 0);
-
-    pa_pstream_ref(p);
-    
-    if (!p->dead && pa_iochannel_is_readable(p->io))
-        do_read(p);
-
-    if (!p->dead && pa_iochannel_is_writable(p->io))
-        do_write(p);
-
-    /* In case the line was hungup, make sure to rerun this function
-       as soon as possible, until all data has been read. */
-    
-    if (!p->dead && pa_iochannel_is_hungup(p->io))
-        p->mainloop->defer_enable(p->defer_event, 1);
-    
-    pa_pstream_unref(p);
-}
-
-static void io_callback(struct pa_iochannel*io, void *userdata) {
-    struct pa_pstream *p = userdata;
-    assert(p && p->io == io);
-    do_something(p);
-}
-
-static void defer_callback(struct pa_mainloop_api *m, struct pa_defer_event *e, void*userdata) {
-    struct pa_pstream *p = userdata;
-    assert(p && p->defer_event == e && p->mainloop == m);
-    do_something(p);
-}
-
-struct pa_pstream *pa_pstream_new(struct pa_mainloop_api *m, struct pa_iochannel *io, struct pa_memblock_stat *s) {
-    struct pa_pstream *p;
-    assert(io);
-
-    p = pa_xmalloc(sizeof(struct pa_pstream));
-    p->ref = 1;
-    p->io = io;
-    pa_iochannel_set_callback(io, io_callback, p);
-
-    p->dead = 0;
-    p->die_callback = NULL;
-    p->die_callback_userdata = NULL;
-
-    p->mainloop = m;
-    p->defer_event = m->defer_new(m, defer_callback, p);
-    m->defer_enable(p->defer_event, 0);
-    
-    p->send_queue = pa_queue_new();
-    assert(p->send_queue);
-
-    p->write.current = NULL;
-    p->write.index = 0;
-
-    p->read.memblock = NULL;
-    p->read.packet = NULL;
-    p->read.index = 0;
-
-    p->recieve_packet_callback = NULL;
-    p->recieve_packet_callback_userdata = NULL;
-    
-    p->recieve_memblock_callback = NULL;
-    p->recieve_memblock_callback_userdata = NULL;
-
-    p->drain_callback = NULL;
-    p->drain_userdata = NULL;
-
-    p->memblock_stat = s;
-
-    pa_iochannel_socket_set_rcvbuf(io, 1024*8); 
-    pa_iochannel_socket_set_sndbuf(io, 1024*8); 
-
-    return p;
-}
-
-static void item_free(void *item, void *p) {
-    struct item_info *i = item;
-    assert(i);
-
-    if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) {
-        assert(i->chunk.memblock);
-        pa_memblock_unref(i->chunk.memblock);
-    } else {
-        assert(i->type == PA_PSTREAM_ITEM_PACKET);
-        assert(i->packet);
-        pa_packet_unref(i->packet);
-    }
-
-    pa_xfree(i);
-}
-
-static void pstream_free(struct pa_pstream *p) {
-    assert(p);
-
-    pa_pstream_close(p);
-    
-    pa_queue_free(p->send_queue, item_free, NULL);
-
-    if (p->write.current)
-        item_free(p->write.current, NULL);
-
-    if (p->read.memblock)
-        pa_memblock_unref(p->read.memblock);
-    
-    if (p->read.packet)
-        pa_packet_unref(p->read.packet);
-
-    pa_xfree(p);
-}
-
-void pa_pstream_send_packet(struct pa_pstream*p, struct pa_packet *packet) {
-    struct item_info *i;
-    assert(p && packet && p->ref >= 1);
-
-    if (p->dead)
-        return;
-    
-/*     pa_log(__FILE__": push-packet %p\n", packet); */
-    
-    i = pa_xmalloc(sizeof(struct item_info));
-    i->type = PA_PSTREAM_ITEM_PACKET;
-    i->packet = pa_packet_ref(packet);
-
-    pa_queue_push(p->send_queue, i);
-    p->mainloop->defer_enable(p->defer_event, 1);
-}
-
-void pa_pstream_send_memblock(struct pa_pstream*p, uint32_t channel, uint32_t delta, const struct pa_memchunk *chunk) {
-    struct item_info *i;
-    assert(p && channel != (uint32_t) -1 && chunk && p->ref >= 1);
-
-    if (p->dead)
-        return;
-    
-/*     pa_log(__FILE__": push-memblock %p\n", chunk); */
-    
-    i = pa_xmalloc(sizeof(struct item_info));
-    i->type = PA_PSTREAM_ITEM_MEMBLOCK;
-    i->chunk = *chunk;
-    i->channel = channel;
-    i->delta = delta;
-
-    pa_memblock_ref(i->chunk.memblock);
-
-    pa_queue_push(p->send_queue, i);
-    p->mainloop->defer_enable(p->defer_event, 1);
-}
-
-void pa_pstream_set_recieve_packet_callback(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, uint32_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) {
-        /*pa_log(__FILE__": pop-packet %p\n", p->write.current->packet);*/
-        
-        assert(p->write.current->packet);
-        p->write.data = p->write.current->packet->data;
-        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length);
-        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1);
-        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = 0;
-    } else {
-        assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock);
-        p->write.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index;
-        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length);
-        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel);
-        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = htonl(p->write.current->delta);
-    }
-}
-
-static void do_write(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 = (uint8_t*) p->write.descriptor + p->write.index;
-        l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;
-    } else {
-        d = (uint8_t*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
-        l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
-    }
-
-    if ((r = pa_iochannel_write(p->io, d, l)) < 0)
-        goto die;
-
-    p->write.index += r;
-
-    if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE+ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {
-        assert(p->write.current);
-        item_free(p->write.current, (void *) 1);
-        p->write.current = NULL;
-
-        if (p->drain_callback && !pa_pstream_is_pending(p))
-            p->drain_callback(p, p->drain_userdata);
-    }
-
-    return;
-    
-die:
-    p->dead = 1;
-    if (p->die_callback)
-        p->die_callback(p, p->die_callback_userdata);
-}
-
-static void do_read(struct pa_pstream *p) {
-    void *d;
-    size_t l;
-    ssize_t r;
-    assert(p);
-
-    if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
-        d = (uint8_t*) p->read.descriptor + p->read.index;
-        l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index;
-    } else {
-        assert(p->read.data);
-        d = (uint8_t*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE;
-        l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE);
-    }
-
-    if ((r = pa_iochannel_read(p->io, d, l)) <= 0)
-        goto die;
-    
-    p->read.index += r;
-
-    if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) {
-        /* Reading of frame descriptor complete */
-
-        /* Frame size too large */
-        if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) {
-            pa_log(__FILE__": Frame size too large\n");
-            goto die;
-        }
-        
-        assert(!p->read.packet && !p->read.memblock);
-
-        if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]) == (uint32_t) -1) {
-            /* Frame is a packet frame */
-            p->read.packet = pa_packet_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]));
-            assert(p->read.packet);
-            p->read.data = p->read.packet->data;
-        } else {
-            /* Frame is a memblock frame */
-            p->read.memblock = pa_memblock_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]), p->memblock_stat);
-            assert(p->read.memblock);
-            p->read.data = p->read.memblock->data;
-        }
-            
-    } else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) {
-        /* Frame payload available */
-        
-        if (p->read.memblock && p->recieve_memblock_callback) { /* Is this memblock data? Than pass it to the user */
-            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]),
-                        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;
-}
-
-void pa_pstream_unref(struct pa_pstream*p) {
-    assert(p && p->ref >= 1);
-
-    if (!(--(p->ref)))
-        pstream_free(p);
-}
-
-struct pa_pstream* pa_pstream_ref(struct pa_pstream*p) {
-    assert(p && p->ref >= 1);
-    p->ref++;
-    return p;
-}
-
-void pa_pstream_close(struct pa_pstream *p) {
-    assert(p);
-
-    p->dead = 1;
-
-    if (p->io) {
-        pa_iochannel_free(p->io);
-        p->io = NULL;
-    }
-
-    if (p->defer_event) {
-        p->mainloop->defer_free(p->defer_event);
-        p->defer_event = NULL;
-    }
-
-    p->die_callback = NULL;
-    p->drain_callback = NULL;
-    p->recieve_packet_callback = NULL;
-    p->recieve_memblock_callback = NULL;
-}
diff --git a/polyp/pstream.h b/polyp/pstream.h
deleted file mode 100644 (file)
index 8fa62f0..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef foopstreamhfoo
-#define foopstreamhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-
-#include "packet.h"
-#include "memblock.h"
-#include "iochannel.h"
-#include "mainloop-api.h"
-#include "memchunk.h"
-
-struct pa_pstream;
-
-struct pa_pstream* pa_pstream_new(struct pa_mainloop_api *m, struct pa_iochannel *io, struct pa_memblock_stat *s);
-void pa_pstream_unref(struct pa_pstream*p);
-struct pa_pstream* pa_pstream_ref(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, uint32_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, uint32_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);
-
-void pa_pstream_close(struct pa_pstream *p);
-
-#endif
diff --git a/polyp/random.c b/polyp/random.c
deleted file mode 100644 (file)
index 456954a..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <time.h>
-
-#include "random.h"
-#include "util.h"
-#include "log.h"
-
-#define RANDOM_DEVICE "/dev/urandom"
-
-void pa_random(void *ret_data, size_t length) {
-    int fd;
-    ssize_t r = 0;
-    assert(ret_data && length);
-    
-    if ((fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) {
-
-        if ((r = pa_loop_read(fd, ret_data, length)) < 0 || (size_t) r != length)
-            pa_log_error(__FILE__": failed to read entropy from '%s'\n", RANDOM_DEVICE);
-
-        close(fd);
-    }
-
-    if ((size_t) r != length) {
-        uint8_t *p;
-        size_t l;
-        
-        pa_log_warn(__FILE__": WARNING: Failed to open entropy device '"RANDOM_DEVICE"': %s"
-                    ", falling back to unsecure pseudo RNG.\n", strerror(errno));
-
-        srandom(time(NULL));
-        
-        for (p = ret_data, l = length; l > 0; p++, l--)
-            *p = (uint8_t) random();
-    }
-}
diff --git a/polyp/resampler.c b/polyp/resampler.c
deleted file mode 100644 (file)
index d3165c9..0000000
+++ /dev/null
@@ -1,496 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <string.h>
-
-#include <samplerate.h>
-
-#include "resampler.h"
-#include "sconv.h"
-#include "xmalloc.h"
-#include "log.h"
-
-struct pa_resampler {
-    struct pa_sample_spec i_ss, o_ss;
-    struct pa_channel_map i_cm, o_cm;
-    size_t i_fz, o_fz;
-    struct pa_memblock_stat *memblock_stat;
-    void *impl_data;
-    int channels;
-    pa_resample_method_t resample_method;
-
-    void (*impl_free)(struct pa_resampler *r);
-    void (*impl_set_input_rate)(struct pa_resampler *r, uint32_t rate);
-    void (*impl_run)(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out);
-};
-
-struct impl_libsamplerate {
-    float* i_buf, *o_buf;
-    unsigned i_alloc, o_alloc;
-    pa_convert_to_float32ne_func_t to_float32ne_func;
-    pa_convert_from_float32ne_func_t from_float32ne_func;
-    SRC_STATE *src_state;
-};
-
-struct impl_trivial {
-    unsigned o_counter;
-    unsigned i_counter;
-};
-
-static int libsamplerate_init(struct pa_resampler*r);
-static int trivial_init(struct pa_resampler*r);
-
-struct pa_resampler* pa_resampler_new(
-    const struct pa_sample_spec *a,
-    const struct pa_channel_map *am,
-    const struct pa_sample_spec *b,
-    const struct pa_channel_map *bm,
-    struct pa_memblock_stat *s,
-    pa_resample_method_t resample_method) {
-    
-    struct pa_resampler *r = NULL;
-    assert(a && b && pa_sample_spec_valid(a) && pa_sample_spec_valid(b) && resample_method != PA_RESAMPLER_INVALID);
-
-    if (a->channels != b->channels && a->channels != 1 && b->channels != 1)
-        goto fail;
-
-    r = pa_xmalloc(sizeof(struct pa_resampler));
-    r->impl_data = NULL;
-    r->memblock_stat = s;
-    r->resample_method = resample_method;
-
-    r->impl_free = NULL;
-    r->impl_set_input_rate = NULL;
-    r->impl_run = NULL;
-
-    /* Fill sample specs */
-    r->i_ss = *a;
-    r->o_ss = *b;
-
-    if (am)
-        r->i_cm = *am;
-    else
-        pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels);
-
-    if (bm)
-        r->o_cm = *bm;
-    else
-        pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels);
-    
-
-    r->i_fz = pa_frame_size(a);
-    r->o_fz = pa_frame_size(b);
-
-    r->channels = a->channels;
-    if (b->channels < r->channels)
-        r->channels = b->channels;
-    
-    /* Choose implementation */
-    if (a->channels != b->channels || a->format != b->format || resample_method != PA_RESAMPLER_TRIVIAL || !pa_channel_map_equal(&r->i_cm, &r->o_cm)) {
-        /* Use the libsamplerate based resampler for the complicated cases */
-        if (resample_method == PA_RESAMPLER_TRIVIAL)
-            r->resample_method = PA_RESAMPLER_SRC_ZERO_ORDER_HOLD;
-
-        if (libsamplerate_init(r) < 0)
-            goto fail;
-        
-    } else {
-        /* Use our own simple non-fp resampler for the trivial cases and when the user selects it */
-        if (trivial_init(r) < 0)
-            goto fail;
-    }
-    
-    return r;
-    
-fail:
-    if (r)
-        pa_xfree(r);
-    
-    return NULL;
-}
-
-void pa_resampler_free(struct pa_resampler *r) {
-    assert(r);
-
-    if (r->impl_free)
-        r->impl_free(r);
-    
-    pa_xfree(r);
-}
-
-void pa_resampler_set_input_rate(struct pa_resampler *r, uint32_t rate) {
-    assert(r && rate);
-
-    r->i_ss.rate = rate;
-    if (r->impl_set_input_rate)
-        r->impl_set_input_rate(r, rate);
-}
-
-void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out) {
-    assert(r && in && out && r->impl_run);
-
-    r->impl_run(r, in, out);
-}
-
-size_t pa_resampler_request(struct pa_resampler *r, size_t out_length) {
-    assert(r && (out_length % r->o_fz) == 0);
-    return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
-}
-
-pa_resample_method_t pa_resampler_get_method(struct pa_resampler *r) {
-    assert(r);
-    return r->resample_method;
-}
-
-/*** libsamplerate based implementation ***/
-
-static void libsamplerate_free(struct pa_resampler *r) {
-    struct impl_libsamplerate *i;
-    assert(r && r->impl_data);
-    i = r->impl_data;
-    
-    if (i->src_state)
-        src_delete(i->src_state);
-
-    pa_xfree(i->i_buf);
-    pa_xfree(i->o_buf);
-    pa_xfree(i);
-}
-
-static void calc_map_table(struct pa_resampler *r) {
-    struct impl_libsamplerate *u;
-    unsigned oc;
-    assert(r);
-    assert(r->impl_data);
-
-    u = r->impl_data;
-
-    if ((u->map_required = (!pa_channel_map_equal(&r->i_cm, r->o_cm) || r->i_ss.channels != r->o_ss.channels))) {
-
-        memset(u->map_table, -1, sizeof(u->map_table));
-
-        for (oc = 0; oc < r->o_iss.channels; oc++) {
-            unsigned i = 0, ic;
-
-            for (ic = 0; ic < r->i_ss.channels; ic++) {
-                pa_channel_position_t a, b;
-                
-                a = r->i_cm.map[ic];
-                b = r->o_cm.map[oc];
-                
-                if (a == b ||
-                    (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_LEFT) ||
-                    (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_RIGHT) ||
-                    (a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) ||
-                    (a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO))
-                    
-                    u->map_table[oc][i++] = ic;
-            }
-        }
-    }
-}
-
-
-static float *remap_to_float(struct pa_resampler *r, const struct pa_memchunk *in) {
-    unsigned nsamples;
-    struct impl_libsamplerate *u;
-    assert(r);
-    assert(r->impl_data);
-
-    u = r->impl_data;
-
-    nsamples = in->length / u->i_fz;
-
-    if () {
-
-        if (u->i_buf_samples < nsamples)
-            u->i_buf = pa_xrealloc(i->i_buf, sizeof(float) * (i->i_buf_samples = nsamples));
-
-        i->to_float32ne_func(ff_ins, (uint8_t*) in->memblock->data+in->index, i_nchannels, i->i_buf);
-
-    }
-
-    
-}
-
-
-static void libsamplerate_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out) {
-
-    
-    
-}
-
-
-static void libsamplerate_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;
-    struct impl_libsamplerate *i;
-    assert(r && in && out && in->length && in->memblock && (in->length % r->i_fz) == 0 && r->impl_data);
-    i = r->impl_data;
-
-    /* How many input samples? */
-    ins = in->length/r->i_fz;
-
-
-    /*     pa_log("%u / %u = %u\n", in->length, r->i_fz, ins); */
-
-    /* How much space for output samples? */
-    if (i->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;
-    }
-
-/*     pa_log("eff_ins = %u \n", eff_ins); */
-    
-    
-    out->memblock = pa_memblock_new(out->length = (ons*r->o_fz), r->memblock_stat);
-    out->index = 0;
-    assert(out->memblock);
-
-    if (i->i_alloc < eff_ins)
-        i->i_buf = pa_xrealloc(i->i_buf, sizeof(float) * (i->i_alloc = eff_ins));
-    assert(i->i_buf);
-
-/*     pa_log("eff_ins = %u \n", eff_ins); */
-
-    i->to_float32ne_func(eff_ins, (uint8_t*) in->memblock->data+in->index, i_nchannels, i->i_buf);
-
-    if (i->src_state) {
-        int ret;
-        SRC_DATA data;
-
-        if (i->o_alloc < eff_ons)
-            i->o_buf = pa_xrealloc(i->o_buf, sizeof(float) * (i->o_alloc = eff_ons));
-        assert(i->o_buf);
-
-        data.data_in = i->i_buf;
-        data.input_frames = ins;
-
-        data.data_out = i->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(i->src_state, &data);
-        assert(ret == 0);
-        assert((unsigned) data.input_frames_used == ins);
-        
-        cbuf = i->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 = i->i_buf;
-
-    if (eff_ons)
-        i->from_float32ne_func(eff_ons, cbuf, (uint8_t*)out->memblock->data+out->index, o_nchannels);
-    out->length = ons*r->o_fz;
-
-    if (!out->length) {
-        pa_memblock_unref(out->memblock);
-        out->memblock = NULL;
-    }
-}
-
-static void libsamplerate_set_input_rate(struct pa_resampler *r, uint32_t rate) {
-    int ret;
-    struct impl_libsamplerate *i;
-    assert(r && rate > 0 && r->impl_data);
-    i = r->impl_data;
-
-    ret = src_set_ratio(i->src_state, (double) r->o_ss.rate / r->i_ss.rate);
-    assert(ret == 0);
-}
-
-static int libsamplerate_init(struct pa_resampler *r) {
-    struct impl_libsamplerate *i = NULL;
-    int err;
-
-    r->impl_data = i = pa_xmalloc(sizeof(struct impl_libsamplerate));
-    
-    i->to_float32ne_func = pa_get_convert_to_float32ne_function(r->i_ss.format);
-    i->from_float32ne_func = pa_get_convert_from_float32ne_function(r->o_ss.format);
-
-    if (!i->to_float32ne_func || !i->from_float32ne_func)
-        goto fail;
-    
-    if (!(i->src_state = src_new(r->resample_method, r->channels, &err)) || !i->src_state)
-        goto fail;
-
-    i->i_buf = i->o_buf = NULL;
-    i->i_alloc = i->o_alloc = 0;
-
-    r->impl_free = libsamplerate_free;
-    r->impl_set_input_rate = libsamplerate_set_input_rate;
-    r->impl_run = libsamplerate_run;
-    
-    return 0;
-
-fail:
-    pa_xfree(i);
-    return -1;
-}
-
-/* Trivial implementation */
-
-static void trivial_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out) {
-    size_t fz;
-    unsigned  nframes;
-    struct impl_trivial *i;
-    assert(r && in && out && r->impl_data);
-    i = r->impl_data;
-
-    fz = r->i_fz;
-    assert(fz == r->o_fz);
-
-    nframes = in->length/fz;
-
-    if (r->i_ss.rate == r->o_ss.rate) {
-
-        /* In case there's no diefference in sample types, do nothing */
-        *out = *in;
-        pa_memblock_ref(out->memblock);
-
-        i->o_counter += nframes;
-    } else {
-        /* Do real resampling */
-        size_t l;
-        unsigned o_index;
-        
-        /* The length of the new memory block rounded up */
-        l = ((((nframes+1) * r->o_ss.rate) / r->i_ss.rate) + 1) * fz;
-        
-        out->index = 0;
-        out->memblock = pa_memblock_new(l, r->memblock_stat);
-        
-        for (o_index = 0;; o_index++, i->o_counter++) {
-            unsigned j;
-            
-            j = (i->o_counter * r->i_ss.rate / r->o_ss.rate);
-            j = j > i->i_counter ? j - i->i_counter : 0;
-            
-            if (j >= nframes)
-                break;
-
-            assert(o_index*fz < out->memblock->length);
-            
-            memcpy((uint8_t*) out->memblock->data + fz*o_index,
-                   (uint8_t*) in->memblock->data + in->index + fz*j, fz);
-            
-        }
-            
-        out->length = o_index*fz;
-    }
-
-    i->i_counter += nframes;
-    
-    /* Normalize counters */
-    while (i->i_counter >= r->i_ss.rate) {
-        i->i_counter -= r->i_ss.rate;
-        assert(i->o_counter >= r->o_ss.rate);
-        i->o_counter -= r->o_ss.rate;
-    }
-}
-
-static void trivial_free(struct pa_resampler *r) {
-    assert(r);
-    pa_xfree(r->impl_data);
-}
-
-static void trivial_set_input_rate(struct pa_resampler *r, uint32_t rate) {
-    struct impl_trivial *i;
-    assert(r && rate > 0 && r->impl_data);
-    i = r->impl_data;
-
-    i->i_counter = 0;
-    i->o_counter = 0;
-}
-
-static int trivial_init(struct pa_resampler*r) {
-    struct impl_trivial *i;
-    assert(r && r->i_ss.format == r->o_ss.format && r->i_ss.channels == r->o_ss.channels);
-
-    r->impl_data = i = pa_xmalloc(sizeof(struct impl_trivial));
-    i->o_counter = i->i_counter = 0;
-
-    r->impl_run = trivial_run;
-    r->impl_free = trivial_free;
-    r->impl_set_input_rate = trivial_set_input_rate;
-                                  
-    return 0;
-}
-
-const char *pa_resample_method_to_string(pa_resample_method_t m) {
-    static const char * const resample_methods[] = {
-        "src-sinc-best-quality",
-        "src-sinc-medium-quality",
-        "src-sinc-fastest",
-        "src-zero-order-hold",
-        "src-linear",
-        "trivial"
-    };
-
-    if (m < 0 || m >= PA_RESAMPLER_MAX)
-        return NULL;
-
-    return resample_methods[m];
-}
-
-pa_resample_method_t pa_parse_resample_method(const char *string) {
-    assert(string);
-
-    if (!strcmp(string, "src-sinc-best-quality"))
-        return PA_RESAMPLER_SRC_SINC_BEST_QUALITY;
-    else if (!strcmp(string, "src-sinc-medium-quality"))
-        return PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY;
-    else if (!strcmp(string, "src-sinc-fastest"))
-        return PA_RESAMPLER_SRC_SINC_FASTEST;
-    else if (!strcmp(string, "src-zero-order-hold"))
-        return PA_RESAMPLER_SRC_ZERO_ORDER_HOLD;
-    else if (!strcmp(string, "src-linear"))
-        return PA_RESAMPLER_SRC_LINEAR;
-    else if (!strcmp(string, "trivial"))
-        return PA_RESAMPLER_TRIVIAL;
-    else
-        return PA_RESAMPLER_INVALID;
-}
-
diff --git a/polyp/resampler.h b/polyp/resampler.h
deleted file mode 100644 (file)
index ec6a808..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-#ifndef fooresamplerhfoo
-#define fooresamplerhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <samplerate.h>
-
-#include "sample.h"
-#include "memblock.h"
-#include "memchunk.h"
-
-struct pa_resampler;
-
-typedef enum {
-    PA_RESAMPLER_INVALID                 = -1,
-    PA_RESAMPLER_SRC_SINC_BEST_QUALITY   = SRC_SINC_BEST_QUALITY,
-    PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = SRC_SINC_MEDIUM_QUALITY,
-    PA_RESAMPLER_SRC_SINC_FASTEST        = SRC_SINC_FASTEST,
-    PA_RESAMPLER_SRC_ZERO_ORDER_HOLD     = SRC_ZERO_ORDER_HOLD,
-    PA_RESAMPLER_SRC_LINEAR              = SRC_LINEAR,
-    PA_RESAMPLER_TRIVIAL,
-    PA_RESAMPLER_MAX
-} pa_resample_method_t;
-
-struct pa_resampler* pa_resampler_new(
-    const struct pa_sample_spec *a,
-    const struct pa_channel_map *am,
-    const struct pa_sample_spec *b,
-    const struct pa_channel_map *bm,
-    struct pa_memblock_stat *s,
-    pa_resample_method_t resample_method);
-
-void pa_resampler_free(struct pa_resampler *r);
-
-/* Returns the size of an input memory block which is required to return the specified amount of output data */
-size_t pa_resampler_request(struct pa_resampler *r, size_t out_length);
-
-/* Pass the specified memory chunk to the resampler and return the newly resampled data */
-void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out);
-
-/* Change the input rate of the resampler object */
-void pa_resampler_set_input_rate(struct pa_resampler *r, uint32_t rate);
-
-/* Return the resampling method of the resampler object */
-pa_resample_method_t pa_resampler_get_method(struct pa_resampler *r);
-
-/* Try to parse the resampler method */
-pa_resample_method_t pa_parse_resample_method(const char *string);
-
-/* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */
-const char *pa_resample_method_to_string(pa_resample_method_t m);
-
-#endif
diff --git a/polyp/sample-util.c b/polyp/sample-util.c
deleted file mode 100644 (file)
index 44cacfc..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <stdlib.h>
-
-#include "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((uint8_t*) c->memblock->data+c->index, c->length, spec);
-}
-
-void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec) {
-    uint8_t c = 0;
-    assert(p && length && spec);
-
-    switch (spec->format) {
-        case PA_SAMPLE_U8:
-            c = 0x80;
-            break;
-        case PA_SAMPLE_S16LE:
-        case PA_SAMPLE_S16BE:
-        case PA_SAMPLE_FLOAT32:
-            c = 0;
-            break;
-        case PA_SAMPLE_ALAW:
-        case PA_SAMPLE_ULAW:
-            c = 80;
-            break;
-        default:
-            assert(0);
-    }
-                
-    memset(p, c, length);
-}
-
-size_t pa_mix(struct pa_mix_info streams[],
-              unsigned nstreams,
-              void *data,
-              size_t length,
-              const struct pa_sample_spec *spec,
-              const struct pa_cvolume *volume) {
-    
-    assert(streams && data && length && spec);
-    
-    if (spec->format == PA_SAMPLE_S16NE) {
-        size_t d;
-        unsigned channel = 0;
-        
-        for (d = 0;; d += sizeof(int16_t)) {
-            unsigned i;
-            int32_t sum = 0;
-            
-            if (d >= length)
-                return d;
-            
-            for (i = 0; i < nstreams; i++) {
-                int32_t v;
-                pa_volume_t cvolume = streams[i].volume.values[channel];
-                
-                if (d >= streams[i].chunk.length)
-                    return d;
-                
-                if (cvolume == PA_VOLUME_MUTED)
-                    v = 0;
-                else {
-                    v = *((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d));
-                    
-                    if (cvolume != PA_VOLUME_NORM) {
-                        v *= cvolume;
-                        v /= PA_VOLUME_NORM;
-                    }
-                }
-                
-                sum += v;
-            }
-            
-            if (volume == PA_VOLUME_MUTED)
-                sum = 0;
-            else if (volume != PA_VOLUME_NORM) {
-                sum *= volume;
-                sum /= PA_VOLUME_NORM;
-            }
-            
-            if (sum < -0x8000) sum = -0x8000;
-            if (sum > 0x7FFF) sum = 0x7FFF;
-            
-            *((int16_t*) data) = sum;
-            data = (uint8_t*) data + sizeof(int16_t);
-
-            if (++channel >= spec->channels)
-                channel = 0;
-        }
-    } else if (spec->format == PA_SAMPLE_U8) {
-        size_t d;
-        unsigned channel = 0;
-        
-        for (d = 0;; d ++) {
-            int32_t sum = 0;
-            unsigned i;
-            
-            if (d >= length)
-                return d;
-            
-            for (i = 0; i < nstreams; i++) {
-                int32_t v;
-                pa_volume_t cvolume = streams[i].volume.values[channel];
-                
-                if (d >= streams[i].chunk.length)
-                    return d;
-                
-                if (cvolume == PA_VOLUME_MUTED)
-                    v = 0;
-                else {
-                    v = (int32_t) *((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d) - 0x80;
-                    
-                    if (cvolume != PA_VOLUME_NORM) {
-                        v *= cvolume;
-                        v /= PA_VOLUME_NORM;
-                    }
-                }
-                
-                sum += v;
-            }
-            
-            if (volume == PA_VOLUME_MUTED)
-                sum = 0;
-            else if (volume != PA_VOLUME_NORM) {
-                sum *= volume;
-                sum /= PA_VOLUME_NORM;
-            }
-            
-            if (sum < -0x80) sum = -0x80;
-            if (sum > 0x7F) sum = 0x7F;
-            
-            *((uint8_t*) data) = (uint8_t) (sum + 0x80);
-            data = (uint8_t*) data + 1;
-
-            if (++channel >= spec->channels)
-                channel = 0;
-        }
-        
-    } else if (spec->format == PA_SAMPLE_FLOAT32NE) {
-        size_t d;
-        unsigned channel = 0;
-        
-        for (d = 0;; d += sizeof(float)) {
-            float_t sum = 0;
-            unsigned i;
-            
-            if (d >= length)
-                return d;
-            
-            for (i = 0; i < nstreams; i++) {
-                float v;
-                pa_volume_t cvolume = streams[i].volume.values[channel];
-                
-                if (d >= streams[i].chunk.length)
-                    return d;
-                
-                if (cvolume == PA_VOLUME_MUTED)
-                    v = 0;
-                else {
-                    v = *((float*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d));
-                    
-                    if (cvolume != PA_VOLUME_NORM)
-                        v = v*cvolume/PA_VOLUME_NORM;
-                }
-                
-                sum += v;
-            }
-            
-            if (volume == PA_VOLUME_MUTED)
-                sum = 0;
-            else if (volume != PA_VOLUME_NORM)
-                sum = sum*volume/PA_VOLUME_NORM;
-            
-            if (sum < -1) sum = -1;
-            if (sum > 1) sum = 1;
-            
-            *((float*) data) = sum;
-            data = (uint8_t*) data + sizeof(float);
-
-            if (++channel >= spec->channels)
-                channel = 0;
-        }
-    } else {
-        abort();
-    }
-}
-
-
-void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, const struct pa_cvolume *volume) {
-    assert(c && spec && (c->length % pa_frame_size(spec) == 0));
-    assert(volume);
-
-    if (pa_cvolume_channels_equal_to(volume, spec->channels, PA_VOLUME_NORM))
-        return;
-
-    if (pa_cvolume_channels_equal_to(volume, spec->channels, PA_VOLUME_MUTED)) {
-        pa_silence_memchunk(c, spec);
-        return;
-    }
-
-    if (spec->format == PA_SAMPLE_S16NE) {
-        int16_t *d;
-        size_t n;
-        unsigned c = 0;
-        
-        for (d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) {
-            int32_t t = (int32_t)(*d);
-            
-            t *= volume->values[c];
-            t /= PA_VOLUME_NORM;
-            
-            if (t < -0x8000) t = -0x8000;
-            if (t > 0x7FFF) t = 0x7FFF;
-            
-            *d = (int16_t) t;
-
-            if (++c >= spec->channels)
-                c = 0;
-        }
-    } else if (spec->format == PA_SAMPLE_U8) {
-        uint8_t *d;
-        size_t n;
-        unsigned c = 0;
-
-        for (d = (uint8_t*) c->memblock->data + c->index, n = c->length; n > 0; d++, n--) {
-            int32_t t = (int32_t) *d - 0x80;
-
-            t *= volume->values[c];
-            t /= PA_VOLUME_NORM;
-
-            if (t < -0x80) t = -0x80;
-            if (t > 0x7F) t = 0x7F;
-
-            *d = (uint8_t) (t + 0x80);
-            
-            if (++c >= spec->channels)
-                c = 0;
-        }
-    } else if (spec->format == PA_SAMPLE_FLOAT32NE) {
-        float *d;
-        size_t n;
-        unsigned c = 0;
-
-        for (d = (float*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(float); n > 0; d++, n--) {
-            float t = *d;
-
-            t *= volume->values[c];
-            t /= PA_VOLUME_NORM;
-
-            if (t < -1) t = -1;
-            if (t > 1) t = 1;
-
-            *d = t;
-
-            if (++c >= spec->channels)
-                c = 0;
-        }
-        
-    } else {
-        abort();
-    }
-}
-
diff --git a/polyp/sample-util.h b/polyp/sample-util.h
deleted file mode 100644 (file)
index f0c71b8..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef foosampleutilhfoo
-#define foosampleutilhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "sample.h"
-#include "memblock.h"
-#include "memchunk.h"
-
-
-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;
-    struct pa_cvolume cvolume;
-    void *userdata;
-};
-
-size_t pa_mix(const struct pa_mix_info channels[],
-              unsigned nchannels,
-              void *data,
-              size_t length,
-              const struct pa_sample_spec *spec,
-              const struct pa_cvolume *volume);
-
-void pa_volume_memchunk(struct pa_memchunk*c,
-                        const struct pa_sample_spec *spec,
-                        const struct pa_cvolume *volume);
-
-#endif
diff --git a/polyp/sample.c b/polyp/sample.c
deleted file mode 100644 (file)
index d38cc1b..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <math.h>
-#include <string.h>
-
-#include "sample.h"
-
-size_t pa_frame_size(const struct pa_sample_spec *spec) {
-    size_t b = 1;
-    assert(spec);
-
-    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_frame_size(spec);
-}
-
-pa_usec_t pa_bytes_to_usec(uint64_t length, const struct pa_sample_spec *spec) {
-    assert(spec);
-
-    return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate);
-}
-
-int pa_sample_spec_valid(const struct pa_sample_spec *spec) {
-    assert(spec);
-
-    if (spec->rate <= 0 ||
-        spec->channels <= 0 ||
-        spec->channels >= PA_CHANNELS_MAX ||
-        spec->format >= PA_SAMPLE_MAX ||
-        spec->format < 0)
-        return 0;
-
-    return 1;
-}
-
-int pa_sample_spec_equal(const 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);
-}
-
-const char *pa_sample_format_to_string(enum pa_sample_format f) {
-    static const char* const table[]= {
-        [PA_SAMPLE_U8] = "u8",
-        [PA_SAMPLE_ALAW] = "aLaw",
-        [PA_SAMPLE_ULAW] = "uLaw",
-        [PA_SAMPLE_S16LE] = "s16le",
-        [PA_SAMPLE_S16BE] = "s16be",
-        [PA_SAMPLE_FLOAT32LE] = "float32le",
-        [PA_SAMPLE_FLOAT32BE] = "float32be",
-    };
-
-    if (f >= PA_SAMPLE_MAX)
-        return NULL;
-
-    return table[f];
-}
-
-char *pa_sample_spec_snprint(char *s, size_t l, const struct pa_sample_spec *spec) {
-    assert(s && l && spec);
-    
-    if (!pa_sample_spec_valid(spec))
-        snprintf(s, l, "Invalid");
-    else
-        snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
-
-    return s;
-}
-
-
-void pa_bytes_snprint(char *s, size_t l, unsigned v) {
-    if (v >= ((unsigned) 1024)*1024*1024)
-        snprintf(s, l, "%0.1f GB", ((double) v)/1024/1024/1024);
-    else if (v >= ((unsigned) 1024)*1024)
-        snprintf(s, l, "%0.1f MB", ((double) v)/1024/1024);
-    else if (v >= (unsigned) 1024)
-        snprintf(s, l, "%0.1f KB", ((double) v)/1024);
-    else
-        snprintf(s, l, "%u B", (unsigned) v);
-}
-
-enum pa_sample_format pa_parse_sample_format(const char *format) {
-    
-    if (strcasecmp(format, "s16le") == 0)
-        return PA_SAMPLE_S16LE;
-    else if (strcasecmp(format, "s16be") == 0)
-        return PA_SAMPLE_S16BE;
-    else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0)
-        return PA_SAMPLE_S16NE;
-    else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0)
-        return PA_SAMPLE_U8;
-    else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0)
-        return PA_SAMPLE_FLOAT32;
-    else if (strcasecmp(format, "float32le") == 0)
-        return PA_SAMPLE_FLOAT32LE;
-    else if (strcasecmp(format, "float32be") == 0)
-        return PA_SAMPLE_FLOAT32BE;
-    else if (strcasecmp(format, "ulaw") == 0)
-        return PA_SAMPLE_ULAW;
-    else if (strcasecmp(format, "alaw") == 0)
-        return PA_SAMPLE_ALAW;
-
-    return -1;
-}
diff --git a/polyp/sample.h b/polyp/sample.h
deleted file mode 100644 (file)
index c4ccd3d..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#ifndef foosamplehfoo
-#define foosamplehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include <sys/types.h>
-#include <math.h>
-
-#include <polyp/cdecl.h>
-
-/** \file
- * Constants and routines for sample type handling */
-
-PA_C_DECL_BEGIN
-
-/* Maximum allowed channels */
-#define PA_CHANNELS_MAX 16
-
-/** Sample format */
-typedef enum {
-    PA_SAMPLE_U8,              /**< Unsigned 8 Bit PCM */
-    PA_SAMPLE_ALAW,            /**< 8 Bit a-Law */
-    PA_SAMPLE_ULAW,            /**< 8 Bit mu-Law */
-    PA_SAMPLE_S16LE,           /**< Signed 16 Bit PCM, little endian (PC) */
-    PA_SAMPLE_S16BE,           /**< Signed 16 Bit PCM, big endian */
-    PA_SAMPLE_FLOAT32LE,       /**< 32 Bit IEEE floating point, little endian, range -1..1 */
-    PA_SAMPLE_FLOAT32BE,       /**< 32 Bit IEEE floating point, big endian, range -1..1 */
-    PA_SAMPLE_MAX,             /**< Upper limit of valid sample types */
-    PA_SAMPLE_INVALID = -1     /**< An invalid value */
-} pa_sample_format_t;
-
-#ifdef WORDS_BIGENDIAN
-/** Signed 16 Bit PCM, native endian */
-#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
-/** 32 Bit IEEE floating point, native endian */
-#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
-#else
-/** Signed 16 Bit PCM, native endian */
-#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
-/** 32 Bit IEEE floating point, native endian */
-#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
-#endif
-
-/** A Shortcut for PA_SAMPLE_FLOAT32NE */
-#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE
-
-/** A sample format and attribute specification */
-struct pa_sample_spec {
-    pa_sample_format_t format;     /**< The sample format */
-    uint32_t rate;                 /**< The sample rate. (e.g. 44100) */
-    uint8_t channels;              /**< Audio channels. (1 for mono, 2 for stereo, ...) */
-};
-
-/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */
-typedef uint64_t pa_usec_t;
-
-/** Return the amount of bytes playback of a second of audio with the specified sample type takes */
-size_t pa_bytes_per_second(const struct pa_sample_spec *spec);
-
-/** Return the size of a frame with the specific sample type */
-size_t pa_frame_size(const struct pa_sample_spec *spec);
-
-/** Calculate the time the specified bytes take to play with the specified sample type */
-pa_usec_t pa_bytes_to_usec(uint64_t length, const struct pa_sample_spec *spec);
-
-/** Return non-zero when the sample type specification is valid */
-int pa_sample_spec_valid(const struct pa_sample_spec *spec);
-
-/** Return non-zero when the two sample type specifications match */
-int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b);
-
-/* Return a descriptive string for the specified sample format. \since 0.8 */
-const char *pa_sample_format_to_string(pa_sample_format_t f);
-
-/** Parse a sample format text. Inverse of pa_sample_format_to_string() */
-pa_sample_format_t pa_parse_sample_format(const char *format);
-
-/** Maximum required string length for pa_sample_spec_snprint() */
-#define PA_SAMPLE_SPEC_SNPRINT_MAX 32
-
-/** Pretty print a sample type specification to a string */
-char* pa_sample_spec_snprint(char *s, size_t l, const struct pa_sample_spec *spec);
-
-/** Pretty print a byte size value. (i.e. "2.5 MB") */
-void pa_bytes_snprint(char *s, size_t l, unsigned v);
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/scache.c b/polyp/scache.c
deleted file mode 100644 (file)
index ccdc718..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <limits.h>
-#include <glob.h>
-
-#include "scache.h"
-#include "sink-input.h"
-#include "mainloop.h"
-#include "sample-util.h"
-#include "play-memchunk.h"
-#include "xmalloc.h"
-#include "subscribe.h"
-#include "namereg.h"
-#include "sound-file.h"
-#include "util.h"
-#include "log.h"
-
-#define UNLOAD_POLL_TIME 2
-
-static void timeout_callback(struct pa_mainloop_api *m, struct pa_time_event*e, const struct timeval *tv, void *userdata) {
-    struct pa_core *c = userdata;
-    struct timeval ntv;
-    assert(c && c->mainloop == m && c->scache_auto_unload_event == e);
-
-    pa_scache_unload_unused(c);
-
-    gettimeofday(&ntv, NULL);
-    ntv.tv_sec += UNLOAD_POLL_TIME;
-    m->time_restart(e, &ntv);
-}
-
-static void free_entry(struct pa_scache_entry *e) {
-    assert(e);
-    pa_namereg_unregister(e->core, e->name);
-    pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);
-    pa_xfree(e->name);
-    pa_xfree(e->filename);
-    if (e->memchunk.memblock)
-        pa_memblock_unref(e->memchunk.memblock);
-    pa_xfree(e);
-}
-
-static struct pa_scache_entry* scache_add_item(struct pa_core *c, const char *name) {
-    struct pa_scache_entry *e;
-    assert(c && name);
-
-    if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) {
-        if (e->memchunk.memblock)
-            pa_memblock_unref(e->memchunk.memblock);
-
-        pa_xfree(e->filename);
-        
-        assert(e->core == c);
-
-        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
-    } else {
-        e = pa_xmalloc(sizeof(struct pa_scache_entry));
-
-        if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) {
-            pa_xfree(e);
-            return NULL;
-        }
-
-        e->name = pa_xstrdup(name);
-        e->core = c;
-
-        if (!c->scache) {
-            c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-            assert(c->scache);
-        }
-
-        pa_idxset_put(c->scache, e, &e->index);
-
-        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index);
-    }
-
-    e->volume = PA_VOLUME_NORM;
-    e->last_used_time = 0;
-    e->memchunk.memblock = NULL;
-    e->memchunk.index = e->memchunk.length = 0;
-    e->filename = NULL;
-    e->lazy = 0;
-    e->last_used_time = 0;
-
-    memset(&e->sample_spec, 0, sizeof(struct pa_sample_spec));
-
-    return e;
-}
-
-int pa_scache_add_item(struct pa_core *c, const char *name, struct pa_sample_spec *ss, struct pa_memchunk *chunk, uint32_t *index) {
-    struct pa_scache_entry *e;
-    assert(c && name);
-
-    if (!(e = scache_add_item(c, name)))
-        return -1;
-
-    if (ss)
-        e->sample_spec = *ss;
-
-    if (chunk) {
-        e->memchunk = *chunk;
-        pa_memblock_ref(e->memchunk.memblock);
-    }
-
-    if (index)
-        *index = e->index;
-
-    return 0;
-}
-
-int pa_scache_add_file(struct pa_core *c, const char *name, const char *filename, uint32_t *index) {
-    struct pa_sample_spec ss;
-    struct pa_memchunk chunk;
-    int r;
-
-    if (pa_sound_file_load(filename, &ss, &chunk, c->memblock_stat) < 0)
-        return -1;
-        
-    r = pa_scache_add_item(c, name, &ss, &chunk, index);
-    pa_memblock_unref(chunk.memblock);
-
-    return r;
-}
-
-int pa_scache_add_file_lazy(struct pa_core *c, const char *name, const char *filename, uint32_t *index) {
-    struct pa_scache_entry *e;
-    assert(c && name);
-
-    if (!(e = scache_add_item(c, name)))
-        return -1;
-
-    e->lazy = 1;
-    e->filename = pa_xstrdup(filename);
-    
-    if (!c->scache_auto_unload_event) {
-        struct timeval ntv;
-        gettimeofday(&ntv, NULL);
-        ntv.tv_sec += UNLOAD_POLL_TIME;
-        c->scache_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
-    }
-
-    return 0;
-}
-
-int pa_scache_remove_item(struct pa_core *c, const char *name) {
-    struct pa_scache_entry *e;
-    assert(c && name);
-
-    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
-        return -1;
-
-    if (pa_idxset_remove_by_data(c->scache, e, NULL) != e)
-        assert(0);
-
-    free_entry(e);
-    return 0;
-}
-
-static void free_cb(void *p, void *userdata) {
-    struct pa_scache_entry *e = p;
-    assert(e);
-    free_entry(e);
-}
-
-void pa_scache_free(struct pa_core *c) {
-    assert(c);
-
-    if (c->scache) {
-        pa_idxset_free(c->scache, free_cb, NULL);
-        c->scache = NULL;
-    }
-
-    if (c->scache_auto_unload_event)
-        c->mainloop->time_free(c->scache_auto_unload_event);
-}
-
-int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume) {
-    struct pa_scache_entry *e;
-    char *t;
-    assert(c && name && sink);
-
-    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1)))
-        return -1;
-
-    if (e->lazy && !e->memchunk.memblock) {
-        if (pa_sound_file_load(e->filename, &e->sample_spec, &e->memchunk, c->memblock_stat) < 0)
-            return -1;
-
-        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
-    }
-    
-    if (!e->memchunk.memblock)
-        return -1;
-
-    t = pa_sprintf_malloc("sample:%s", name);
-    if (pa_play_memchunk(sink, t, &e->sample_spec, &e->memchunk, pa_volume_multiply(volume, e->volume)) < 0) {
-        free(t);
-        return -1;
-    }
-
-    free(t);
-
-    if (e->lazy)
-        time(&e->last_used_time);
-    
-    return 0;
-}
-
-const char * pa_scache_get_name_by_id(struct pa_core *c, uint32_t id) {
-    struct pa_scache_entry *e;
-    assert(c && id != PA_IDXSET_INVALID);
-
-    if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id)))
-        return NULL;
-
-    return e->name;
-}
-
-uint32_t pa_scache_get_id_by_name(struct pa_core *c, const char *name) {
-    struct pa_scache_entry *e;
-    assert(c && name);
-
-    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
-        return PA_IDXSET_INVALID;
-
-    return e->index;
-}
-
-uint32_t pa_scache_total_size(struct pa_core *c) {
-    struct pa_scache_entry *e;
-    uint32_t index, sum = 0;
-    assert(c);
-
-    if (!c->scache || !pa_idxset_ncontents(c->scache))
-        return 0;
-    
-    for (e = pa_idxset_first(c->scache, &index); e; e = pa_idxset_next(c->scache, &index))
-        if (e->memchunk.memblock)
-            sum += e->memchunk.length;
-
-    return sum;
-}
-
-void pa_scache_unload_unused(struct pa_core *c) {
-    struct pa_scache_entry *e;
-    time_t now;
-    uint32_t index;
-    assert(c);
-
-    if (!c->scache || !pa_idxset_ncontents(c->scache))
-        return;
-    
-    time(&now);
-
-    for (e = pa_idxset_first(c->scache, &index); e; e = pa_idxset_next(c->scache, &index)) {
-
-        if (!e->lazy || !e->memchunk.memblock)
-            continue;
-
-        if (e->last_used_time + c->scache_idle_time > now)
-            continue;
-        
-        pa_memblock_unref(e->memchunk.memblock);
-        e->memchunk.memblock = NULL;
-        e->memchunk.index = e->memchunk.length = 0;
-
-        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
-    }
-}
-
-static void add_file(struct pa_core *c, const char *pathname) {
-    struct stat st;
-    const char *e;
-
-    if (!(e = strrchr(pathname, '/')))
-        e = pathname;
-    else
-        e++;
-    
-    if (stat(pathname, &st) < 0) {
-        pa_log(__FILE__": stat('%s') failed: %s\n", pathname, strerror(errno));
-        return;
-    }
-
-    if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
-        pa_scache_add_file_lazy(c, e, pathname, NULL);
-}
-
-int pa_scache_add_directory_lazy(struct pa_core *c, const char *pathname) {
-    DIR *dir;
-    assert(c && pathname);
-
-    /* First try to open this as directory */
-    if (!(dir = opendir(pathname))) {
-        glob_t p;
-        unsigned int i;
-        /* If that fails, try to open it as shell glob */
-
-        if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) {
-            pa_log(__FILE__": Failed to open directory: %s\n", strerror(errno));
-            return -1;
-        }
-
-        for (i = 0; i < p.gl_pathc; i++)
-            add_file(c, p.gl_pathv[i]);
-        
-        globfree(&p);
-    } else {
-        struct dirent *e;
-
-        while ((e = readdir(dir))) {
-            char p[PATH_MAX];
-
-            if (e->d_name[0] == '.')
-                continue;
-
-            snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
-            add_file(c, p);
-        }
-    }
-
-    closedir(dir);
-    return 0;
-}
diff --git a/polyp/scache.h b/polyp/scache.h
deleted file mode 100644 (file)
index f7043f1..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#ifndef fooscachehfoo
-#define fooscachehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "core.h"
-#include "memchunk.h"
-#include "sink.h"
-
-struct pa_scache_entry {
-    struct pa_core *core;
-    uint32_t index;
-    char *name;
-    
-    uint32_t volume;
-    struct pa_sample_spec sample_spec;
-    struct pa_memchunk memchunk;
-
-    char *filename;
-    
-    int lazy;
-    time_t last_used_time;
-};
-
-int pa_scache_add_item(struct pa_core *c, const char *name, struct pa_sample_spec *ss, struct pa_memchunk *chunk, uint32_t *index);
-int pa_scache_add_file(struct pa_core *c, const char *name, const char *filename, uint32_t *index);
-int pa_scache_add_file_lazy(struct pa_core *c, const char *name, const char *filename, uint32_t *index);
-
-int pa_scache_add_directory_lazy(struct pa_core *c, const char *pathname);
-
-int pa_scache_remove_item(struct pa_core *c, const char *name);
-int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume);
-void pa_scache_free(struct pa_core *c);
-
-const char *pa_scache_get_name_by_id(struct pa_core *c, uint32_t id);
-uint32_t pa_scache_get_id_by_name(struct pa_core *c, const char *name);
-
-uint32_t pa_scache_total_size(struct pa_core *c);
-
-void pa_scache_unload_unused(struct pa_core *c);
-
-#endif
diff --git a/polyp/sconv-s16be.h b/polyp/sconv-s16be.h
deleted file mode 100644 (file)
index 86107fb..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef foosconv_s16befoo
-#define foosconv_s16befoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-void pa_sconv_s16be_to_float32ne(unsigned n, const void *a, unsigned an, float *b);
-void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, void *b, unsigned bn);
-
-#endif
diff --git a/polyp/sconv-s16le.c b/polyp/sconv-s16le.c
deleted file mode 100644 (file)
index 49c4746..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <inttypes.h>
-
-#include "endianmacros.h"
-#include "sconv.h"
-#include "log.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_float32ne(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_float32ne(unsigned n, const float *a, void *b, unsigned bn) {
-    int16_t *cb = b;
-
-/*     pa_log("%u %p %p %u\n", n, a, b, bn); */
-    
-    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
deleted file mode 100644 (file)
index caed826..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef foosconv_s16lefoo
-#define foosconv_s16lefoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, unsigned an, float *b);
-void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b, unsigned bn);
-
-#endif
diff --git a/polyp/sconv.c b/polyp/sconv.c
deleted file mode 100644 (file)
index a404f43..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include "endianmacros.h"
-#include "sconv.h"
-#include "g711.h"
-
-#include "sconv-s16le.h"
-#include "sconv-s16be.h"
-
-static void u8_to_float32ne(unsigned n, const void *a, 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)-128)/127;
-        }
-
-        if (sum > 1)
-            sum = 1;
-        if (sum < -1)
-            sum = -1;
-
-        *(b++) = sum;
-    }
-}    
-
-static void u8_from_float32ne(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+128);
-        
-        for (i = 0; i < bn; i++)
-            *(cb++) = u;
-    }
-}
-
-static void float32ne_to_float32ne(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 float32ne_from_float32ne(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;
-    }
-}
-
-static void ulaw_to_float32ne(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++)
-            sum += (float) st_ulaw2linear16(*ca++) / 0x7FFF;
-
-        if (sum > 1)
-            sum = 1;
-        if (sum < -1)
-            sum = -1;
-
-        *(b++) = sum;
-    }
-}
-
-static void ulaw_from_float32ne(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 = st_14linear2ulaw((int16_t) (v * 0x1FFF));
-        
-        for (i = 0; i < bn; i++)
-            *(cb++) = u;
-    }
-}
-
-static void alaw_to_float32ne(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++)
-            sum += (float) st_alaw2linear16(*ca++) / 0x7FFF;
-
-        if (sum > 1)
-            sum = 1;
-        if (sum < -1)
-            sum = -1;
-
-        *(b++) = sum;
-    }
-}
-
-static void alaw_from_float32ne(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 = st_13linear2alaw((int16_t) (v * 0xFFF));
-        
-        for (i = 0; i < bn; i++)
-            *(cb++) = u;
-    }
-}
-
-
-pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(enum pa_sample_format f) {
-    switch(f) {
-        case PA_SAMPLE_U8:
-            return u8_to_float32ne;
-        case PA_SAMPLE_S16LE:
-            return pa_sconv_s16le_to_float32ne;
-        case PA_SAMPLE_S16BE:
-            return pa_sconv_s16be_to_float32ne;
-        case PA_SAMPLE_FLOAT32NE:
-            return float32ne_to_float32ne;
-        case PA_SAMPLE_ALAW:
-            return alaw_to_float32ne;
-        case PA_SAMPLE_ULAW:
-            return ulaw_to_float32ne;
-        default:
-            return NULL;
-    }
-}
-
-pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(enum pa_sample_format f) {
-    switch(f) {
-        case PA_SAMPLE_U8:
-            return u8_from_float32ne;
-        case PA_SAMPLE_S16LE:
-            return pa_sconv_s16le_from_float32ne;
-        case PA_SAMPLE_S16BE:
-            return pa_sconv_s16be_from_float32ne;
-        case PA_SAMPLE_FLOAT32NE:
-            return float32ne_from_float32ne;
-        case PA_SAMPLE_ALAW:
-            return alaw_from_float32ne;
-        case PA_SAMPLE_ULAW:
-            return ulaw_from_float32ne;
-        default:
-            return NULL;
-    }
-}
diff --git a/polyp/sconv.h b/polyp/sconv.h
deleted file mode 100644 (file)
index 71517ec..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef foosconvhfoo
-#define foosconvhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "sample.h"
-
-typedef void (*pa_convert_to_float32ne_func_t)(unsigned n, const void *a, unsigned an, float *b);
-typedef void (*pa_convert_from_float32ne_func_t)(unsigned n, const float *a, void *b, unsigned bn);
-
-pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(enum pa_sample_format f);
-pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(enum pa_sample_format f);
-
-#endif
diff --git a/polyp/sink-input.c b/polyp/sink-input.c
deleted file mode 100644 (file)
index 40ee705..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "sink-input.h"
-#include "sample-util.h"
-#include "xmalloc.h"
-#include "subscribe.h"
-#include "log.h"
-
-#define CONVERT_BUFFER_LENGTH 4096
-
-struct pa_sink_input* pa_sink_input_new(
-    struct pa_sink *s,
-    const char *name,
-    const char *driver,
-    const struct pa_sample_spec *spec,
-    const struct pa_channel_map *map,
-    int variable_rate,
-    int resample_method) {
-    
-    struct pa_sink_input *i;
-    struct pa_resampler *resampler = NULL;
-    int r;
-    char st[256];
-
-    assert(s);
-    assert(spec);
-    assert(s->state == PA_SINK_RUNNING);
-
-    if (pa_idxset_ncontents(s->inputs) >= PA_MAX_INPUTS_PER_SINK) {
-        pa_log(__FILE__": Failed to create sink input: too many inputs per sink.\n");
-        return NULL;
-    }
-
-    if (resample_method == PA_RESAMPLER_INVALID)
-        resample_method = s->core->resample_method;
-    
-    if (variable_rate || !pa_sample_spec_equal(spec, &s->sample_spec))
-        if (!(resampler = pa_resampler_new(spec, map, &s->sample_spec, &s->channel_map, s->core->memblock_stat, resample_method)))
-            return NULL;
-    
-    i = pa_xmalloc(sizeof(struct pa_sink_input));
-    i->ref = 1;
-    i->state = PA_SINK_INPUT_RUNNING;
-    i->name = pa_xstrdup(name);
-    i->driver = pa_xstrdup(driver);
-    i->client = NULL;
-    i->owner = NULL;
-    i->sink = s;
-
-    i->sample_spec = *spec;
-    if (map)
-        i->channel_map = *map;
-    else
-        pa_channel_map_init_auto(&i->channel_map, spec->channels);
-
-    i->peek = NULL;
-    i->drop = NULL;
-    i->kill = NULL;
-    i->get_latency = NULL;
-    i->userdata = NULL;
-    i->underrun = NULL;
-
-    pa_cvolume_reset(&i->volume);
-    i->playing = 0;
-
-    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_spec_snprint(st, sizeof(st), spec);
-    pa_log_info(__FILE__": created %u \"%s\" on %u with sample spec \"%s\"\n", i->index, i->name, s->index, st);
-
-    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
-    
-    return i;    
-}
-
-void pa_sink_input_disconnect(struct pa_sink_input *i) {
-    assert(i && i->state != PA_SINK_INPUT_DISCONNECTED && 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);
-
-    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
-    i->sink = NULL;
-
-    i->peek = NULL;
-    i->drop = NULL;
-    i->kill = NULL;
-    i->get_latency = NULL;
-
-    i->state = PA_SINK_INPUT_DISCONNECTED;
-}
-
-static void sink_input_free(struct pa_sink_input* i) {
-    assert(i);
-
-    if (i->state != PA_SINK_INPUT_DISCONNECTED)
-        pa_sink_input_disconnect(i);
-
-    pa_log_info(__FILE__": freed %u \"%s\"\n", i->index, i->name); 
-    
-    if (i->resampled_chunk.memblock)
-        pa_memblock_unref(i->resampled_chunk.memblock);
-    if (i->resampler)
-        pa_resampler_free(i->resampler);
-
-    pa_xfree(i->name);
-    pa_xfree(i->driver);
-    pa_xfree(i);
-}
-
-void pa_sink_input_unref(struct pa_sink_input *i) {
-    assert(i && i->ref >= 1);
-
-    if (!(--i->ref))
-        sink_input_free(i);
-}
-
-struct pa_sink_input* pa_sink_input_ref(struct pa_sink_input *i) {
-    assert(i && i->ref >= 1);
-    i->ref++;
-    return i;
-}
-
-void pa_sink_input_kill(struct pa_sink_input*i) {
-    assert(i && i->ref >= 1);
-
-    if (i->kill)
-        i->kill(i);
-}
-
-pa_usec_t pa_sink_input_get_latency(struct pa_sink_input *i) {
-    pa_usec_t r = 0;
-    assert(i && i->ref >= 1);
-    
-    if (i->get_latency)
-        r += i->get_latency(i);
-
-    if (i->resampled_chunk.memblock)
-        r += pa_bytes_to_usec(i->resampled_chunk.length, &i->sample_spec);
-
-    return r;
-}
-
-int pa_sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) {
-    int ret = -1;
-    assert(i && chunk && i->ref >= 1);
-
-    pa_sink_input_ref(i);
-
-    if (!i->peek || !i->drop || i->state == PA_SINK_INPUT_CORKED)
-        goto finish;
-        
-    if (!i->resampler) {
-        ret = i->peek(i, chunk);
-        goto finish;
-    }
-
-    while (!i->resampled_chunk.memblock) {
-        struct pa_memchunk tchunk;
-        size_t l;
-        
-        if ((ret = i->peek(i, &tchunk)) < 0)
-            goto finish;
-
-        assert(tchunk.length);
-        
-        l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH);
-
-        if (l > tchunk.length)
-            l = tchunk.length;
-
-        i->drop(i, &tchunk, l);
-        tchunk.length = l;
-
-        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);
-
-    ret = 0;
-
-finish:
-
-    if (ret < 0 && i->playing && i->underrun)
-        i->underrun(i);
-
-    i->playing = ret >= 0;
-    
-    pa_sink_input_unref(i);
-    
-    return ret;
-}
-
-void pa_sink_input_drop(struct pa_sink_input *i, const struct pa_memchunk *chunk, size_t length) {
-    assert(i && length && i->ref >= 1);
-
-    if (!i->resampler) {
-        if (i->drop)
-            i->drop(i, chunk, 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;
-    }
-}
-
-void pa_sink_input_set_volume(struct pa_sink_input *i, const struct pa_cvolume *volume) {
-    assert(i && i->sink && i->sink->core && i->ref >= 1);
-
-    if (!pa_cvolume_equal(&i->volume, volume)) {
-        i->volume = *volume;
-        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-    }
-}
-
-const struct pa_cvolume * pa_sink_input_get_volume(struct pa_sink_input *i) {
-    assert(i);
-    assert(i->ref >= 1);
-
-    return &i->volume;
-}
-
-void pa_sink_input_cork(struct pa_sink_input *i, int b) {
-    int n;
-    assert(i && i->ref >= 1);
-
-    if (i->state == PA_SINK_INPUT_DISCONNECTED)
-        return;
-
-    n = i->state == PA_SINK_INPUT_CORKED && !b;
-    i->state = b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
-
-    if (n)
-        pa_sink_notify(i->sink);
-}
-
-void pa_sink_input_set_rate(struct pa_sink_input *i, uint32_t rate) {
-    assert(i && i->resampler && i->ref >= 1);
-
-    if (i->sample_spec.rate == rate)
-        return;
-
-    i->sample_spec.rate = rate;
-    pa_resampler_set_input_rate(i->resampler, rate);
-}
-
-void pa_sink_input_set_name(struct pa_sink_input *i, const char *name) {
-    assert(i && i->ref >= 1);
-
-    pa_xfree(i->name);
-    i->name = pa_xstrdup(name);
-
-    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-}
-
-enum pa_resample_method pa_sink_input_get_resample_method(struct pa_sink_input *i) {
-    assert(i && i->ref >= 1);
-
-    if (!i->resampler)
-        return PA_RESAMPLER_INVALID;
-
-    return pa_resampler_get_method(i->resampler);
-}
diff --git a/polyp/sink-input.h b/polyp/sink-input.h
deleted file mode 100644 (file)
index 90723c0..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-#ifndef foosinkinputhfoo
-#define foosinkinputhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-
-#include "sink.h"
-#include "sample.h"
-#include "memblockq.h"
-#include "resampler.h"
-#include "module.h"
-#include "client.h"
-
-typedef enum {
-    PA_SINK_INPUT_RUNNING,
-    PA_SINK_INPUT_CORKED,
-    PA_SINK_INPUT_DISCONNECTED
-} pa_sink_input_state_t;
-
-struct pa_sink_input {
-    int ref;
-    uint32_t index;
-    pa_sink_input_state_t state;
-    
-    char *name, *driver;
-    struct pa_module *owner;
-    struct pa_client *client;
-    struct pa_sink *sink;
-    
-    struct pa_sample_spec sample_spec;
-    struct pa_channel_map channel_map;
-    struct pa_cvolume volume;
-    
-    int (*peek) (struct pa_sink_input *i, struct pa_memchunk *chunk);
-    void (*drop) (struct pa_sink_input *i, const struct pa_memchunk *chunk, size_t length);
-    void (*kill) (struct pa_sink_input *i);
-    pa_usec_t (*get_latency) (struct pa_sink_input *i);
-    void (*underrun) (struct pa_sink_input *i);
-
-    void *userdata;
-
-    int playing;
-
-    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 char *driver,
-    const struct pa_sample_spec *spec,
-    const struct pa_channel_map *map,
-    int variable_rate,
-    int resample_method);
-
-void pa_sink_input_unref(struct pa_sink_input* i);
-struct pa_sink_input* pa_sink_input_ref(struct pa_sink_input* i);
-
-/* To be called by the implementing module only */
-void pa_sink_input_disconnect(struct pa_sink_input* i);
-
-/* External code may request disconnection with this funcion */
-void pa_sink_input_kill(struct pa_sink_input*i);
-
-pa_usec_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, const struct pa_memchunk *chunk, size_t length);
-
-void pa_sink_input_set_volume(struct pa_sink_input *i, const struct pa_cvolume *volume);
-const struct pa_cvolume *volume pa_sink_input_get_volume(struct pa_sink_input *i);
-
-void pa_sink_input_cork(struct pa_sink_input *i, int b);
-
-void pa_sink_input_set_rate(struct pa_sink_input *i, uint32_t rate);
-
-void pa_sink_input_set_name(struct pa_sink_input *i, const char *name);
-
-enum pa_resample_method pa_sink_input_get_resample_method(struct pa_sink_input *i);
-
-#endif
diff --git a/polyp/sink.c b/polyp/sink.c
deleted file mode 100644 (file)
index 3b07472..0000000
+++ /dev/null
@@ -1,419 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "sink.h"
-#include "sink-input.h"
-#include "namereg.h"
-#include "util.h"
-#include "sample-util.h"
-#include "xmalloc.h"
-#include "subscribe.h"
-#include "log.h"
-
-#define MAX_MIX_CHANNELS 32
-
-struct pa_sink* pa_sink_new(
-    struct pa_core *core,
-    const char *name,
-    const char *driver,
-    int fail,
-    const struct pa_sample_spec *spec,
-    const struct pa_channel_map *map) {
-    
-    struct pa_sink *s;
-    char *n = NULL;
-    char st[256];
-    int r;
-
-    assert(core);
-    assert(name);
-    assert(*name);
-    assert(spec);
-
-    s = pa_xmalloc(sizeof(struct pa_sink));
-
-    if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) {
-        pa_xfree(s);
-        return NULL;
-    }
-
-    s->ref = 1;
-    s->core = core;
-
-    s->state = PA_SINK_RUNNING;
-    s->name = pa_xstrdup(name);
-    s->description = NULL;
-    s->driver = pa_xstrdup(driver);
-    s->owner = NULL;
-
-    s->sample_spec = *spec;
-    if (map)
-        s->channel_map = *map;
-    else
-        pa_channel_map_init_auto(&s->channel_map, spec->channels);
-    
-    s->inputs = pa_idxset_new(NULL, NULL);
-
-    n = pa_sprintf_malloc("%s_monitor", name);
-    s->monitor_source = pa_source_new(core, n, driver, 0, spec, map);
-    assert(s->monitor_source);
-    pa_xfree(n);
-    s->monitor_source->monitor_of = s;
-    s->monitor_source->description = pa_sprintf_malloc("Monitor source of sink '%s'", s->name);
-
-    pa_cvolume_reset(&s->sw_volume);
-    pa_cvolume_reset(&s->hw_volume);
-
-    s->notify = NULL;
-    s->get_latency = NULL;
-    s->set_volume = NULL;
-    s->get_volume = NULL;
-    s->userdata = NULL;
-
-    s->flags = 0;
-
-    r = pa_idxset_put(core->sinks, s, &s->index);
-    assert(s->index != PA_IDXSET_INVALID && r >= 0);
-    
-    pa_sample_spec_snprint(st, sizeof(st), spec);
-    pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st);
-
-    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
-    
-    return s;
-}
-
-void pa_sink_disconnect(struct pa_sink* s) {
-    struct pa_sink_input *i, *j = NULL;
-    assert(s && s->state == PA_SINK_RUNNING);
-
-    pa_namereg_unregister(s->core, s->name);
-    
-    while ((i = pa_idxset_first(s->inputs, NULL))) {
-        assert(i != j);
-        pa_sink_input_kill(i);
-        j = i;
-    }
-
-    pa_source_disconnect(s->monitor_source);
-
-    pa_idxset_remove_by_data(s->core->sinks, s, NULL);
-    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
-
-    s->notify = NULL;
-    s->get_latency = NULL;
-    
-    s->state = PA_SINK_DISCONNECTED;
-}
-
-static void sink_free(struct pa_sink *s) {
-    assert(s && s->ref == 0);
-    
-    if (s->state != PA_SINK_DISCONNECTED)
-        pa_sink_disconnect(s);
-
-    pa_log_info(__FILE__": freed %u \"%s\"\n", s->index, s->name); 
-
-    pa_source_unref(s->monitor_source);
-    s->monitor_source = NULL;
-    
-    pa_idxset_free(s->inputs, NULL, NULL);
-
-    pa_xfree(s->name);
-    pa_xfree(s->description);
-    pa_xfree(s->driver);
-    pa_xfree(s);
-}
-
-void pa_sink_unref(struct pa_sink*s) {
-    assert(s && s->ref >= 1);
-
-    if (!(--s->ref))
-        sink_free(s);
-}
-
-struct pa_sink* pa_sink_ref(struct pa_sink *s) {
-    assert(s && s->ref >= 1);
-    s->ref++;
-    return s;
-}
-
-void pa_sink_notify(struct pa_sink*s) {
-    assert(s && s->ref >= 1);
-
-    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 && s->ref >= 1 && info);
-
-    for (i = pa_idxset_first(s->inputs, &index); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &index)) {
-        pa_sink_input_ref(i);
-
-        if (pa_sink_input_peek(i, &info->chunk) < 0) {
-            pa_sink_input_unref(i);
-            continue;
-        }
-
-        info->volume = i->volume;
-        info->userdata = i;
-        
-        assert(info->chunk.memblock && info->chunk.memblock->data && info->chunk.length);
-        
-        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 && s->ref >= 1 && info);
-
-    for (; maxinfo > 0; maxinfo--, info++) {
-        struct pa_sink_input *i = info->userdata;
-        assert(i && info->chunk.memblock);
-        
-        pa_sink_input_drop(i, &info->chunk, length);
-        pa_memblock_unref(info->chunk.memblock);
-
-        pa_sink_input_unref(i);
-        info->userdata = NULL;
-    }
-}
-        
-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;
-    int r = -1;
-    assert(s);
-    assert(s->ref >= 1);
-    assert(length);
-    assert(result);
-
-    pa_sink_ref(s);
-    
-    n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
-
-    if (n <= 0)
-        goto finish;
-
-    if (n == 1) {
-        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, s->core->memblock_stat, 0);
-            pa_volume_memchunk(result, &s->sample_spec, volume);
-        }
-    } else {
-        result->memblock = pa_memblock_new(length, s->core->memblock_stat);
-        assert(result->memblock);
-
-        result->length = 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);
-
-    r = 0;
-
-finish:
-    pa_sink_unref(s);
-
-    return r;
-}
-
-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;
-    int r = -1;
-    assert(s && s->ref >= 1 && target && target->length && target->memblock && target->memblock->data);
-
-    pa_sink_ref(s);
-    
-    n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
-
-    if (n <= 0)
-        goto finish;
-
-    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((uint8_t*) target->memblock->data+target->index, (uint8_t*) 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, (uint8_t*) 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);
-
-    r = 0;
-
-finish:
-    pa_sink_unref(s);
-    
-    return r;
-}
-
-void pa_sink_render_into_full(struct pa_sink *s, struct pa_memchunk *target) {
-    struct pa_memchunk chunk;
-    size_t l, d;
-    assert(s && s->ref >= 1 && target && target->memblock && target->length && target->memblock->data);
-
-    pa_sink_ref(s);
-    
-    l = target->length;
-    d = 0;
-    while (l > 0) {
-        chunk = *target;
-        chunk.index += d;
-        chunk.length -= d;
-        
-        if (pa_sink_render_into(s, &chunk) < 0)
-            break;
-
-        d += chunk.length;
-        l -= chunk.length;
-    }
-
-    if (l > 0) {
-        chunk = *target;
-        chunk.index += d;
-        chunk.length -= d;
-        pa_silence_memchunk(&chunk, &s->sample_spec);
-    }
-
-    pa_sink_unref(s);
-}
-
-void pa_sink_render_full(struct pa_sink *s, size_t length, struct pa_memchunk *result) {
-    assert(s && s->ref >= 1 && length && result);
-
-    /*** This needs optimization ***/
-    
-    result->memblock = pa_memblock_new(result->length = length, s->core->memblock_stat);
-    result->index = 0;
-
-    pa_sink_render_into_full(s, result);
-}
-
-pa_usec_t pa_sink_get_latency(struct pa_sink *s) {
-    assert(s && s->ref >= 1);
-
-    if (!s->get_latency)
-        return 0;
-
-    return s->get_latency(s);
-}
-
-void pa_sink_set_owner(struct pa_sink *s, struct pa_module *m) {
-    assert(s && s->ref >= 1);
-           
-    s->owner = m;
-
-    if (s->monitor_source)
-        pa_source_set_owner(s->monitor_source, m);
-}
-
-void pa_sink_set_volume(struct pa_sink *s, pa_mixer_t m, const struct pa_cvolume *volume) {
-    struct pa_cvolume *v;
-    assert(s);
-    assert(s->ref >= 1);
-    assert(volume);
-
-    if ((m == PA_MIXER_HARDWARE || m == PA_MIXER_AUTO) && s->set_volume)
-        v = &s->hw_volume;
-    else
-        v = &s->sw_volume;
-
-    if (pa_cvolume_equal(v, volume))
-        return;
-
-    *v = volume;
-    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-
-    if (v == &s->hw_volume)
-        s->set_volume(s);
-}
-
-const struct pa_cvolume *pa_sink_get_volume(struct pa_sink *sink, pa_mixer_t m) {
-    struct pa_cvolume *v;
-    assert(s);
-    assert(s->ref >= 1);
-
-    if ((m == PA_MIXER_HARDWARE || m == PA_MIXER_AUTO) && s->set_volume) {
-
-        if (s->get_volume)
-            s->get_volume(s);
-        
-        return &s->hw_volume;
-    } else
-        return &s->sw_volume;
-}
diff --git a/polyp/sink.h b/polyp/sink.h
deleted file mode 100644 (file)
index a6d7efa..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#ifndef foosinkhfoo
-#define foosinkhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-struct pa_sink;
-
-#include <inttypes.h>
-
-#include "core.h"
-#include "sample.h"
-#include "idxset.h"
-#include "source.h"
-#include "channelmap.h"
-
-#define PA_MAX_INPUTS_PER_SINK 6
-
-typedef enum {
-    PA_SINK_RUNNING,
-    PA_SINK_DISCONNECTED
-} pa_sink_state_t;
-
-typedef enum {
-    PA_MIXER_AUTO,
-    PA_MIXER_SOFTWARE,
-    PA_MIXER_HARDWARE
-} pa_mixer_t;
-
-struct pa_sink {
-    int ref;
-    uint32_t index;
-    struct pa_core *core;
-    pa_sink_state_t state;
-
-    char *name, *description, *driver;
-    struct pa_sample_spec sample_spec;
-    struct pa_channel_map channel_map;
-    struct pa_idxset *inputs;
-    struct pa_module *owner;
-    struct pa_source *monitor_source;
-    struct pa_cvolume hw_volume, sw_volume;
-
-    void (*notify)(struct pa_sink*sink);
-    pa_usec_t (*get_latency)(struct pa_sink *s);
-    void (*set_volume)(struct pa_sink *s);
-    void (*get_volume)(struct pa_sink *s);
-    
-    void *userdata;
-};
-
-struct pa_sink* pa_sink_new(
-    struct pa_core *core,
-    const char *name,
-    const char *driver,
-    int fail,
-    const struct pa_sample_spec *spec,
-    const struct pa_channel_map *map);
-
-void pa_sink_disconnect(struct pa_sink* s);
-void pa_sink_unref(struct pa_sink*s);
-struct pa_sink* pa_sink_ref(struct pa_sink *s);
-
-int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result);
-void pa_sink_render_full(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);
-    
-pa_usec_t pa_sink_get_latency(struct pa_sink *s);
-
-void pa_sink_notify(struct pa_sink*s);
-
-void pa_sink_set_owner(struct pa_sink *sink, struct pa_module *m);
-
-void pa_sink_set_volume(struct pa_sink *sink, pa_mixer_t m, const struct pa_cvolume *volume);
-const struct pa_cvolume *pa_sink_get_volume(struct pa_sink *sink, pa_mixer_t m);
-
-#endif
diff --git a/polyp/socket-client.c b/polyp/socket-client.c
deleted file mode 100644 (file)
index 1bcf82e..0000000
+++ /dev/null
@@ -1,461 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-/* #undef HAVE_LIBASYNCNS */
-
-#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#ifdef HAVE_LIBASYNCNS
-#include <asyncns.h>
-#endif
-
-#include "socket-client.h"
-#include "socket-util.h"
-#include "util.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "parseaddr.h"
-
-#define CONNECT_TIMEOUT 5
-
-struct pa_socket_client {
-    int ref;
-    struct pa_mainloop_api *mainloop;
-    int fd;
-    struct pa_io_event *io_event;
-    struct pa_time_event *timeout_event;
-    struct pa_defer_event *defer_event;
-    void (*callback)(struct pa_socket_client*c, struct pa_iochannel *io, void *userdata);
-    void *userdata;
-    int local;
-#ifdef HAVE_LIBASYNCNS
-    asyncns_t *asyncns;
-    asyncns_query_t * asyncns_query;
-    struct pa_io_event *asyncns_io_event;
-#endif
-};
-
-static struct pa_socket_client*pa_socket_client_new(struct pa_mainloop_api *m) {
-    struct pa_socket_client *c;
-    assert(m);
-
-    c = pa_xmalloc(sizeof(struct pa_socket_client));
-    c->ref = 1;
-    c->mainloop = m;
-    c->fd = -1;
-    c->io_event = NULL;
-    c->defer_event = NULL;
-    c->timeout_event = NULL;
-    c->callback = NULL;
-    c->userdata = NULL;
-    c->local = 0;
-
-#ifdef HAVE_LIBASYNCNS
-    c->asyncns = NULL;
-    c->asyncns_io_event = NULL;
-    c->asyncns_query = NULL;
-#endif
-
-    return c;
-}
-
-static void free_events(struct pa_socket_client *c) {
-    assert(c);
-    
-    if (c->io_event) {
-        c->mainloop->io_free(c->io_event);
-        c->io_event = NULL;
-    }
-    
-    if (c->defer_event) {
-        c->mainloop->defer_free(c->defer_event);
-        c->defer_event = NULL;
-    }
-    
-    if (c->timeout_event) {
-        c->mainloop->time_free(c->timeout_event);
-        c->timeout_event = NULL;
-    }
-}
-
-static void do_call(struct pa_socket_client *c) {
-    struct pa_iochannel *io = NULL;
-    int error;
-    socklen_t lerror;
-    assert(c && c->callback);
-
-    pa_socket_client_ref(c);
-
-    if (c->fd < 0)
-        goto finish;
-    
-    lerror = sizeof(error);
-    if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &error, &lerror) < 0) {
-        pa_log(__FILE__": getsockopt(): %s\n", strerror(errno));
-        goto finish;
-    }
-
-    if (lerror != sizeof(error)) {
-        pa_log(__FILE__": getsockopt() returned invalid size.\n");
-        goto finish;
-    }
-
-    if (error != 0) {
-        pa_log_debug(__FILE__": connect(): %s\n", strerror(error)); 
-        errno = error;
-        goto finish;
-    }
-        
-    io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
-    assert(io);
-    
-finish:
-    if (!io && c->fd >= 0)
-        close(c->fd);
-    c->fd = -1;
-
-    free_events(c);
-    
-    assert(c->callback);
-    c->callback(c, io, c->userdata);
-    
-    pa_socket_client_unref(c);
-}
-
-static void connect_fixed_cb(struct pa_mainloop_api *m, struct pa_defer_event *e, void *userdata) {
-    struct pa_socket_client *c = userdata;
-    assert(m && c && c->defer_event == e);
-    do_call(c);
-}
-
-static void connect_io_cb(struct pa_mainloop_api*m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    struct pa_socket_client *c = userdata;
-    assert(m && c && c->io_event == e && fd >= 0);
-    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) {
-            /*pa_log(__FILE__": connect(): %s\n", strerror(errno));*/
-            return -1;
-        }
-
-        c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c);
-        assert(c->io_event);
-    } else {
-        c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c);
-        assert(c->defer_event);
-    }
-
-    return 0;
-}
-
-struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) {
-    struct sockaddr_in sa;
-    assert(m && port > 0);
-
-    memset(&sa, 0, sizeof(sa));
-    sa.sin_family = AF_INET;
-    sa.sin_port = htons(port);
-    sa.sin_addr.s_addr = htonl(address);
-
-    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
-}
-
-struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename) {
-    struct sockaddr_un sa;
-    assert(m && filename);
-    
-    memset(&sa, 0, sizeof(sa));
-    sa.sun_family = AF_LOCAL;
-    strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
-    sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
-
-    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
-}
-
-static int sockaddr_prepare(struct pa_socket_client *c, const struct sockaddr *sa, size_t salen) {
-    assert(c);
-    assert(sa);
-    assert(salen);
-    
-    switch (sa->sa_family) {
-        case AF_UNIX:
-            c->local = 1;
-            break;
-            
-        case AF_INET:
-            c->local = ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
-            break;
-            
-        case AF_INET6:
-            c->local = memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
-            break;
-            
-        default:
-            c->local = 0;
-    }
-    
-    if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
-        pa_log(__FILE__": socket(): %s\n", strerror(errno));
-        return -1;
-    }
-
-    pa_fd_set_cloexec(c->fd, 1);
-    if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
-        pa_socket_tcp_low_delay(c->fd);
-    else
-        pa_socket_low_delay(c->fd);
-
-    if (do_connect(c, sa, salen) < 0)
-        return -1;
-
-    return 0;
-}
-
-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 (sockaddr_prepare(c, sa, salen) < 0)
-        goto fail;
-    
-    return c;
-
-fail:
-    pa_socket_client_unref(c);
-    return NULL;
-    
-}
-
-void socket_client_free(struct pa_socket_client *c) {
-    assert(c && c->mainloop);
-
-
-    free_events(c);
-    
-    if (c->fd >= 0)
-        close(c->fd);
-
-#ifdef HAVE_LIBASYNCNS
-    if (c->asyncns_query)
-        asyncns_cancel(c->asyncns, c->asyncns_query);
-    if (c->asyncns)
-        asyncns_free(c->asyncns);
-    if (c->asyncns_io_event)
-        c->mainloop->io_free(c->asyncns_io_event);
-#endif
-    
-    pa_xfree(c);
-}
-
-void pa_socket_client_unref(struct pa_socket_client *c) {
-    assert(c && c->ref >= 1);
-
-    if (!(--(c->ref)))
-        socket_client_free(c);
-}
-
-struct pa_socket_client* pa_socket_client_ref(struct pa_socket_client *c) {
-    assert(c && c->ref >= 1);
-    c->ref++;
-    return 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;
-}
-
-struct pa_socket_client* pa_socket_client_new_ipv6(struct pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
-    struct sockaddr_in6 sa;
-    
-    memset(&sa, 0, sizeof(sa));
-    sa.sin6_family = AF_INET6;
-    sa.sin6_port = htons(port);
-    memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr));
-
-    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
-}
-
-#ifdef HAVE_LIBASYNCNS
-
-static void asyncns_cb(struct pa_mainloop_api*m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    struct pa_socket_client *c = userdata;
-    struct addrinfo *res = NULL;
-    int ret;
-    assert(m && c && c->asyncns_io_event == e && fd >= 0);
-
-    if (asyncns_wait(c->asyncns, 0) < 0)
-        goto fail;
-
-    if (!asyncns_isdone(c->asyncns, c->asyncns_query))
-        return;
-
-    ret = asyncns_getaddrinfo_done(c->asyncns, c->asyncns_query, &res);
-    c->asyncns_query = NULL;
-
-    if (ret != 0 || !res)
-        goto fail;
-    
-    if (res->ai_addr)
-        sockaddr_prepare(c, res->ai_addr, res->ai_addrlen);
-    
-    asyncns_freeaddrinfo(res);
-
-    goto finish;
-
-fail:
-    errno == EHOSTUNREACH;
-    do_call(c);
-    
-finish:
-    
-    m->io_free(c->asyncns_io_event);
-    c->asyncns_io_event = NULL;
-}
-
-#endif
-
-static void timeout_cb(struct pa_mainloop_api *m, struct pa_time_event *e, const struct timeval *tv, void *userdata) {
-    struct pa_socket_client *c = userdata;
-    assert(m);
-    assert(e);
-    assert(tv);
-    assert(c);
-
-    if (c->fd >= 0) {
-        close(c->fd);
-        c->fd = -1;
-    }
-
-    errno = ETIMEDOUT;
-    do_call(c);
-}
-
-static void start_timeout(struct pa_socket_client *c) {
-    struct timeval tv;
-    assert(c);
-    assert(!c->timeout_event);
-
-    gettimeofday(&tv, NULL);
-    pa_timeval_add(&tv, CONNECT_TIMEOUT * 1000000);
-    c->timeout_event = c->mainloop->time_new(c->mainloop, &tv, timeout_cb, c);
-}
-
-struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m, const char*name, uint16_t default_port) {
-    struct pa_socket_client *c = NULL;
-    struct pa_parsed_address a;
-    assert(m && name);
-
-    if (pa_parse_address(name, &a) < 0)
-        return NULL;
-
-    if (!a.port)
-        a.port = default_port;
-    
-    switch (a.type) {
-        case PA_PARSED_ADDRESS_UNIX:
-            if ((c = pa_socket_client_new_unix(m, a.path_or_host)))
-               start_timeout(c);
-            break;
-
-        case PA_PARSED_ADDRESS_TCP4:  /* Fallthrough */
-        case PA_PARSED_ADDRESS_TCP6:  /* Fallthrough */
-        case PA_PARSED_ADDRESS_TCP_AUTO:{
-
-            struct addrinfo hints;
-            char port[12];
-
-            snprintf(port, sizeof(port), "%u", (unsigned) a.port);
-
-            memset(&hints, 0, sizeof(hints));
-            hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC);
-            hints.ai_socktype = SOCK_STREAM;
-            
-#ifdef HAVE_LIBASYNCNS
-            {
-                asyncns_t *asyncns;
-                
-                if (!(asyncns = asyncns_new(1)))
-                    goto finish;
-
-                c = pa_socket_client_new(m);
-                c->asyncns = asyncns;
-                c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);
-                c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints);
-                assert(c->asyncns_query);
-                start_timeout(c);
-            }
-#else
-            {
-                int ret;
-                struct addrinfo *res = NULL;
-
-                ret = getaddrinfo(a.path_or_host, port, &hints, &res);
-                
-                if (ret < 0 || !res)
-                    goto finish;
-
-                if (res->ai_addr) {
-                    if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen)))
-                       tart_timeout(c);
-                
-                freeaddrinfo(res);
-            }
-#endif            
-        }
-    }
-
-finish:
-    pa_xfree(a.path_or_host);
-    return c;
-    
-}
-
-/* Return non-zero when the target sockaddr is considered
-   local. "local" means UNIX socket or TCP socket on localhost. Other
-   local IP addresses are not considered local. */
-int pa_socket_client_is_local(struct pa_socket_client *c) {
-    assert(c);
-    return c->local;
-}
diff --git a/polyp/socket-client.h b/polyp/socket-client.h
deleted file mode 100644 (file)
index 9c3e0b3..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef foosocketclienthfoo
-#define foosocketclienthfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include <sys/socket.h>
-
-#include "mainloop-api.h"
-#include "iochannel.h"
-
-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_ipv6(struct pa_mainloop_api *m, uint8_t address[16], 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);
-struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m, const char *a, uint16_t default_port);
-
-void pa_socket_client_unref(struct pa_socket_client *c);
-struct pa_socket_client* pa_socket_client_ref(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);
-
-int pa_socket_client_is_local(struct pa_socket_client *c);
-
-#endif
diff --git a/polyp/socket-server.c b/polyp/socket-server.c
deleted file mode 100644 (file)
index e67a9da..0000000
+++ /dev/null
@@ -1,403 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#ifdef HAVE_LIBWRAP
-#include <tcpd.h>
-#endif
-
-#include "socket-server.h"
-#include "socket-util.h"
-#include "xmalloc.h"
-#include "util.h"
-#include "log.h"
-
-struct pa_socket_server {
-    int ref;
-    int fd;
-    char *filename;
-    char *tcpwrap_service;
-
-    void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata);
-    void *userdata;
-
-    struct pa_io_event *io_event;
-    struct pa_mainloop_api *mainloop;
-    enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type;
-};
-
-static void callback(struct pa_mainloop_api *mainloop, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    struct pa_socket_server *s = userdata;
-    struct pa_iochannel *io;
-    int nfd;
-    assert(s && s->mainloop == mainloop && s->io_event == e && e && fd >= 0 && fd == s->fd);
-
-    pa_socket_server_ref(s);
-    
-    if ((nfd = accept(fd, NULL, NULL)) < 0) {
-        pa_log(__FILE__": accept(): %s\n", strerror(errno));
-        goto finish;
-    }
-
-    pa_fd_set_cloexec(nfd, 1);
-    
-    if (!s->on_connection) {
-        close(nfd);
-        goto finish;
-    }
-
-#ifdef HAVE_LIBWRAP
-
-    if (s->type == SOCKET_SERVER_IPV4 && s->tcpwrap_service) {
-        struct request_info req;
-
-        request_init(&req, RQ_DAEMON, s->tcpwrap_service, RQ_FILE, nfd, NULL);
-        fromhost(&req);
-        if (!hosts_access(&req)) {
-            pa_log(__FILE__": TCP connection refused by tcpwrap.\n");
-            close(nfd);
-            goto finish;
-        }
-
-        pa_log(__FILE__": TCP connection accepted by tcpwrap.\n");
-    }
-#endif
-    
-    /* There should be a check for socket type here */
-    if (s->type == SOCKET_SERVER_IPV4) 
-        pa_socket_tcp_low_delay(fd);
-    else
-        pa_socket_low_delay(fd);
-    
-    io = pa_iochannel_new(s->mainloop, nfd, nfd);
-    assert(io);
-    s->on_connection(s, io, s->userdata);
-
-finish:
-    pa_socket_server_unref(s);
-}
-
-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 = pa_xmalloc(sizeof(struct pa_socket_server));
-    s->ref = 1;
-    s->fd = fd;
-    s->filename = NULL;
-    s->on_connection = NULL;
-    s->userdata = NULL;
-    s->tcpwrap_service = NULL;
-
-    s->mainloop = m;
-    s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s);
-    assert(s->io_event);
-
-    s->type = SOCKET_SERVER_GENERIC;
-    
-    return s;
-}
-
-struct pa_socket_server* pa_socket_server_ref(struct pa_socket_server *s) {
-    assert(s && s->ref >= 1);
-    s->ref++;
-    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) {
-        pa_log(__FILE__": socket(): %s\n", strerror(errno));
-        goto fail;
-    }
-
-    pa_fd_set_cloexec(fd, 1);
-
-    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) {
-        pa_log(__FILE__": bind(): %s\n", strerror(errno));
-        goto fail;
-    }
-
-    if (listen(fd, 5) < 0) {
-        pa_log(__FILE__": listen(): %s\n", strerror(errno));
-        goto fail;
-    }
-
-    s = pa_socket_server_new(m, fd);
-    assert(s);
-
-    s->filename = pa_xstrdup(filename);
-    s->type = SOCKET_SERVER_UNIX;
-    
-    return s;
-                                                                                                                                                                         
-fail:
-    if (fd >= 0)
-        close(fd);
-
-    return NULL;
-}
-
-struct pa_socket_server* pa_socket_server_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service) {
-    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) {
-        pa_log(__FILE__": socket(): %s\n", strerror(errno));
-        goto fail;
-    }
-
-    pa_fd_set_cloexec(fd, 1);
-
-    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
-        pa_log(__FILE__": setsockopt(): %s\n", strerror(errno));
-
-    pa_socket_tcp_low_delay(fd);
-    
-    memset(&sa, 0, sizeof(sa));
-    sa.sin_family = AF_INET;
-    sa.sin_port = htons(port);
-    sa.sin_addr.s_addr = htonl(address);
-
-    if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
-        pa_log(__FILE__": bind(): %s\n", strerror(errno));
-        goto fail;
-    }
-
-    if (listen(fd, 5) < 0) {
-        pa_log(__FILE__": listen(): %s\n", strerror(errno));
-        goto fail;
-    }
-
-    if ((ss = pa_socket_server_new(m, fd))) {
-        ss->type = SOCKET_SERVER_IPV4;
-        ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
-    }
-
-    return ss;
-    
-fail:
-    if (fd >= 0)
-        close(fd);
-
-    return NULL;
-}
-
-struct pa_socket_server* pa_socket_server_new_ipv6(struct pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
-    struct pa_socket_server *ss;
-    int fd = -1;
-    struct sockaddr_in6 sa;
-    int on = 1;
-
-    assert(m && port);
-
-    if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
-        pa_log(__FILE__": socket(): %s\n", strerror(errno));
-        goto fail;
-    }
-
-    pa_fd_set_cloexec(fd, 1);
-
-    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
-        pa_log(__FILE__": setsockopt(): %s\n", strerror(errno));
-
-    pa_socket_tcp_low_delay(fd);
-
-    memset(&sa, 0, sizeof(sa));
-    sa.sin6_family = AF_INET6;
-    sa.sin6_port = htons(port);
-    memcpy(sa.sin6_addr.s6_addr, address, 16);
-
-    if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
-        pa_log(__FILE__": bind(): %s\n", strerror(errno));
-        goto fail;
-    }
-
-    if (listen(fd, 5) < 0) {
-        pa_log(__FILE__": listen(): %s\n", strerror(errno));
-        goto fail;
-    }
-
-    if ((ss = pa_socket_server_new(m, fd)))
-        ss->type = SOCKET_SERVER_IPV6;
-
-    return ss;
-    
-fail:
-    if (fd >= 0)
-        close(fd);
-
-    return NULL;
-}
-
-static void socket_server_free(struct pa_socket_server*s) {
-    assert(s);
-    close(s->fd);
-
-    if (s->filename) {
-        unlink(s->filename);
-        pa_xfree(s->filename);
-    }
-
-    pa_xfree(s->tcpwrap_service);
-
-    s->mainloop->io_free(s->io_event);
-    pa_xfree(s);
-}
-
-void pa_socket_server_unref(struct pa_socket_server *s) {
-    assert(s && s->ref >= 1);
-
-    if (!(--(s->ref)))
-        socket_server_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->ref >= 1);
-
-    s->on_connection = on_connection;
-    s->userdata = userdata;
-}
-
-
-char *pa_socket_server_get_address(struct pa_socket_server *s, char *c, size_t l) {
-    assert(s && c && l > 0);
-    
-    switch (s->type) {
-        case SOCKET_SERVER_IPV6: {
-            struct sockaddr_in6 sa;
-            socklen_t l = sizeof(sa);
-
-            if (getsockname(s->fd, (struct sockaddr*) &sa, &l) < 0) {
-                pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno));
-                return NULL;
-            }
-
-            if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) {
-                char fqdn[256];
-                if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
-                    return NULL;
-                
-                snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
-                
-            } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
-                char hn[256];
-                if (!pa_get_host_name(hn, sizeof(hn)))
-                    return NULL;
-                
-                snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
-            } else {
-                char ip[INET6_ADDRSTRLEN];
-                
-                if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) {
-                    pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno));
-                    return NULL;
-                }
-                
-                snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
-            }
-
-            return c;
-        }
-
-        case SOCKET_SERVER_IPV4: {
-            struct sockaddr_in sa;
-            socklen_t l = sizeof(sa);
-
-            if (getsockname(s->fd, &sa, &l) < 0) {
-                pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno));
-                return NULL;
-            }
-
-            if (sa.sin_addr.s_addr == INADDR_ANY) {
-                char fqdn[256];
-                if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
-                    return NULL;
-                
-                snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
-            } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
-                char hn[256];
-                if (!pa_get_host_name(hn, sizeof(hn)))
-                    return NULL;
-                
-                snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
-            } else {
-                char ip[INET_ADDRSTRLEN];
-
-                if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) {
-                    pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno));
-                    return NULL;
-                }
-                
-                snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
-
-            }
-            
-            return c;
-        }
-
-        case SOCKET_SERVER_UNIX: {
-            char hn[256];
-
-            if (!s->filename)
-                return NULL;
-            
-            if (!pa_get_host_name(hn, sizeof(hn)))
-                return NULL;
-
-            snprintf(c, l, "{%s}unix:%s", hn, s->filename);
-            return c;
-        }
-
-        default:
-            return NULL;
-    }
-}
diff --git a/polyp/socket-server.h b/polyp/socket-server.h
deleted file mode 100644 (file)
index dbd8251..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef foosocketserverhfoo
-#define foosocketserverhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include "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, const char *tcpwrap_service);
-struct pa_socket_server* pa_socket_server_new_ipv6(struct pa_mainloop_api *m, uint8_t address[16], uint16_t port);
-
-void pa_socket_server_unref(struct pa_socket_server*s);
-struct pa_socket_server* pa_socket_server_ref(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);
-
-char *pa_socket_server_get_address(struct pa_socket_server *s, char *c, size_t l);
-
-#endif
diff --git a/polyp/socket-util.c b/polyp/socket-util.c
deleted file mode 100644 (file)
index 495ee1b..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <errno.h>
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <netinet/in_systm.h>
-#include <netinet/tcp.h>
-#include <netinet/ip.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <netdb.h>
-
-#include "socket-util.h"
-#include "util.h"
-#include "xmalloc.h"
-#include "log.h"
-
-void pa_socket_peer_to_string(int fd, char *c, size_t l) {
-    struct stat st;
-
-    assert(c && l && fd >= 0);
-    
-    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);
-
-#ifdef SO_PRIORITY
-    priority = 7;
-    if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0)
-        return -1;
-#endif
-
-    return 0;
-}
-
-int pa_socket_tcp_low_delay(int fd) {
-    int ret, tos, on;
-
-    assert(fd >= 0);
-
-    ret = pa_socket_low_delay(fd);
-    
-    on = 1;
-
-#if defined(SOL_TCP) || defined(IPPROTO_TCP)
-#if defined(SOL_TCP)
-    if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
-#else
-    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
-#endif
-        ret = -1;
-#endif
-
-#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || \
-       defined(IPPROTO_IP))
-    tos = IPTOS_LOWDELAY;
-#ifdef SOL_IP
-    if (setsockopt(fd, SOL_IP, IP_TOS, &tos, sizeof(tos)) < 0)
-#else
-    if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0)
-#endif
-        ret = -1;
-#endif
-
-    return ret;
-
-}
-
-int pa_socket_set_rcvbuf(int fd, size_t l) {
-    assert(fd >= 0);
-
-/*     if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &l, sizeof(l)) < 0) { */
-/*         pa_log(__FILE__": SO_RCVBUF: %s\n", strerror(errno)); */
-/*         return -1; */
-/*     } */
-
-    return 0;
-}
-
-int pa_socket_set_sndbuf(int fd, size_t l) {
-    assert(fd >= 0);
-
-/*     if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &l, sizeof(l)) < 0) { */
-/*         pa_log(__FILE__": SO_SNDBUF: %s\n", strerror(errno)); */
-/*         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) {
-        pa_log(__FILE__": 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;
-}
diff --git a/polyp/sound-file-stream.c b/polyp/sound-file-stream.c
deleted file mode 100644 (file)
index 621c3ed..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <sndfile.h>
-
-#include "sound-file-stream.h"
-#include "sink-input.h"
-#include "xmalloc.h"
-#include "log.h"
-
-#define BUF_SIZE (1024*10)
-#define PA_TYPEID_SOUND_FILE PA_TYPEID_MAKE('S', 'N', 'D', 'F')
-
-struct userdata {
-    SNDFILE *sndfile;
-    struct pa_sink_input *sink_input;
-    struct pa_memchunk memchunk;
-    sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
-};
-
-static void free_userdata(struct userdata *u) {
-    assert(u);
-    if (u->sink_input) {
-        pa_sink_input_disconnect(u->sink_input);
-        pa_sink_input_unref(u->sink_input);
-    }
-    
-    if (u->memchunk.memblock)
-        pa_memblock_unref(u->memchunk.memblock);
-    if (u->sndfile)
-        sf_close(u->sndfile);
-
-    pa_xfree(u);
-}
-
-static void sink_input_kill(struct pa_sink_input *i) {
-    assert(i && i->userdata);
-    free_userdata(i->userdata);
-}
-
-static void si_kill(struct pa_mainloop_api *m, void *i) {
-    sink_input_kill(i);
-}
-
-static int sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) {
-    struct userdata *u;
-    assert(i && chunk && i->userdata);
-    u = i->userdata;
-
-    if (!u->memchunk.memblock) {
-        uint32_t fs = pa_frame_size(&i->sample_spec);
-        sf_count_t samples = BUF_SIZE/fs;
-
-        u->memchunk.memblock = pa_memblock_new(BUF_SIZE, i->sink->core->memblock_stat);
-        u->memchunk.index = 0;
-        samples = u->readf_function(u->sndfile, u->memchunk.memblock->data, samples);
-        u->memchunk.length = samples*fs;
-        
-        if (!u->memchunk.length) {
-            pa_memblock_unref(u->memchunk.memblock);
-            u->memchunk.memblock = NULL;
-            u->memchunk.index = u->memchunk.length = 0;
-            pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i);
-            return -1;
-        }
-    }
-
-    *chunk = u->memchunk;
-    pa_memblock_ref(chunk->memblock);
-    assert(chunk->length);
-    return 0;
-}
-
-static void sink_input_drop(struct pa_sink_input *i, const struct pa_memchunk*chunk, size_t length) {
-    struct userdata *u;
-    assert(i && chunk && length && i->userdata);
-    u = i->userdata;
-
-    assert(!memcmp(chunk, &u->memchunk, sizeof(chunk)));
-    assert(length <= u->memchunk.length);
-
-    u->memchunk.index += length;
-    u->memchunk.length -= length;
-
-    if (u->memchunk.length <= 0) {
-        pa_memblock_unref(u->memchunk.memblock);
-        u->memchunk.memblock = NULL;
-        u->memchunk.index = u->memchunk.length = 0;
-    }
-}
-
-int pa_play_file(struct pa_sink *sink, const char *fname, pa_volume_t volume) {
-    struct userdata *u = NULL;
-    SF_INFO sfinfo;
-    struct pa_sample_spec ss;
-    assert(sink && fname);
-
-    if (volume <= 0)
-        goto fail;
-
-    u = pa_xmalloc(sizeof(struct userdata));
-    u->sink_input = NULL;
-    u->memchunk.memblock = NULL;
-    u->memchunk.index = u->memchunk.length = 0;
-    u->sndfile = NULL;
-
-    memset(&sfinfo, 0, sizeof(sfinfo));
-
-    if (!(u->sndfile = sf_open(fname, SFM_READ, &sfinfo))) {
-        pa_log(__FILE__": Failed to open file %s\n", fname);
-        goto fail;
-    }
-
-    switch (sfinfo.format & 0xFF) {
-        case SF_FORMAT_PCM_16:
-        case SF_FORMAT_PCM_U8:
-        case SF_FORMAT_ULAW:
-        case SF_FORMAT_ALAW:
-            ss.format = PA_SAMPLE_S16NE;
-            u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
-            break;
-        case SF_FORMAT_FLOAT:
-        default:
-            ss.format = PA_SAMPLE_FLOAT32NE;
-            u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
-            break;
-    }
-            
-    ss.rate = sfinfo.samplerate;
-    ss.channels = sfinfo.channels;
-
-    if (!pa_sample_spec_valid(&ss)) {
-        pa_log(__FILE__": Unsupported sample format in file %s\n", fname);
-        goto fail;
-    }
-    
-    if (!(u->sink_input = pa_sink_input_new(sink, PA_TYPEID_SOUND_FILE, fname, &ss, 0, -1)))
-        goto fail;
-
-    u->sink_input->volume = volume;
-    u->sink_input->peek = sink_input_peek;
-    u->sink_input->drop = sink_input_drop;
-    u->sink_input->kill = sink_input_kill;
-    u->sink_input->userdata = u;
-    
-    pa_sink_notify(sink);
-
-    return 0;
-
-fail:
-    if (u)
-        free_userdata(u);
-    
-    return -1;
-}
diff --git a/polyp/sound-file.c b/polyp/sound-file.c
deleted file mode 100644 (file)
index 80233da..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <assert.h>
-
-#include <sndfile.h>
-
-#include "sound-file.h"
-#include "sample.h"
-#include "log.h"
-
-#define MAX_FILE_SIZE (1024*1024)
-
-int pa_sound_file_load(const char *fname, struct pa_sample_spec *ss, struct pa_memchunk *chunk, struct pa_memblock_stat *s) {
-    SNDFILE*sf = NULL;
-    SF_INFO sfinfo;
-    int ret = -1;
-    size_t l;
-    sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
-    assert(fname && ss && chunk);
-
-    chunk->memblock = NULL;
-    chunk->index = chunk->length = 0;
-
-    memset(&sfinfo, 0, sizeof(sfinfo));
-
-    if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
-        pa_log(__FILE__": Failed to open file %s\n", fname);
-        goto finish;
-    }
-
-    switch (sfinfo.format & 0xFF) {
-        case SF_FORMAT_PCM_16:
-        case SF_FORMAT_PCM_U8:
-        case SF_FORMAT_ULAW:
-        case SF_FORMAT_ALAW:
-            ss->format = PA_SAMPLE_S16NE;
-            readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
-            break;
-        case SF_FORMAT_FLOAT:
-        default:
-            ss->format = PA_SAMPLE_FLOAT32NE;
-            readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
-            break;
-    }
-
-    ss->rate = sfinfo.samplerate;
-    ss->channels = sfinfo.channels;
-
-    if (!pa_sample_spec_valid(ss)) {
-        pa_log(__FILE__": Unsupported sample format in file %s\n", fname);
-        goto finish;
-    }
-    
-    if ((l = pa_frame_size(ss)*sfinfo.frames) > MAX_FILE_SIZE) {
-        pa_log(__FILE__": File to large\n");
-        goto finish;
-    }
-
-    chunk->memblock = pa_memblock_new(l, s);
-    assert(chunk->memblock);
-    chunk->index = 0;
-    chunk->length = l;
-
-    if (readf_function(sf, chunk->memblock->data, sfinfo.frames) != sfinfo.frames) {
-        pa_log(__FILE__": Premature file end\n");
-        goto finish;
-    }
-
-    ret = 0;
-
-finish:
-
-    if (sf)
-        sf_close(sf);
-
-    if (ret != 0 && chunk->memblock)
-        pa_memblock_unref(chunk->memblock);
-    
-    return ret;
-    
-}
diff --git a/polyp/source-output.c b/polyp/source-output.c
deleted file mode 100644 (file)
index fa9f252..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "source-output.h"
-#include "xmalloc.h"
-#include "subscribe.h"
-#include "log.h"
-
-struct pa_source_output* pa_source_output_new(
-    struct pa_source *s,
-    const char *name,
-    const char *driver,
-    const struct pa_sample_spec *spec,
-    const struct pa_channel_map *map,
-    int resample_method) {
-    
-    struct pa_source_output *o;
-    struct pa_resampler *resampler = NULL;
-    int r;
-    char st[256];
-    assert(s && spec);
-
-    if (pa_idxset_ncontents(s->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
-        pa_log(__FILE__": Failed to create source output: too many outputs per source.\n");
-        return NULL;
-    }
-
-    if (resample_method == PA_RESAMPLER_INVALID)
-        resample_method = s->core->resample_method;
-
-    if (!pa_sample_spec_equal(&s->sample_spec, spec))
-        if (!(resampler = pa_resampler_new(&s->sample_spec, &s->channel_map, spec, map, s->core->memblock_stat, resample_method)))
-            return NULL;
-    
-    o = pa_xmalloc(sizeof(struct pa_source_output));
-    o->ref = 1;
-    o->state = PA_SOURCE_OUTPUT_RUNNING;
-    o->name = pa_xstrdup(name);
-    o->driver = pa_xstrdup(driver);
-    
-    o->client = NULL;
-    o->owner = NULL;
-    o->source = s;
-    
-    o->sample_spec = *spec;
-    if (map)
-        c->channel_map = *map;
-    else
-        pa_channel_map_init_auto(&c->channel_map, spec->channels);
-
-    o->push = NULL;
-    o->kill = NULL;
-    o->userdata = NULL;
-    o->get_latency = NULL;
-    
-    o->resampler = resampler;
-    
-    assert(s->core);
-    r = pa_idxset_put(s->core->source_outputs, o, &o->index);
-    assert(r == 0 && o->index != PA_IDXSET_INVALID);
-    r = pa_idxset_put(s->outputs, o, NULL);
-    assert(r == 0);
-
-    pa_sample_spec_snprint(st, sizeof(st), spec);
-    pa_log_info(__FILE__": created %u \"%s\" on %u with sample spec \"%s\"\n", o->index, o->name, s->index, st);
-    
-    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
-    
-    return o;    
-}
-
-void pa_source_output_disconnect(struct pa_source_output*o) {
-    assert(o && o->state != PA_SOURCE_OUTPUT_DISCONNECTED && 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);
-
-    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
-    o->source = NULL;
-
-    o->push = NULL;
-    o->kill = NULL;
-    
-    o->state = PA_SOURCE_OUTPUT_DISCONNECTED;
-}
-
-static void source_output_free(struct pa_source_output* o) {
-    assert(o);
-
-    if (o->state != PA_SOURCE_OUTPUT_DISCONNECTED)
-        pa_source_output_disconnect(o);
-
-    pa_log_info(__FILE__": freed %u \"%s\"\n", o->index, o->name); 
-    
-    if (o->resampler)
-        pa_resampler_free(o->resampler);
-
-    pa_xfree(o->name);
-    pa_xfree(o->driver);
-    pa_xfree(o);
-}
-
-
-void pa_source_output_unref(struct pa_source_output* o) {
-    assert(o && o->ref >= 1);
-
-    if (!(--o->ref))
-        source_output_free(o);
-}
-
-struct pa_source_output* pa_source_output_ref(struct pa_source_output *o) {
-    assert(o && o->ref >= 1);
-    o->ref++;
-    return o;
-}
-
-
-void pa_source_output_kill(struct pa_source_output*o) {
-    assert(o && o->ref >= 1);
-
-    if (o->kill)
-        o->kill(o);
-}
-
-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->state == PA_SOURCE_OUTPUT_CORKED)
-        return;
-    
-    if (!o->resampler) {
-        o->push(o, chunk);
-        return;
-    }
-
-    pa_resampler_run(o->resampler, chunk, &rchunk);
-    if (!rchunk.length)
-        return;
-    
-    assert(rchunk.memblock);
-    o->push(o, &rchunk);
-    pa_memblock_unref(rchunk.memblock);
-}
-
-void pa_source_output_set_name(struct pa_source_output *o, const char *name) {
-    assert(o && o->ref >= 1);
-    pa_xfree(o->name);
-    o->name = pa_xstrdup(name);
-
-    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-}
-
-pa_usec_t pa_source_output_get_latency(struct pa_source_output *o) {
-    assert(o && o->ref >= 1);
-    
-    if (o->get_latency)
-        return o->get_latency(o);
-
-    return 0;
-}
-
-void pa_source_output_cork(struct pa_source_output *o, int b) {
-    assert(o && o->ref >= 1);
-
-    if (o->state == PA_SOURCE_OUTPUT_DISCONNECTED)
-        return;
-    
-    o->state = b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
-}
-
-enum pa_resample_method pa_source_output_get_resample_method(struct pa_source_output *o) {
-    assert(o && o->ref >= 1);
-
-    if (!o->resampler)
-        return PA_RESAMPLER_INVALID;
-
-    return pa_resampler_get_method(o->resampler);
-}
diff --git a/polyp/source-output.h b/polyp/source-output.h
deleted file mode 100644 (file)
index f561e05..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#ifndef foosourceoutputhfoo
-#define foosourceoutputhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-
-#include "source.h"
-#include "sample.h"
-#include "memblockq.h"
-#include "resampler.h"
-#include "module.h"
-#include "client.h"
-
-typedef enum {
-    PA_SOURCE_OUTPUT_RUNNING,
-    PA_SOURCE_OUTPUT_CORKED,
-    PA_SOURCE_OUTPUT_DISCONNECTED
-} pa_source_output_state_t;
-
-struct pa_source_output {
-    int ref;
-    uint32_t index;
-    pa_source_output_state_t state;
-    
-    char *name, *driver;
-    struct pa_module *owner;
-    struct pa_client *client;
-    struct pa_source *source;
-
-    struct pa_sample_spec sample_spec;
-    struct pa_channel_map channel_map;
-    
-    void (*push)(struct pa_source_output *o, const struct pa_memchunk *chunk);
-    void (*kill)(struct pa_source_output* o);
-    pa_usec_t (*get_latency) (struct pa_source_output *i);
-
-    struct pa_resampler* resampler;
-    
-    void *userdata;
-};
-
-struct pa_source_output* pa_source_output_new(
-    struct pa_source *s,
-    const char *name,
-    const char *driver,
-    const struct pa_sample_spec *spec,
-    const struct pa_channel_map *map;
-    int resample_method);
-
-void pa_source_output_unref(struct pa_source_output* o);
-struct pa_source_output* pa_source_output_ref(struct pa_source_output *o);
-
-/* To be called by the implementing module only */
-void pa_source_output_disconnect(struct pa_source_output*o);
-
-/* External code may request disconnection with this funcion */
-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);
-
-void pa_source_output_set_name(struct pa_source_output *i, const char *name);
-
-pa_usec_t pa_source_output_get_latency(struct pa_source_output *i);
-
-void pa_source_output_cork(struct pa_source_output *i, int b);
-
-enum pa_resample_method pa_source_output_get_resample_method(struct pa_source_output *o);
-
-#endif
diff --git a/polyp/source.c b/polyp/source.c
deleted file mode 100644 (file)
index a80c6af..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "source.h"
-#include "source-output.h"
-#include "namereg.h"
-#include "xmalloc.h"
-#include "subscribe.h"
-#include "log.h"
-
-struct pa_source* pa_source_new(
-    struct pa_core *core,
-    const char *name,
-    const char *driver,
-    int fail,
-    const struct pa_sample_spec *spec,
-    const struct pa_channel_map *map) {
-    
-    struct pa_source *s;
-    char st[256];
-    int r;
-    
-    assert(core);
-    assert(name);
-    assert(*name);
-    assert(spec);
-
-    s = pa_xmalloc(sizeof(struct pa_source));
-
-    if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) {
-        pa_xfree(s);
-        return NULL;
-    }
-
-    s->ref = 1;
-    s->state = PA_SOURCE_RUNNING;
-    
-    s->name = pa_xstrdup(name);
-    s->description = NULL;
-    s->driver = pa_xstrdup(driver);
-
-    s->owner = NULL;
-    s->core = core;
-    s->sample_spec = *spec;
-    if (map)
-        s->channel_map = *map;
-    else
-        pa_channel_map_init_auto(&s->channel_map, spec->channels);
-
-    s->outputs = pa_idxset_new(NULL, NULL);
-    s->monitor_of = NULL;
-
-    s->get_latency = NULL;
-    s->notify = NULL;
-    s->userdata = NULL;
-
-    r = pa_idxset_put(core->sources, s, &s->index);
-    assert(s->index != PA_IDXSET_INVALID && r >= 0);
-
-    pa_sample_spec_snprint(st, sizeof(st), spec);
-    pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st); 
-
-    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
-    
-    return s;
-}
-
-void pa_source_disconnect(struct pa_source *s) {
-    struct pa_source_output *o, *j = NULL;
-    assert(s && s->state == PA_SOURCE_RUNNING);
-
-    pa_namereg_unregister(s->core, s->name);
-    
-    while ((o = pa_idxset_first(s->outputs, NULL))) {
-        assert(o != j);
-        pa_source_output_kill(o);
-        j = o;
-    }
-
-    pa_idxset_remove_by_data(s->core->sources, s, NULL);
-    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
-
-    s->notify = NULL;
-    
-    s->state = PA_SOURCE_DISCONNECTED;
-}
-
-static void source_free(struct pa_source *s) {
-    assert(s && !s->ref);
-    
-    if (s->state != PA_SOURCE_DISCONNECTED)
-        pa_source_disconnect(s);
-    
-    pa_log_info(__FILE__": freed %u \"%s\"\n", s->index, s->name); 
-
-    pa_idxset_free(s->outputs, NULL, NULL);
-
-    pa_xfree(s->name);
-    pa_xfree(s->description);
-    pa_xfree(s->driver);
-    pa_xfree(s);
-}
-
-void pa_source_unref(struct pa_source *s) {
-    assert(s && s->ref >= 1);
-
-    if (!(--s->ref))
-        source_free(s);
-}
-
-struct pa_source* pa_source_ref(struct pa_source *s) {
-    assert(s && s->ref >= 1);
-    s->ref++;
-    return s;
-}
-
-void pa_source_notify(struct pa_source*s) {
-    assert(s && s->ref >= 1);
-
-    if (s->notify)
-        s->notify(s);
-}
-
-static int do_post(void *p, uint32_t index, int *del, void*userdata) {
-    const 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, const struct pa_memchunk *chunk) {
-    assert(s && s->ref >= 1 && chunk);
-
-    pa_source_ref(s);
-    pa_idxset_foreach(s->outputs, do_post, (void*) chunk);
-    pa_source_unref(s);
-}
-
-void pa_source_set_owner(struct pa_source *s, struct pa_module *m) {
-    assert(s);
-    s->owner = m;
-}
-
-pa_usec_t pa_source_get_latency(struct pa_source *s) {
-    assert(s && s->ref >= 1);
-
-    if (!s->get_latency)
-        return 0;
-
-    return s->get_latency(s);
-}
-
diff --git a/polyp/source.h b/polyp/source.h
deleted file mode 100644 (file)
index 21ebda5..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-#ifndef foosourcehfoo
-#define foosourcehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-struct pa_source;
-
-#include <inttypes.h>
-#include "core.h"
-#include "sample.h"
-#include "idxset.h"
-#include "memblock.h"
-#include "memchunk.h"
-#include "sink.h"
-#include "channelmap.h"
-
-#define PA_MAX_OUTPUTS_PER_SOURCE 16
-
-typedef enum {
-    PA_SOURCE_RUNNING,
-    PA_SOURCE_DISCONNECTED
-} pa_source_state_t;
-
-struct pa_source {
-    int ref;
-    uint32_t index;
-    struct pa_core *core;
-    pa_source_state_t state;
-    
-    char *name, *description, *driver;
-    struct pa_sample_spec sample_spec;
-    struct pa_channel_map channel_map;
-    struct pa_idxset *outputs;
-    struct pa_sink *monitor_of;
-    struct pa_module *owner;
-
-    void (*notify)(struct pa_source*source);
-    pa_usec_t (*get_latency)(struct pa_source *s);
-    
-    void *userdata;
-};
-
-struct pa_source* pa_source_new(
-    struct pa_core *core,
-    const char *name,
-    const char *driver,
-    int fail,
-    const struct pa_sample_spec *spec,
-    const struct pa_channel_map *map);
-
-void pa_source_disconnect(struct pa_source *s);
-void pa_source_unref(struct pa_source *s);
-struct pa_source* pa_source_ref(struct pa_source *c);
-
-/* Pass a new memory block to all output streams */
-void pa_source_post(struct pa_source*s, const struct pa_memchunk *b);
-
-void pa_source_notify(struct pa_source *s);
-
-void pa_source_set_owner(struct pa_source *s, struct pa_module *m);
-
-pa_usec_t pa_source_get_latency(struct pa_source *s);
-
-#endif
diff --git a/polyp/strbuf.h b/polyp/strbuf.h
deleted file mode 100644 (file)
index 281eee1..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef foostrbufhfoo
-#define foostrbufhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "gcc-printf.h"
-
-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, ...)  PA_GCC_PRINTF_ATTR(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/strlist.c b/polyp/strlist.c
deleted file mode 100644 (file)
index 6dc865d..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <assert.h>
-
-#include "strlist.h"
-#include "xmalloc.h"
-#include "strbuf.h"
-#include "util.h"
-
-struct pa_strlist {
-    struct pa_strlist *next;
-    char *str;
-};
-
-struct pa_strlist* pa_strlist_prepend(struct pa_strlist *l, const char *s) {
-    struct pa_strlist *n;
-    assert(s);
-    n = pa_xmalloc(sizeof(struct pa_strlist));
-    n->str = pa_xstrdup(s);
-    n->next = l;
-    return  n;
-}
-
-char *pa_strlist_tostring(struct pa_strlist *l) {
-    int first = 1;
-    struct pa_strbuf *b;
-
-    b = pa_strbuf_new();
-    for (; l; l = l->next) {
-        if (!first)
-            pa_strbuf_puts(b, " ");
-        first = 0;
-        pa_strbuf_puts(b, l->str);
-    }
-
-    return pa_strbuf_tostring_free(b);
-}
-
-struct pa_strlist* pa_strlist_remove(struct pa_strlist *l, const char *s) {
-    struct pa_strlist *ret = l, *prev = NULL;
-    assert(l && s);
-
-    while (l) {
-        if (!strcmp(l->str, s)) {
-            struct pa_strlist *n = l->next;
-            
-            if (!prev) {
-                assert(ret == l);
-                ret = n;
-            } else
-                prev->next = n;
-
-            pa_xfree(l->str);
-            pa_xfree(l);
-
-            l = n;
-            
-        } else {
-            prev = l;
-            l = l->next;
-        }
-    }
-
-    return ret;
-}
-
-void pa_strlist_free(struct pa_strlist *l) {
-    while (l) {
-        struct pa_strlist *c = l;
-        l = l->next;
-
-        pa_xfree(c->str);
-        pa_xfree(c);
-    }
-}
-
-struct pa_strlist* pa_strlist_pop(struct pa_strlist *l, char **s) {
-    struct pa_strlist *r;
-    assert(s);
-    
-    if (!l) {
-        *s = NULL;
-        return NULL;
-    }
-        
-    *s = l->str;
-    r = l->next;
-    pa_xfree(l);
-    return r;
-}
-
-struct pa_strlist* pa_strlist_parse(const char *s) {
-    struct pa_strlist *head = NULL, *p = NULL;
-    const char *state = NULL;
-    char *r;
-
-    while ((r = pa_split_spaces(s, &state))) {
-        struct pa_strlist *n;
-
-        n = pa_xmalloc(sizeof(struct pa_strlist));
-        n->str = r;
-        n->next = NULL;
-
-        if (p)
-            p->next = n;
-        else
-            head = n;
-
-        p = n;
-    }
-
-    return head;
-}
diff --git a/polyp/subscribe.c b/polyp/subscribe.c
deleted file mode 100644 (file)
index ee6ef3a..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-
-#include "queue.h"
-#include "subscribe.h"
-#include "xmalloc.h"
-#include "log.h"
-
-/* The subscription subsystem may be used to be notified whenever an
- * entity (sink, source, ...) is created or deleted. Modules may
- * register a callback function that is called whenever an event
- * matching a subscription mask happens. The execution of the callback
- * function is postponed to the next main loop iteration, i.e. is not
- * called from within the stack frame the entity was created in. */
-
-struct pa_subscription {
-    struct pa_core *core;
-    int dead;
-    void (*callback)(struct pa_core *c, enum pa_subscription_event_type t, uint32_t index, void *userdata);
-    void *userdata;
-    enum pa_subscription_mask mask;
-
-    struct pa_subscription *prev, *next;
-};
-
-struct pa_subscription_event {
-    enum pa_subscription_event_type type;
-    uint32_t index;
-};
-
-static void sched_event(struct pa_core *c);
-
-/* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */
-struct pa_subscription* pa_subscription_new(struct pa_core *c, enum pa_subscription_mask m, void (*callback)(struct pa_core *c, enum pa_subscription_event_type t, uint32_t index, void *userdata), void *userdata) {
-    struct pa_subscription *s;
-    assert(c);
-
-    s = pa_xmalloc(sizeof(struct pa_subscription));
-    s->core = c;
-    s->dead = 0;
-    s->callback = callback;
-    s->userdata = userdata;
-    s->mask = m;
-
-    if ((s->next = c->subscriptions))
-        s->next->prev = s;
-    s->prev = NULL;
-    c->subscriptions = s;
-    return s;
-}
-
-/* Free a subscription object, effectively marking it for deletion */
-void pa_subscription_free(struct pa_subscription*s) {
-    assert(s && !s->dead);
-    s->dead = 1;
-    sched_event(s->core);
-}
-
-static void free_item(struct pa_subscription *s) {
-    assert(s && s->core);
-
-    if (s->prev)
-        s->prev->next = s->next;
-    else
-        s->core->subscriptions = s->next;
-            
-    if (s->next)
-        s->next->prev = s->prev;
-    
-    pa_xfree(s);
-}
-
-/* Free all subscription objects */
-void pa_subscription_free_all(struct pa_core *c) {
-    struct pa_subscription_event *e;
-    assert(c);
-    
-    while (c->subscriptions)
-        free_item(c->subscriptions);
-
-    if (c->subscription_event_queue) {
-        while ((e = pa_queue_pop(c->subscription_event_queue)))
-            pa_xfree(e);
-        
-        pa_queue_free(c->subscription_event_queue, NULL, NULL);
-        c->subscription_event_queue = NULL;
-    }
-
-    if (c->subscription_defer_event) {
-        c->mainloop->defer_free(c->subscription_defer_event);
-        c->subscription_defer_event = NULL;
-    }
-}
-
-/*static void dump_event(struct pa_subscription_event*e) {
-    switch (e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
-        case PA_SUBSCRIPTION_EVENT_SINK:
-            pa_log(__FILE__": SINK_EVENT");
-            break;
-        case PA_SUBSCRIPTION_EVENT_SOURCE:
-            pa_log(__FILE__": SOURCE_EVENT");
-            break;
-        case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
-            pa_log(__FILE__": SINK_INPUT_EVENT");
-            break;
-        case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
-            pa_log(__FILE__": SOURCE_OUTPUT_EVENT");
-            break;
-        case PA_SUBSCRIPTION_EVENT_MODULE:
-            pa_log(__FILE__": MODULE_EVENT");
-            break;
-        case PA_SUBSCRIPTION_EVENT_CLIENT:
-            pa_log(__FILE__": CLIENT_EVENT");
-            break;
-        default:
-            pa_log(__FILE__": OTHER");
-            break;
-    }
-
-    switch (e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
-        case PA_SUBSCRIPTION_EVENT_NEW:
-            pa_log(__FILE__":  NEW");
-            break;
-        case PA_SUBSCRIPTION_EVENT_CHANGE:
-            pa_log(__FILE__":  CHANGE");
-            break;
-        case PA_SUBSCRIPTION_EVENT_REMOVE:
-            pa_log(__FILE__":  REMOVE");
-            break;
-        default:
-            pa_log(__FILE__":  OTHER");
-            break;
-    }
-
-    pa_log(__FILE__":  %u\n", e->index);
-}*/
-
-/* Deferred callback for dispatching subscirption events */
-static void defer_cb(struct pa_mainloop_api *m, struct pa_defer_event *e, void *userdata) {
-    struct pa_core *c = userdata;
-    struct pa_subscription *s;
-    assert(c && c->subscription_defer_event == e && c->mainloop == m);
-
-    c->mainloop->defer_enable(c->subscription_defer_event, 0);
-
-
-    /* Dispatch queued events */
-    
-    if (c->subscription_event_queue) {
-        struct pa_subscription_event *e;
-        
-        while ((e = pa_queue_pop(c->subscription_event_queue))) {
-            struct pa_subscription *s;
-
-            for (s = c->subscriptions; s; s = s->next) {
-
-                if (!s->dead && pa_subscription_match_flags(s->mask, e->type))
-                    s->callback(c, e->type, e->index, s->userdata);
-            }
-            
-            pa_xfree(e);
-        }
-    }
-
-    /* Remove dead subscriptions */
-    
-    s = c->subscriptions;
-    while (s) {
-        struct pa_subscription *n = s->next;
-        if (s->dead)
-            free_item(s);
-        s = n;
-    }
-}
-
-/* Schedule an mainloop event so that a pending subscription event is dispatched */
-static void sched_event(struct pa_core *c) {
-    assert(c);
-
-    if (!c->subscription_defer_event) {
-        c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c);
-        assert(c->subscription_defer_event);
-    }
-        
-    c->mainloop->defer_enable(c->subscription_defer_event, 1);
-}
-
-/* Append a new subscription event to the subscription event queue and schedule a main loop event */
-void pa_subscription_post(struct pa_core *c, enum pa_subscription_event_type t, uint32_t index) {
-    struct pa_subscription_event *e;
-    assert(c);
-
-    e = pa_xmalloc(sizeof(struct pa_subscription_event));
-    e->type = t;
-    e->index = index;
-
-    if (!c->subscription_event_queue) {
-        c->subscription_event_queue = pa_queue_new();
-        assert(c->subscription_event_queue);
-    }
-    
-    pa_queue_push(c->subscription_event_queue, e);
-    sched_event(c);
-}
-
-
diff --git a/polyp/subscribe.h b/polyp/subscribe.h
deleted file mode 100644 (file)
index 38323fa..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef foosubscribehfoo
-#define foosubscribehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include "core.h"
-#include "native-common.h"
-
-struct pa_subscription;
-struct pa_subscription_event;
-
-struct pa_subscription* pa_subscription_new(struct pa_core *c, enum pa_subscription_mask m,  void (*callback)(struct pa_core *c, enum pa_subscription_event_type t, uint32_t index, void *userdata), void *userdata);
-void pa_subscription_free(struct pa_subscription*s);
-void pa_subscription_free_all(struct pa_core *c);
-
-void pa_subscription_post(struct pa_core *c, enum pa_subscription_event_type t, uint32_t index);
-
-#endif
diff --git a/polyp/tagstruct.c b/polyp/tagstruct.c
deleted file mode 100644 (file)
index a6dad86..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include <netinet/in.h>
-#include <assert.h>
-
-#include "tagstruct.h"
-#include "xmalloc.h"
-
-enum tags {
-    TAG_STRING = 't',
-    TAG_NULL_STRING = 'N',
-    TAG_U32 = 'L',
-    TAG_S32 = 'l',
-    TAG_U16 = 'S',
-    TAG_S16 = 's',
-    TAG_U8 = 'B',
-    TAG_S8 = 'b',
-    TAG_U64 = 'R',
-    TAG_S64 = 'r',
-    TAG_SAMPLE_SPEC = 'a',
-    TAG_ARBITRARY = 'x',
-    TAG_BOOLEAN_TRUE = '1',
-    TAG_BOOLEAN_FALSE = '0',
-    TAG_TIMEVAL = 'T',
-    TAG_USEC = 'U'  /* 64bit unsigned */
-};
-
-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 = pa_xmalloc(sizeof(struct pa_tagstruct));
-    t->data = (uint8_t*) data;
-    t->allocated = t->length = data ? length : 0;
-    t->rindex = 0;
-    t->dynamic = !data;
-    return t;
-}
-    
-void pa_tagstruct_free(struct pa_tagstruct*t) {
-    assert(t);
-    if (t->dynamic)
-        pa_xfree(t->data);
-    pa_xfree(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;
-    pa_xfree(t);
-    return p;
-}
-
-static void extend(struct pa_tagstruct*t, size_t l) {
-    assert(t && t->dynamic);
-
-    if (t->length+l <= t->allocated)
-        return;
-
-    t->data = pa_xrealloc(t->data, t->allocated = t->length+l+100);
-}
-
-void pa_tagstruct_puts(struct pa_tagstruct*t, const char *s) {
-    size_t l;
-    assert(t);
-    if (s) {
-        l = strlen(s)+2;
-        extend(t, l);
-        t->data[t->length] = TAG_STRING;
-        strcpy((char*) (t->data+t->length+1), s);
-        t->length += l;
-    } else {
-        extend(t, 1);
-        t->data[t->length] = TAG_NULL_STRING;
-        t->length += 1;
-    }
-}
-
-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;
-}
-
-void pa_tagstruct_put_boolean(struct pa_tagstruct*t, int b) {
-    assert(t);
-    extend(t, 1);
-    t->data[t->length] = b ? TAG_BOOLEAN_TRUE : TAG_BOOLEAN_FALSE;
-    t->length += 1;
-}
-
-void pa_tagstruct_put_timeval(struct pa_tagstruct*t, const struct timeval *tv) {
-    assert(t);
-    extend(t, 9);
-    t->data[t->length] = TAG_TIMEVAL;
-    *((uint32_t*) (t->data+t->length+1)) = htonl(tv->tv_sec);
-    *((uint32_t*) (t->data+t->length+5)) = htonl(tv->tv_usec);
-    t->length += 9;
-}
-
-void pa_tagstruct_put_usec(struct pa_tagstruct*t, pa_usec_t u) {
-    assert(t);
-    extend(t, 9);
-    t->data[t->length] = TAG_USEC;
-    *((uint32_t*) (t->data+t->length+1)) = htonl((uint32_t) (u >> 32));
-    *((uint32_t*) (t->data+t->length+5)) = htonl((uint32_t) u);
-    t->length += 9;
-}
-
-void pa_tagstruct_putu64(struct pa_tagstruct*t, uint64_t u) {
-    assert(t);
-    extend(t, 9);
-    t->data[t->length] = TAG_U64;
-    *((uint32_t*) (t->data+t->length+1)) = htonl((uint32_t) (u >> 32));
-    *((uint32_t*) (t->data+t->length+5)) = htonl((uint32_t) u);
-    t->length += 9;
-}
-
-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+1 > t->length)
-        return -1;
-
-    if (t->data[t->rindex] == TAG_NULL_STRING) {
-        t->rindex++;
-        *s = NULL;
-        return 0;
-    }
-    
-    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;
-}
-
-int pa_tagstruct_get_boolean(struct pa_tagstruct*t, int *b) {
-    assert(t && b);
-
-    if (t->rindex+1 > t->length)
-        return -1;
-
-    if (t->data[t->rindex] == TAG_BOOLEAN_TRUE)
-        *b = 1;
-    else if (t->data[t->rindex] == TAG_BOOLEAN_FALSE)
-        *b = 0;
-    else
-        return -1;
-    
-    t->rindex +=1;
-    return 0;
-}
-
-int pa_tagstruct_get_timeval(struct pa_tagstruct*t, struct timeval *tv) {
-
-    if (t->rindex+9 > t->length)
-        return -1;
-
-    if (t->data[t->rindex] != TAG_TIMEVAL)
-        return -1;
-    
-    tv->tv_sec = ntohl(*((uint32_t*) (t->data+t->rindex+1)));
-    tv->tv_usec = ntohl(*((uint32_t*) (t->data+t->rindex+5)));
-    t->rindex += 9;
-    return 0;
-    
-}
-
-int pa_tagstruct_get_usec(struct pa_tagstruct*t, pa_usec_t *u) {
-    assert(t && u);
-
-    if (t->rindex+9 > t->length)
-        return -1;
-
-    if (t->data[t->rindex] != TAG_USEC)
-        return -1;
-
-    *u = (pa_usec_t) ntohl(*((uint32_t*) (t->data+t->rindex+1))) << 32;
-    *u |= (pa_usec_t) ntohl(*((uint32_t*) (t->data+t->rindex+5)));
-    t->rindex +=9;
-    return 0;
-}
-
-int pa_tagstruct_getu64(struct pa_tagstruct*t, uint64_t *u) {
-    assert(t && u);
-
-    if (t->rindex+9 > t->length)
-        return -1;
-
-    if (t->data[t->rindex] != TAG_U64)
-        return -1;
-
-    *u = (uint64_t) ntohl(*((uint32_t*) (t->data+t->rindex+1))) << 32;
-    *u |= (uint64_t) ntohl(*((uint32_t*) (t->data+t->rindex+5)));
-    t->rindex +=9;
-    return 0;
-}
diff --git a/polyp/tagstruct.h b/polyp/tagstruct.h
deleted file mode 100644 (file)
index f867cfb..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef footagstructhfoo
-#define footagstructhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <time.h>
-
-#include "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_putu8(struct pa_tagstruct*t, uint8_t c);
-void pa_tagstruct_putu32(struct pa_tagstruct*t, uint32_t i);
-void pa_tagstruct_putu64(struct pa_tagstruct*t, uint64_t i);
-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);
-void pa_tagstruct_put_boolean(struct pa_tagstruct*t, int b);
-void pa_tagstruct_put_timeval(struct pa_tagstruct*t, const struct timeval *tv);
-void pa_tagstruct_put_usec(struct pa_tagstruct*t, pa_usec_t u);
-void pa_tagstruct_put_channel_map(struct pa_tagstruct *t, const struct pa_channel_map *map);
-void pa_tagstruct_put_cvolume(struct pa_tagstruct *t, const struct pa_cvolume *cvolume);
-
-int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s);
-int pa_tagstruct_getu8(struct pa_tagstruct*t, uint8_t *c);
-int pa_tagstruct_getu32(struct pa_tagstruct*t, uint32_t *i);
-int pa_tagstruct_getu64(struct pa_tagstruct*t, uint64_t *i);
-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_get_boolean(struct pa_tagstruct *t, int *b);
-int pa_tagstruct_get_timeval(struct pa_tagstruct*t, struct timeval *tv);
-int pa_tagstruct_get_usec(struct pa_tagstruct*t, pa_usec_t *u);
-int pa_tagstruct_get_channel_map(struct pa_tagstruct *t, struct pa_channel_map *map);
-int pa_tagstruct_get_cvolume(struct pa_tagstruct *t, struct pa_cvolume *v);
-
-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/typeid.c b/polyp/typeid.c
deleted file mode 100644 (file)
index 70d3df3..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <stdio.h>
-
-#include "typeid.h"
-
-char *pa_typeid_to_string(pa_typeid_t id, char *ret_s, size_t length) {
-    if (id == PA_TYPEID_UNKNOWN)
-        snprintf(ret_s, length, "????");
-    else
-        snprintf(ret_s, length, "%c%c%c%c", (char) (id >> 24), (char) (id >> 16), (char) (id >> 8), (char) (id));
-    
-    return ret_s;
-}
diff --git a/polyp/typeid.h b/polyp/typeid.h
deleted file mode 100644 (file)
index cc1676b..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef footypeidhfoo
-#define footypeidhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include <sys/types.h>
-
-#include <polyp/cdecl.h>
-
-PA_C_DECL_BEGIN
-
-typedef uint32_t pa_typeid_t;
-
-#define PA_TYPEID_UNKNOWN ((pa_typeid_t) -1)
-
-char *pa_typeid_to_string(pa_typeid_t id, char *ret_s, size_t length);
-
-#define PA_TYPEID_MAKE(a,b,c,d) (\
-    (((pa_typeid_t) a & 0xFF) << 24) | \
-    (((pa_typeid_t) b & 0xFF) << 16) | \
-    (((pa_typeid_t) c & 0xFF) << 8) | \
-    (((pa_typeid_t) d & 0xFF)))
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/util.c b/polyp/util.c
deleted file mode 100644 (file)
index ee3fa87..0000000
+++ /dev/null
@@ -1,905 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <errno.h>
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <pwd.h>
-#include <signal.h>
-#include <pthread.h>
-#include <sys/time.h>
-#include <sched.h>
-#include <sys/resource.h>
-#include <limits.h>
-#include <unistd.h>
-#include <grp.h>
-#include <netdb.h>
-
-#include <samplerate.h>
-
-#include "util.h"
-#include "xmalloc.h"
-#include "log.h"
-
-#define PA_RUNTIME_PATH_PREFIX "/tmp/polypaudio-"
-
-/** Make a file descriptor nonblock. Doesn't do any error checking */
-void pa_make_nonblock_fd(int fd) {
-    int v;
-    assert(fd >= 0);
-
-    if ((v = fcntl(fd, F_GETFL)) >= 0)
-        if (!(v & O_NONBLOCK))
-            fcntl(fd, F_SETFL, v|O_NONBLOCK);
-}
-
-/** Creates a directory securely */
-int pa_make_secure_dir(const char* dir) {
-    struct stat st;
-    assert(dir);
-
-    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;
-}
-
-/* Creates a the parent directory of the specified path securely */
-int pa_make_secure_parent_dir(const char *fn) {
-    int ret = -1;
-    char *slash, *dir = pa_xstrdup(fn);
-    
-    if (!(slash = strrchr(dir, '/')))
-        goto finish;
-    *slash = 0;
-    
-    if (pa_make_secure_dir(dir) < 0)
-        goto finish;
-
-    ret = 0;
-    
-finish:
-    pa_xfree(dir);
-    return ret;
-}
-
-
-/** Calls read() in a loop. Makes sure that as much as 'size' bytes,
- * unless EOF is reached or an error occured */
-ssize_t pa_loop_read(int fd, void*data, size_t size) {
-    ssize_t ret = 0;
-    assert(fd >= 0 && data && size);
-
-    while (size > 0) {
-        ssize_t r;
-
-        if ((r = read(fd, data, size)) < 0)
-            return r;
-
-        if (r == 0)
-            break;
-        
-        ret += r;
-        data = (uint8_t*) data + r;
-        size -= r;
-    }
-
-    return ret;
-}
-
-/** Similar to pa_loop_read(), but wraps write() */
-ssize_t pa_loop_write(int fd, const void*data, size_t size) {
-    ssize_t ret = 0;
-    assert(fd >= 0 && data && size);
-
-    while (size > 0) {
-        ssize_t r;
-
-        if ((r = write(fd, data, size)) < 0)
-            return r;
-
-        if (r == 0)
-            break;
-        
-        ret += r;
-        data = (uint8_t*) data + r;
-        size -= r;
-    }
-
-    return ret;
-}
-
-/* Print a warning messages in case that the given signal is not
- * blocked or trapped */
-void pa_check_signal_is_blocked(int sig) {
-    struct sigaction sa;
-    sigset_t set;
-
-    /* If POSIX threads are supported use thread-aware
-     * pthread_sigmask() function, to check if the signal is
-     * blocked. Otherwise fall back to sigprocmask() */
-    
-#ifdef HAVE_PTHREAD    
-    if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) {
-#endif
-        if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) {
-            pa_log(__FILE__": sigprocmask() failed: %s\n", strerror(errno));
-            return;
-        }
-#ifdef HAVE_PTHREAD
-    }
-#endif
-
-    if (sigismember(&set, sig))
-        return;
-
-    /* Check whether the signal is trapped */
-    
-    if (sigaction(sig, NULL, &sa) < 0) {
-        pa_log(__FILE__": sigaction() failed: %s\n", strerror(errno));
-        return;
-    }
-        
-    if (sa.sa_handler != SIG_DFL)
-        return;
-    
-    pa_log(__FILE__": WARNING: %s is not trapped. This might cause malfunction!\n", pa_strsignal(sig));
-}
-
-/* The following function is based on an example from the GNU libc
- * documentation. This function is similar to GNU's asprintf(). */
-char *pa_sprintf_malloc(const char *format, ...) {
-    int  size = 100;
-    char *c = NULL;
-    
-    assert(format);
-    
-    for(;;) {
-        int r;
-        va_list ap;
-
-        c = pa_xrealloc(c, size);
-
-        va_start(ap, format);
-        r = vsnprintf(c, size, format, ap);
-        va_end(ap);
-        
-        if (r > -1 && r < size)
-            return c;
-
-        if (r > -1)    /* glibc 2.1 */
-            size = r+1; 
-        else           /* glibc 2.0 */
-            size *= 2;
-    }
-}
-
-/* Same as the previous function, but use a va_list instead of an
- * ellipsis */
-char *pa_vsprintf_malloc(const char *format, va_list ap) {
-    int  size = 100;
-    char *c = NULL;
-    
-    assert(format);
-    
-    for(;;) {
-        int r;
-        c = pa_xrealloc(c, size);
-        r = vsnprintf(c, size, format, ap);
-        
-        if (r > -1 && r < size)
-            return c;
-
-        if (r > -1)    /* glibc 2.1 */
-            size = r+1; 
-        else           /* glibc 2.0 */
-            size *= 2;
-    }
-}
-
-/* Return the current username in the specified string buffer. */
-char *pa_get_user_name(char *s, size_t l) {
-    struct passwd pw, *r;
-    char buf[1024];
-    char *p;
-    assert(s && l > 0);
-
-    if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) {
-        
-#ifdef HAVE_GETPWUID_R
-        if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
-#else
-            /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
-             * that do not support getpwuid_r. */
-            if ((r = getpwuid(getuid())) == NULL) {
-#endif
-                snprintf(s, l, "%lu", (unsigned long) getuid());
-                return s;
-            }
-            
-            p = r->pw_name;
-        }
-
-    return pa_strlcpy(s, p, l);
-    }
-
-/* Return the current hostname in the specified buffer. */
-char *pa_get_host_name(char *s, size_t l) {
-    assert(s && l > 0);
-    if (gethostname(s, l) < 0) {
-        pa_log(__FILE__": gethostname(): %s\n", strerror(errno));
-        return NULL;
-    }
-    s[l-1] = 0;
-    return s;
-}
-
-/* Return the home directory of the current user */
-char *pa_get_home_dir(char *s, size_t l) {
-    char *e;
-    char buf[1024];
-    struct passwd pw, *r;
-    assert(s && l);
-
-    if ((e = getenv("HOME")))
-        return pa_strlcpy(s, e, l);
-
-    if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
-        pa_log(__FILE__": getpwuid_r() failed\n");
-        return NULL;
-    }
-
-    return pa_strlcpy(s, r->pw_dir, l);
-}
-
-/* Similar to OpenBSD's strlcpy() function */
-char *pa_strlcpy(char *b, const char *s, size_t l) {
-    assert(b && s && l > 0);
-
-    strncpy(b, s, l);
-    b[l-1] = 0;
-    return b;
-}
-
-/* Calculate the difference between the two specfified timeval
- * timestamsps. */
-pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
-    pa_usec_t r;
-    assert(a && b);
-
-    /* Check which whan is the earlier time and swap the two arguments if reuqired. */
-    if (pa_timeval_cmp(a, b) < 0) {
-        const struct timeval *c;
-        c = a;
-        a = b;
-        b = c;
-    }
-
-    /* Calculate the second difference*/
-    r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000;
-
-    /* Calculate the microsecond difference */
-    if (a->tv_usec > b->tv_usec)
-        r += ((pa_usec_t) a->tv_usec - b->tv_usec);
-    else if (a->tv_usec < b->tv_usec)
-        r -= ((pa_usec_t) b->tv_usec - a->tv_usec);
-
-    return r;
-}
-
-/* Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */
-int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {
-    assert(a && b);
-
-    if (a->tv_sec < b->tv_sec)
-        return -1;
-
-    if (a->tv_sec > b->tv_sec)
-        return 1;
-
-    if (a->tv_usec < b->tv_usec)
-        return -1;
-
-    if (a->tv_usec > b->tv_usec)
-        return 1;
-
-    return 0;
-}
-
-/* Return the time difference between now and the specified timestamp */
-pa_usec_t pa_timeval_age(const struct timeval *tv) {
-    struct timeval now;
-    assert(tv);
-    gettimeofday(&now, NULL);
-    return pa_timeval_diff(&now, tv);
-}
-
-/* Add the specified time inmicroseconds to the specified timeval structure */
-void pa_timeval_add(struct timeval *tv, pa_usec_t v) {
-    unsigned long secs;
-    assert(tv);
-    
-    secs = (v/1000000);
-    tv->tv_sec += (unsigned long) secs;
-    v -= secs*1000000;
-
-    tv->tv_usec += v;
-
-    /* Normalize */
-    while (tv->tv_usec >= 1000000) {
-        tv->tv_sec++;
-        tv->tv_usec -= 1000000;
-    }
-}
-
-#define NICE_LEVEL (-15)
-
-/* Raise the priority of the current process as much as possible and
-sensible: set the nice level to -15 and enable realtime scheduling if
-supported.*/
-void pa_raise_priority(void) {
-
-    if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0)
-        pa_log_warn(__FILE__": setpriority() failed: %s\n", strerror(errno));
-    else 
-        pa_log_info(__FILE__": Successfully gained nice level %i.\n", NICE_LEVEL); 
-    
-#ifdef _POSIX_PRIORITY_SCHEDULING
-    {
-        struct sched_param sp;
-
-        if (sched_getparam(0, &sp) < 0) {
-            pa_log(__FILE__": sched_getparam() failed: %s\n", strerror(errno));
-            return;
-        }
-        
-        sp.sched_priority = 1;
-        if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) {
-            pa_log_warn(__FILE__": sched_setscheduler() failed: %s\n", strerror(errno));
-            return;
-        }
-
-        pa_log_info(__FILE__": Successfully enabled SCHED_FIFO scheduling.\n"); 
-    }
-#endif
-}
-
-/* Reset the priority to normal, inverting the changes made by pa_raise_priority() */
-void pa_reset_priority(void) {
-#ifdef _POSIX_PRIORITY_SCHEDULING
-    {
-        struct sched_param sp;
-        sched_getparam(0, &sp);
-        sp.sched_priority = 0;
-        sched_setscheduler(0, SCHED_OTHER, &sp);
-    }
-#endif
-
-    setpriority(PRIO_PROCESS, 0, 0);
-}
-
-/* Set the FD_CLOEXEC flag for a fd */
-int pa_fd_set_cloexec(int fd, int b) {
-    int v;
-    assert(fd >= 0);
-
-    if ((v = fcntl(fd, F_GETFD, 0)) < 0)
-        return -1;
-    
-    v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0);
-    
-    if (fcntl(fd, F_SETFD, v) < 0)
-        return -1;
-    
-    return 0;
-}
-
-/* Return the binary file name of the current process. Works on Linux
- * only. This shoul be used for eyecandy only, don't rely on return
- * non-NULL! */
-char *pa_get_binary_name(char *s, size_t l) {
-    char path[PATH_MAX];
-    int i;
-    assert(s && l);
-
-    /* This works on Linux only */
-    
-    snprintf(path, sizeof(path), "/proc/%u/exe", (unsigned) getpid());
-    if ((i = readlink(path, s, l-1)) < 0)
-        return NULL;
-
-    s[i] = 0;
-    return s;
-}
-
-/* Return a pointer to the filename inside a path (which is the last
- * component). */
-char *pa_path_get_filename(const char *p) {
-    char *fn;
-
-    if ((fn = strrchr(p, '/')))
-        return fn+1;
-
-    return (char*) p;
-}
-
-/* Try to parse a boolean string value.*/
-int pa_parse_boolean(const char *v) {
-    
-    if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
-        return 1;
-    else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
-        return 0;
-
-    return -1;
-}
-
-/* Split the specified string wherever one of the strings in delimiter
- * occurs. Each time it is called returns a newly allocated string
- * with pa_xmalloc(). The variable state points to, should be
- * initiallized to NULL before the first call. */
-char *pa_split(const char *c, const char *delimiter, const char**state) {
-    const char *current = *state ? *state : c;
-    size_t l;
-
-    if (!*current)
-        return NULL;
-    
-    l = strcspn(current, delimiter);
-    *state = current+l;
-
-    if (**state)
-        (*state)++;
-
-    return pa_xstrndup(current, l);
-}
-
-/* What is interpreted as whitespace? */
-#define WHITESPACE " \t\n"
-
-/* Split a string into words. Otherwise similar to pa_split(). */
-char *pa_split_spaces(const char *c, const char **state) {
-    const char *current = *state ? *state : c;
-    size_t l;
-
-    if (!*current || *c == 0)
-        return NULL;
-
-    current += strspn(current, WHITESPACE);
-    l = strcspn(current, WHITESPACE);
-
-    *state = current+l;
-
-    return pa_xstrndup(current, l);
-}
-
-/* Return the name of an UNIX signal. Similar to GNU's strsignal() */
-const char *pa_strsignal(int sig) {
-    switch(sig) {
-        case SIGINT: return "SIGINT";
-        case SIGTERM: return "SIGTERM";
-        case SIGUSR1: return "SIGUSR1";
-        case SIGUSR2: return "SIGUSR2";
-        case SIGXCPU: return "SIGXCPU";
-        case SIGPIPE: return "SIGPIPE";
-        case SIGCHLD: return "SIGCHLD";
-        case SIGHUP: return "SIGHUP";
-        default: return "UNKNOWN SIGNAL";
-    }
-}
-
-
-/* Check whether the specified GID and the group name match */
-static int is_group(gid_t gid, const char *name) {
-    struct group group, *result = NULL;
-    long n;
-    void *data;
-    int r = -1;
-
-#ifdef HAVE_GETGRGID_R
-#ifdef _SC_GETGR_R_SIZE_MAX
-    n = sysconf(_SC_GETGR_R_SIZE_MAX);
-#else
-    n = -1;
-#endif
-    if (n < 0) n = 512;
-    data = pa_xmalloc(n);
-
-    if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) {
-        pa_log(__FILE__ ": getgrgid_r(%u) failed: %s\n", gid, strerror(errno));
-        goto finish;
-    }
-
-    
-    r = strcmp(name, result->gr_name) == 0;
-    
-finish:
-    pa_xfree(data);
-#else
-    /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
-     * support getgrgid_r. */
-    if ((result = getgrgid(gid)) == NULL) {
-       pa_log(__FILE__ ": getgrgid(%u) failed: %s\n", gid, strerror(errno));
-       goto finish;
-    }
-
-    r = strcmp(name, result->gr_name) == 0;
-
-finish:
-#endif
-    
-    return r;
-}
-
-/* Check the current user is member of the specified group */
-int pa_uid_in_group(const char *name, gid_t *gid) {
-    gid_t *gids, tgid;
-    long n = sysconf(_SC_NGROUPS_MAX);
-    int r = -1, i;
-
-    assert(n > 0);
-    
-    gids = pa_xmalloc(sizeof(gid_t)*n);
-    
-    if ((n = getgroups(n, gids)) < 0) {
-        pa_log(__FILE__": getgroups() failed: %s\n", strerror(errno));
-        goto finish;
-    }
-
-    for (i = 0; i < n; i++) {
-        if (is_group(gids[i], name) > 0) {
-            *gid = gids[i];
-            r = 1;
-            goto finish;
-        }
-    }
-
-    if (is_group(tgid = getgid(), name) > 0) {
-        *gid = tgid;
-        r = 1;
-        goto finish;
-    }
-
-    r = 0;
-    
-finish:
-
-    pa_xfree(gids);
-    return r;
-}
-
-/* Lock or unlock a file entirely. (advisory) */
-int pa_lock_fd(int fd, int b) {
-    struct flock flock;
-
-    /* Try a R/W lock first */
-    
-    flock.l_type = b ? F_WRLCK : F_UNLCK;
-    flock.l_whence = SEEK_SET;
-    flock.l_start = 0;
-    flock.l_len = 0;
-
-    if (fcntl(fd, F_SETLKW, &flock) >= 0)
-        return 0;
-
-    /* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */
-    if (b && errno == EBADF) {
-        flock.l_type = F_RDLCK;
-        if (fcntl(fd, F_SETLKW, &flock) >= 0)
-            return 0;
-    }
-        
-    pa_log(__FILE__": %slock failed: %s\n", !b ? "un" : "", strerror(errno));
-    return -1;
-}
-
-/* Remove trailing newlines from a string */
-char* pa_strip_nl(char *s) {
-    assert(s);
-
-    s[strcspn(s, "\r\n")] = 0;
-    return s;
-}
-
-/* Create a temporary lock file and lock it. */
-int pa_lock_lockfile(const char *fn) {
-    int fd = -1;
-    assert(fn);
-
-    for (;;) {
-        struct stat st;
-        
-        if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
-            pa_log(__FILE__": failed to create lock file '%s': %s\n", fn, strerror(errno));
-            goto fail;
-        }
-        
-        if (pa_lock_fd(fd, 1) < 0) {
-            pa_log(__FILE__": failed to lock file '%s'.\n", fn);
-            goto fail;
-        }
-        
-        if (fstat(fd, &st) < 0) {
-            pa_log(__FILE__": failed to fstat() file '%s'.\n", fn);
-            goto fail;
-        }
-
-        /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */
-        if (st.st_nlink >= 1)
-            break;
-            
-        if (pa_lock_fd(fd, 0) < 0) {
-            pa_log(__FILE__": failed to unlock file '%s'.\n", fn);
-            goto fail;
-        }
-        
-        if (close(fd) < 0) {
-            pa_log(__FILE__": failed to close file '%s'.\n", fn);
-            goto fail;
-        }
-
-        fd = -1;
-    }
-        
-    return fd;
-
-fail:
-
-    if (fd >= 0)
-        close(fd);
-
-    return -1;
-}
-
-/* Unlock a temporary lcok file */
-int pa_unlock_lockfile(const char *fn, int fd) {
-    int r = 0;
-    assert(fn && fd >= 0);
-
-    if (unlink(fn) < 0) {
-        pa_log_warn(__FILE__": WARNING: unable to remove lock file '%s': %s\n", fn, strerror(errno));
-        r = -1;
-    }
-    
-    if (pa_lock_fd(fd, 0) < 0) {
-        pa_log_warn(__FILE__": WARNING: failed to unlock file '%s'.\n", fn);
-        r = -1;
-    }
-
-    if (close(fd) < 0) {
-        pa_log_warn(__FILE__": WARNING: failed to close lock file '%s': %s\n", fn, strerror(errno));
-        r = -1;
-    }
-
-    return r;
-}
-
-/* Try to open a configuration file. If "env" is specified, open the
- * value of the specified environment variable. Otherwise look for a
- * file "local" in the home directory or a file "global" in global
- * file system. If "result" is non-NULL, a pointer to a newly
- * allocated buffer containing the used configuration file is
- * stored there.*/
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
-    const char *e;
-    char h[PATH_MAX];
-
-    if (env && (e = getenv(env))) {
-        if (result)
-            *result = pa_xstrdup(e);
-        return fopen(e, "r");
-    }
-
-    if (local && pa_get_home_dir(h, sizeof(h))) {
-        FILE *f;
-        char *l;
-        
-        l = pa_sprintf_malloc("%s/%s", h, local);
-        f = fopen(l, "r");
-
-        if (f || errno != ENOENT) {
-            if (result)
-                *result = l;
-            else
-                pa_xfree(l);
-            return f;
-        }
-        
-        pa_xfree(l);
-    }
-
-    if (!global) {
-        if (result)
-            *result = NULL;
-        errno = ENOENT;
-        return NULL;
-    }
-
-    if (result)
-        *result = pa_xstrdup(global);
-    
-    return fopen(global, "r");
-}
-                 
-/* Format the specified data as a hexademical string */
-char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) {
-    size_t i = 0, j = 0;
-    const char hex[] = "0123456789abcdef";
-    assert(d && s && slength > 0);
-
-    while (i < dlength && j+3 <= slength) {
-        s[j++] = hex[*d >> 4];
-        s[j++] = hex[*d & 0xF];
-
-        d++;
-        i++;
-    }
-
-    s[j < slength ? j : slength] = 0;
-    return s;
-}
-
-/* Convert a hexadecimal digit to a number or -1 if invalid */
-static int hexc(char c) {
-    if (c >= '0' && c <= '9')
-        return c - '0';
-
-    if (c >= 'A' && c <= 'F')
-        return c - 'A' + 10;
-
-    if (c >= 'a' && c <= 'f')
-        return c - 'a' + 10;
-
-    return -1;
-}
-
-/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */
-size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
-    size_t j = 0;
-    assert(p && d);
-
-    while (j < dlength && *p) {
-        int b;
-
-        if ((b = hexc(*(p++))) < 0)
-            return (size_t) -1;
-        
-        d[j] = (uint8_t) (b << 4);
-
-        if (!*p)
-            return (size_t) -1;
-
-        if ((b = hexc(*(p++))) < 0)
-            return (size_t) -1;
-
-        d[j] |= (uint8_t) b;
-        j++;
-    }
-
-    return j;
-}
-
-/* Return the fully qualified domain name in *s */
-char *pa_get_fqdn(char *s, size_t l) {
-    char hn[256];
-    struct addrinfo *a, hints;
-
-    if (!pa_get_host_name(hn, sizeof(hn)))
-        return NULL;
-
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family = AF_UNSPEC;
-    hints.ai_flags = AI_CANONNAME;
-    
-    if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname)
-        return pa_strlcpy(s, hn, l);
-
-    pa_strlcpy(s, a->ai_canonname, l);
-    freeaddrinfo(a);
-    return s;
-}
-
-/* Returns nonzero when *s starts with *pfx */
-int pa_startswith(const char *s, const char *pfx) {
-    size_t l;
-    assert(s && pfx);
-    l = strlen(pfx);
-
-    return strlen(s) >= l && strncmp(s, pfx, l) == 0;
-}
-
-/* if fn is null return the polypaudio run time path in s (/tmp/polypaudio)
- * if fn is non-null and starts with / return fn in s
- * otherwise append fn to the run time path and return it in s */
-char *pa_runtime_path(const char *fn, char *s, size_t l) {
-    char u[256];
-
-    if (fn && *fn == '/')
-        return pa_strlcpy(s, fn, l);
-    
-    snprintf(s, l, PA_RUNTIME_PATH_PREFIX"%s%s%s", pa_get_user_name(u, sizeof(u)), fn ? "/" : "", fn ? fn : "");
-    return s;
-}
-
-/* Wait t milliseconds */
-int pa_msleep(unsigned long t) {
-    struct timespec ts;
-
-    ts.tv_sec = t/1000;
-    ts.tv_nsec = (t % 1000) * 1000000;
-
-    return nanosleep(&ts, NULL);
-}
-
-/* Convert the string s to a signed integer in *ret_i */
-int pa_atoi(const char *s, int32_t *ret_i) {
-    char *x = NULL;
-    long l;
-    assert(s && ret_i);
-
-    l = strtol(s, &x, 0);
-
-    if (!x || *x)
-        return -1;
-
-    *ret_i = (int32_t) l;
-    
-    return 0;
-}
-
-/* Convert the string s to an unsigned integer in *ret_u */
-int pa_atou(const char *s, uint32_t *ret_u) {
-    char *x = NULL;
-    unsigned long l;
-    assert(s && ret_u);
-
-    l = strtoul(s, &x, 0);
-
-    if (!x || *x)
-        return -1;
-
-    *ret_u = (uint32_t) l;
-    
-    return 0;
-}
diff --git a/polyp/util.h b/polyp/util.h
deleted file mode 100644 (file)
index 2cfc5f6..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#ifndef fooutilhfoo
-#define fooutilhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <sys/types.h>
-#include <inttypes.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-#include "gcc-printf.h"
-#include "sample.h"
-
-void pa_make_nonblock_fd(int fd);
-
-int pa_make_secure_dir(const char* dir);
-int pa_make_secure_parent_dir(const char *fn);
-
-ssize_t pa_loop_read(int fd, void*data, size_t size);
-ssize_t pa_loop_write(int fd, const void*data, size_t size);
-
-void pa_check_signal_is_blocked(int sig);
-
-char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
-char *pa_vsprintf_malloc(const char *format, va_list ap);
-
-char *pa_strlcpy(char *b, const char *s, size_t l);
-
-char *pa_get_user_name(char *s, size_t l);
-char *pa_get_host_name(char *s, size_t l);
-char *pa_get_fqdn(char *s, size_t l);
-char *pa_get_binary_name(char *s, size_t l);
-char *pa_get_home_dir(char *s, size_t l);
-
-char *pa_path_get_filename(const char *p);
-
-pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b);
-int pa_timeval_cmp(const struct timeval *a, const struct timeval *b);
-pa_usec_t pa_timeval_age(const struct timeval *tv);
-void pa_timeval_add(struct timeval *tv, pa_usec_t v);
-
-void pa_raise_priority(void);
-void pa_reset_priority(void);
-
-int pa_fd_set_cloexec(int fd, int b);
-
-int pa_parse_boolean(const char *s);
-
-char *pa_split(const char *c, const char*delimiters, const char **state);
-char *pa_split_spaces(const char *c, const char **state);
-
-char *pa_strip_nl(char *s);
-
-const char *pa_strsignal(int sig);
-
-int pa_uid_in_group(const char *name, gid_t *gid);
-
-int pa_lock_fd(int fd, int b);
-
-int pa_lock_lockfile(const char *fn);
-int pa_unlock_lockfile(const char *fn, int fd);
-
-FILE *pa_open_config_file(const char *env, const char *global, const char *local, char **result);
-
-char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
-size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
-
-int pa_startswith(const char *s, const char *pfx);
-
-char *pa_runtime_path(const char *fn, char *s, size_t l);
-
-int pa_msleep(unsigned long t);
-
-int pa_atoi(const char *s, int32_t *ret_i);
-int pa_atou(const char *s, uint32_t *ret_u);
-
-#endif
diff --git a/polyp/volume.h b/polyp/volume.h
deleted file mode 100644 (file)
index c013ddd..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-#ifndef foovolumehfoo
-#define foovolumehfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <inttypes.h>
-#include <polyp/cdecl.h>
-#include <polyp/sample.h>
-
-/** \file
- * Constants and routines for volume handling */
-
-PA_C_DECL_BEGIN
-
-/** Volume specification:
- *  PA_VOLUME_MUTED: silence;
- * < PA_VOLUME_NORM: decreased volume;
- *   PA_VOLUME_NORM: normal volume;
- * > PA_VOLUME_NORM: increased volume */
-typedef uint32_t pa_volume_t;
-
-/** Normal volume (100%) */
-#define PA_VOLUME_NORM (0x10000)
-
-/** Muted volume (0%) */
-#define PA_VOLUME_MUTED (0)
-
-/** A structure encapsulating a per-channel volume */
-struct pa_cvolume {
-    uint8_t channels;
-    pa_volume_t values[PA_CHANNELS_MAX];
-};
-
-/** Return non-zero when *a == *b */
-int pa_cvolume_equal(const struct pa_cvolume *a, const struct pa_cvolume *b);
-
-/** Set the volume of all channels to PA_VOLUME_NORM */
-void pa_cvolume_reset(struct pa_cvolume *a);
-
-/** Set the volume of all channels to PA_VOLUME_MUTED */
-void pa_cvolume_mute(struct pa_cvolume *a);
-
-/** Set the volume of all channels to the specified parameter */
-void pa_cvolume_set(struct pa_cvolume *a, pa_volume_t v);
-
-/** Pretty print a volume structure */
-char *pa_cvolume_snprintf(char *s, size_t l, const struct pa_cvolume *c, unsigned channels);
-
-/** Return the average volume of all channels */
-pa_volume_t pa_cvolume_avg(const struct pa_cvolume *a);
-
-/** Return non-zero if the volume of all channels is equal to the specified value */
-int pa_cvolume_channels_equal_to(const struct pa_cvolume *a, uint8_t channels, pa_volume_t v);
-
-/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. */
-pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b);
-
-/** Convert a decibel value to a volume. \since 0.4 */
-pa_volume_t pa_sw_volume_from_dB(double f);
-
-/** Convert a volume to a decibel value.  \since 0.4 */
-double pa_sw_volume_to_dB(pa_volume_t v);
-
-/** Convert a linear factor to a volume. \since 0.8 */
-pa_volume_t pa_sw_volume_from_linear(double v);
-
-/** Convert a volume to a linear factor. \since 0.8 */
-double pa_sw_volume_to_linear(pa_volume_t v);
-
-#ifdef INFINITY
-#define PA_DECIBEL_MININFTY (-INFINITY)
-#else
-/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */
-#define PA_DECIBEL_MININFTY (-200)
-#endif
-
-PA_C_DECL_END
-
-#endif
diff --git a/polyp/x11wrap.c b/polyp/x11wrap.c
deleted file mode 100644 (file)
index 4d8d693..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <assert.h>
-#include <stdio.h>
-
-#include "llist.h"
-#include "x11wrap.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "props.h"
-
-struct pa_x11_client;
-
-struct pa_x11_internal {
-    PA_LLIST_FIELDS(struct pa_x11_internal);
-    struct pa_x11_wrapper *wrapper;
-    struct pa_io_event* io_event;
-    int fd;
-};
-
-struct pa_x11_wrapper {
-    struct pa_core *core;
-    int ref;
-    
-    char *property_name;
-    Display *display;
-
-    struct pa_defer_event* defer_event;
-    struct pa_io_event* io_event;
-
-    PA_LLIST_HEAD(struct pa_x11_client, clients);
-    PA_LLIST_HEAD(struct pa_x11_internal, internals);
-};
-
-struct pa_x11_client {
-    PA_LLIST_FIELDS(struct pa_x11_client);
-    struct pa_x11_wrapper *wrapper;
-    int (*callback)(struct pa_x11_wrapper *w, XEvent *e, void *userdata);
-    void *userdata;
-};
-
-/* Dispatch all pending X11 events */
-static void work(struct pa_x11_wrapper *w) {
-    assert(w && w->ref >= 1);
-    
-    while (XPending(w->display)) {
-        struct pa_x11_client *c;
-        XEvent e;
-        XNextEvent(w->display, &e);
-
-        for (c = w->clients; c; c = c->next) {
-            assert(c->callback);
-            if (c->callback(w, &e, c->userdata) != 0)
-                break;
-        }
-    }
-}
-
-/* IO notification event for the X11 display connection */
-static void display_io_event(struct pa_mainloop_api *m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    struct pa_x11_wrapper *w = userdata;
-    assert(m && e && fd >= 0 && w && w->ref >= 1);
-    work(w);
-}
-
-/* Deferred notification event. Called once each main loop iteration */
-static void defer_event(struct pa_mainloop_api *m, struct pa_defer_event *e, void *userdata) {
-    struct pa_x11_wrapper *w = userdata;
-    assert(m && e && w && w->ref >= 1);
-    work(w);
-}
-
-/* IO notification event for X11 internal connections */
-static void internal_io_event(struct pa_mainloop_api *m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
-    struct pa_x11_wrapper *w = userdata;
-    assert(m && e && fd >= 0 && w && w->ref >= 1);
-
-    XProcessInternalConnection(w->display, fd);
-}
-
-/* Add a new IO source for the specified X11 internal connection */
-static struct pa_x11_internal* x11_internal_add(struct pa_x11_wrapper *w, int fd) {
-    struct pa_x11_internal *i;
-    assert(i && fd >= 0);
-
-    i = pa_xmalloc(sizeof(struct pa_x11_internal));
-    i->wrapper = w;
-    i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w);
-    i->fd = fd;
-
-    PA_LLIST_PREPEND(struct pa_x11_internal, w->internals, i);
-    return i;
-}
-
-/* Remove an IO source for an X11 internal connection */
-void x11_internal_remove(struct pa_x11_wrapper *w, struct pa_x11_internal *i) {
-    assert(i);
-
-    PA_LLIST_REMOVE(struct pa_x11_internal, w->internals, i);
-    w->core->mainloop->io_free(i->io_event);
-    pa_xfree(i);
-}
-
-/* Implementation of XConnectionWatchProc */
-static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) {
-    struct pa_x11_wrapper *w = (struct pa_x11_wrapper*) userdata;
-    assert(display && w && fd >= 0);
-
-    if (opening)
-        *watch_data = (XPointer) x11_internal_add(w, fd);
-    else
-        x11_internal_remove(w, (struct pa_x11_internal*) *watch_data);
-}
-
-static struct pa_x11_wrapper* x11_wrapper_new(struct pa_core *c, const char *name, const char *t) {
-    struct pa_x11_wrapper*w;
-    Display *d;
-    int r;
-
-    if (!(d = XOpenDisplay(name))) {
-        pa_log(__FILE__": XOpenDisplay() failed\n");
-        return NULL;
-    }
-
-    w = pa_xmalloc(sizeof(struct pa_x11_wrapper));
-    w->core = c;
-    w->ref = 1;
-    w->property_name = pa_xstrdup(t);
-    w->display = d;
-    
-    PA_LLIST_HEAD_INIT(struct pa_x11_client, w->clients);
-    PA_LLIST_HEAD_INIT(struct pa_x11_internal, w->internals);
-
-    w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w);
-    w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w);
-
-    XAddConnectionWatch(d, x11_watch, (XPointer) w);
-    
-    r = pa_property_set(c, w->property_name, w);
-    assert(r >= 0);
-    
-    return w;
-}
-
-static void x11_wrapper_free(struct pa_x11_wrapper*w) {
-    int r;
-    assert(w);
-
-    r = pa_property_remove(w->core, w->property_name);
-    assert(r >= 0);
-
-    assert(!w->clients);
-
-    XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
-    XCloseDisplay(w->display);
-    
-    w->core->mainloop->io_free(w->io_event);
-    w->core->mainloop->defer_free(w->defer_event);
-
-    while (w->internals)
-        x11_internal_remove(w, w->internals);
-    
-    pa_xfree(w->property_name);
-    pa_xfree(w);
-}
-
-struct pa_x11_wrapper* pa_x11_wrapper_get(struct pa_core *c, const char *name) {
-    char t[256];
-    struct pa_x11_wrapper *w;
-    assert(c);
-        
-    snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : "");
-    if ((w = pa_property_get(c, t)))
-        return pa_x11_wrapper_ref(w);
-
-    return x11_wrapper_new(c, name, t);
-}
-
-struct pa_x11_wrapper* pa_x11_wrapper_ref(struct pa_x11_wrapper *w) {
-    assert(w && w->ref >= 1);
-    w->ref++;
-    return w;
-}
-
-void pa_x11_wrapper_unref(struct pa_x11_wrapper* w) {
-    assert(w && w->ref >= 1);
-
-    if (!(--w->ref))
-        x11_wrapper_free(w);
-}
-
-Display *pa_x11_wrapper_get_display(struct pa_x11_wrapper *w) {
-    assert(w && w->ref >= 1);
-    return w->display;
-}
-
-struct pa_x11_client* pa_x11_client_new(struct pa_x11_wrapper *w, int (*cb)(struct pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata) {
-    struct pa_x11_client *c;
-    assert(w && w->ref >= 1);
-
-    c = pa_xmalloc(sizeof(struct pa_x11_client));
-    c->wrapper = w;
-    c->callback = cb;
-    c->userdata = userdata;
-
-    PA_LLIST_PREPEND(struct pa_x11_client, w->clients, c);
-
-    return c;
-}
-
-void pa_x11_client_free(struct pa_x11_client *c) {
-    assert(c && c->wrapper && c->wrapper->ref >= 1);
-
-    PA_LLIST_REMOVE(struct pa_x11_client, c->wrapper->clients, c);
-    pa_xfree(c);
-}
diff --git a/polyp/x11wrap.h b/polyp/x11wrap.h
deleted file mode 100644 (file)
index 9ed690f..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef foox11wraphfoo
-#define foox11wraphfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <X11/Xlib.h>
-
-#include "core.h"
-
-struct pa_x11_wrapper;
-
-/* Return the X11 wrapper for this core. In case no wrapper was
-    existant before, allocate a new one */
-struct pa_x11_wrapper* pa_x11_wrapper_get(struct pa_core *c, const char *name);
-
-/* Increase the wrapper's reference count by one */
-struct pa_x11_wrapper* pa_x11_wrapper_ref(struct pa_x11_wrapper *w);
-
-/* Decrease the reference counter of an X11 wrapper object */
-void pa_x11_wrapper_unref(struct pa_x11_wrapper* w);
-
-/* Return the X11 display object for this connection */
-Display *pa_x11_wrapper_get_display(struct pa_x11_wrapper *w);
-
-struct pa_x11_client;
-
-/* Register an X11 client, that is called for each X11 event */
-struct pa_x11_client* pa_x11_client_new(struct pa_x11_wrapper *w, int (*cb)(struct pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata);
-
-/* Free an X11 client object */
-void pa_x11_client_free(struct pa_x11_client *c);
-
-#endif
diff --git a/polyp/xmalloc.h b/polyp/xmalloc.h
deleted file mode 100644 (file)
index b37ece9..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef foomemoryhfoo
-#define foomemoryhfoo
-
-/* $Id$ */
-
-/***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-  You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <sys/types.h>
-#include <stdlib.h>
-
-void* pa_xmalloc(size_t l);
-void *pa_xmalloc0(size_t l);
-void *pa_xrealloc(void *ptr, size_t size);
-#define pa_xfree free
-
-char *pa_xstrdup(const char *s);
-char *pa_xstrndup(const char *s, size_t l);
-
-void* pa_xmemdup(const void *p, size_t l);
-
-#endif
diff --git a/polyplib-browse.pc.in b/polyplib-browse.pc.in
deleted file mode 100644 (file)
index b77967e..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
-includedir=${prefix}/include
-
-Name: polyplib-browse
-Description: Polypaudio network browsing API
-Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -lpolyp-browse-@PA_MAJORMINOR@
-Cflags: -D_REENTRANT -I${includedir}
-Requires: polyplib-mainloop
diff --git a/polyplib-error.pc.in b/polyplib-error.pc.in
deleted file mode 100644 (file)
index 45f69db..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-prefix=@prefix@
-exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
-includedir=${prefix}/include
-
-Name: polyplib-error
-Description: Error library for the polypaudio sound daemon
-Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -lpolyp-error-@PA_MAJORMINOR@
-Cflags: -D_REENTRANT -I${includedir}
diff --git a/polyplib-glib-mainloop.pc.in b/polyplib-glib-mainloop.pc.in
deleted file mode 100644 (file)
index 431354e..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
-includedir=${prefix}/include
-
-Name: polyplib-glib-mainloop
-Description: GLIB main loop wrapper for polypaudio
-Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -lpolyp-mainloop-glib-@PA_MAJORMINOR@
-Cflags: -D_REENTRANT -I${includedir}
-Requires: polyplib-mainloop
diff --git a/polyplib-glib12-mainloop.pc.in b/polyplib-glib12-mainloop.pc.in
deleted file mode 100644 (file)
index 5c5f408..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
-includedir=${prefix}/include
-
-Name: polyplib-glib12-mainloop
-Description: GLIB main loop wrapper for polypaudio
-Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -lpolyp-mainloop-glib12-@PA_MAJORMINOR@
-Cflags: -D_REENTRANT -I${includedir}
-Requires: polyplib-mainloop
diff --git a/polyplib-mainloop.pc.in b/polyplib-mainloop.pc.in
deleted file mode 100644 (file)
index ab9b3e6..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-prefix=@prefix@
-exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
-includedir=${prefix}/include
-
-Name: polyplib-mainloop
-Description: Main loop support for polypaudio
-Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -lpolyp-mainloop-@PA_MAJORMINOR@
-Cflags: -D_REENTRANT -I${includedir}
diff --git a/polyplib-simple.pc.in b/polyplib-simple.pc.in
deleted file mode 100644 (file)
index a285034..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
-includedir=${prefix}/include
-
-Name: polyplib-simple
-Description: Simplified synchronous client interface to polypaudio
-Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -lpolyp-simple-@PA_MAJORMINOR@
-Cflags: -D_REENTRANT -I${includedir}
-Requires: polyplib polyplib-mainloop polyplib-error
diff --git a/polyplib.pc.in b/polyplib.pc.in
deleted file mode 100644 (file)
index c79d98d..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
-includedir=${prefix}/include
-
-Name: polyplib
-Description: Client interface to polypaudio
-Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -lpolyp-@PA_MAJORMINOR@
-Cflags: -D_REENTRANT -I${includedir}
-Requires: polyplib-error polyplib-mainloop
diff --git a/pulseaudio-text.svg b/pulseaudio-text.svg
new file mode 100644 (file)
index 0000000..0e12613
--- /dev/null
@@ -0,0 +1,388 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48px"
+   height="48px"
+   id="svg2161"
+   sodipodi:version="0.32"
+   inkscape:version="0.45"
+   sodipodi:docbase="/home/lennart/projects/pulseaudio"
+   sodipodi:docname="pulseaudio.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   sodipodi:modified="TRUE">
+  <defs
+     id="defs2163">
+    <linearGradient
+       id="linearGradient3093">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop3095" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop3097" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3093"
+       id="radialGradient2472"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.266476,0,283.9565)"
+       cx="224.5"
+       cy="387.11252"
+       fx="224.5"
+       fy="387.11252"
+       r="174.5" />
+    <linearGradient
+       id="linearGradient2503">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop2505" />
+      <stop
+         style="stop-color:#141413;stop-opacity:1;"
+         offset="1"
+         id="stop2507" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2503"
+       id="linearGradient1476"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       id="linearGradient2495">
+      <stop
+         style="stop-color:#0a0a09;stop-opacity:1;"
+         offset="0"
+         id="stop2497" />
+      <stop
+         style="stop-color:#282927;stop-opacity:1;"
+         offset="1"
+         id="stop2499" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2495"
+       id="linearGradient1474"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="674"
+       y1="276.11252"
+       x2="505"
+       y2="199.11252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2399"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       id="linearGradient2535">
+      <stop
+         id="stop2537"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:0.36078432;" />
+      <stop
+         id="stop2539"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2397"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-291.933,627.3998)"
+       x1="532"
+       y1="131.40625"
+       x2="667.5"
+       y2="357.40625" />
+    <linearGradient
+       id="linearGradient3072">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3074" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3076" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3072"
+       id="linearGradient2395"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="76.360481"
+       x2="585"
+       y2="170.3912" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3093"
+       id="radialGradient2234"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.266476,0,283.9565)"
+       cx="224.5"
+       cy="387.11252"
+       fx="224.5"
+       fy="387.11252"
+       r="174.5" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2495"
+       id="linearGradient2236"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="674"
+       y1="276.11252"
+       x2="505"
+       y2="199.11252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2503"
+       id="linearGradient2238"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3072"
+       id="linearGradient2240"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="76.360481"
+       x2="585"
+       y2="170.3912" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2242"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-291.933,627.3998)"
+       x1="532"
+       y1="131.40625"
+       x2="667.5"
+       y2="357.40625" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2244"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2495"
+       id="linearGradient2255"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="674"
+       y1="276.11252"
+       x2="505"
+       y2="199.11252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2503"
+       id="linearGradient2257"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3093"
+       id="radialGradient2260"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.266476,-145.39702,-74.948037)"
+       cx="224.5"
+       cy="387.11252"
+       fx="224.5"
+       fy="387.11252"
+       r="174.5" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="4.9497475"
+     inkscape:cx="16.230436"
+     inkscape:cy="-2.4336194"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="2043"
+     inkscape:window-height="794"
+     inkscape:window-x="180"
+     inkscape:window-y="140"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <metadata
+     id="metadata2166">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <cc:license
+           rdf:resource="http://www.gnu.org/copyleft/gpl.html" />
+        <dc:title>PulseAudio logotype</dc:title>
+        <dc:date>2006-08-28</dc:date>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Pierre Ossman &lt;ossman@cendio.se&gt; for Cendio AB</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:rights>
+          <cc:Agent>
+            <dc:title />
+          </cc:Agent>
+        </dc:rights>
+        <dc:contributor>
+          <cc:Agent>
+            <dc:title>Rafael Jannone (basic idea)</dc:title>
+          </cc:Agent>
+        </dc:contributor>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <rect
+       ry="6.5049205"
+       y="2.2257283"
+       x="5.4760308"
+       height="37.047943"
+       width="37.047943"
+       id="rect2371"
+       style="fill:url(#linearGradient2255);fill-opacity:1;stroke:url(#linearGradient2257);stroke-width:0.99792439;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       inkscape:export-filename="/home/lennart/test.png"
+       inkscape:export-xdpi="165.5896"
+       inkscape:export-ydpi="165.5896" />
+    <g
+       transform="matrix(0.124741,0,0,0.124741,-61.69688,-99.94425)"
+       id="g2415"
+       inkscape:export-filename="/home/lennart/test.png"
+       inkscape:export-xdpi="165.5896"
+       inkscape:export-ydpi="165.5896">
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path2417"
+         sodipodi:cx="-1"
+         sodipodi:cy="863.61249"
+         sodipodi:rx="23"
+         sodipodi:ry="23"
+         d="M 22 863.61249 A 23 23 0 1 1  -24,863.61249 A 23 23 0 1 1  22 863.61249 z"
+         transform="matrix(1.676363,0,0,1.676363,688.6772,-480.168)" />
+      <path
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="M 666.92273,892.01313 C 633.50485,900.88553 608.86021,931.34683 608.86023,967.54442 C 608.86023,1003.7419 633.50486,1034.2345 666.92273,1043.1069 C 642.81497,1032.2877 625.48523,1002.5195 625.48523,967.54442 C 625.48522,932.56943 642.81496,902.83233 666.92273,892.01313 z M 707.07898,892.01313 C 731.18675,902.83233 748.51648,932.56933 748.51648,967.54442 C 748.51648,1002.5195 731.18674,1032.2877 707.07898,1043.1069 C 740.49686,1034.2345 765.1415,1003.7419 765.14148,967.54442 C 765.14148,931.34693 740.49687,900.88553 707.07898,892.01313 z "
+         id="path2419" />
+      <path
+         id="path2421"
+         d="M 655.64705,849.58672 C 603.46201,863.44178 564.97718,911.00985 564.97721,967.53562 C 564.97721,1024.0613 603.46203,1071.6783 655.64705,1085.5333 C 618.0006,1068.6381 590.93865,1022.1524 590.93865,967.53562 C 590.93863,912.91905 618.00059,866.48188 655.64705,849.58672 z M 718.35466,849.58672 C 756.00112,866.48188 783.06306,912.91889 783.06306,967.53562 C 783.06306,1022.1524 756.00111,1068.6381 718.35466,1085.5333 C 770.5397,1071.6783 809.02453,1024.0613 809.0245,967.53562 C 809.0245,911.01001 770.53972,863.44178 718.35466,849.58672 z "
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+    <g
+       id="g1494"
+       transform="matrix(0.124741,0,0,0.124741,-13.36814,-87.21636)"
+       inkscape:export-filename="/home/lennart/test.png"
+       inkscape:export-xdpi="165.5896"
+       inkscape:export-ydpi="165.5896">
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         d="M 495.15625,93.84375 C 468.52243,93.84375 447.21875,115.11921 447.21875,141.75 L 447.21875,334.46875 C 447.21875,361.09954 468.52545,382.40625 495.15625,382.40625 L 687.84375,382.40625 C 714.47454,382.40625 735.78125,361.09955 735.78125,334.46875 L 735.78125,141.75 C 735.78125,115.11921 714.47755,93.84375 687.84375,93.84375 L 495.15625,93.84375 z "
+         id="path2373"
+         style="fill:url(#linearGradient2240);fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         inkscape:original="M 495.15625 89.625 C 466.26648 89.625 443 112.86023 443 141.75 L 443 334.46875 C 443 363.35852 466.26647 386.625 495.15625 386.625 L 687.84375 386.625 C 716.73352 386.625 740 363.35853 740 334.46875 L 740 141.75 C 740 112.86023 716.7335 89.625 687.84375 89.625 L 495.15625 89.625 z "
+         inkscape:radius="-4.2074337"
+         sodipodi:type="inkscape:offset"
+         transform="translate(-291.933,627.3998)" />
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="M 436.05138,821.4365 C 397.62524,862.62866 358.12861,865.874 299.93097,865.874 C 242.63828,865.874 199.11564,893.22114 163.06701,927.96775 L 163.06701,961.86851 C 163.06701,985.0884 181.12504,1001.9935 203.22326,1001.9935 L 395.91076,1006.0248 C 420.50531,1006.0248 436.03576,986.46307 436.03576,961.86851 L 436.05138,821.4365 z "
+         id="path2375"
+         sodipodi:nodetypes="cscccccc" />
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         sodipodi:type="inkscape:offset"
+         inkscape:radius="-8"
+         inkscape:original="M 495.15625 89.625 C 466.26648 89.625 443 112.86023 443 141.75 L 443 334.46875 C 443 363.35852 466.26647 386.625 495.15625 386.625 L 687.84375 386.625 C 716.73352 386.625 740 363.35853 740 334.46875 L 740 141.75 C 740 112.86023 716.7335 89.625 687.84375 89.625 L 495.15625 89.625 z "
+         style="fill:none;fill-opacity:1;stroke:url(#linearGradient2244);stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path2377"
+         d="M 495.15625,97.625 C 470.55593,97.625 451,117.15545 451,141.75 L 451,334.46875 C 451,359.0633 470.56169,378.625 495.15625,378.625 L 687.84375,378.625 C 712.4383,378.625 732,359.06331 732,334.46875 L 732,141.75 C 732,117.15545 712.44405,97.625 687.84375,97.625 L 495.15625,97.625 z "
+         transform="translate(-291.933,627.3998)" />
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-size:30.33161926px;font-style:normal;font-weight:normal;line-height:125%;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+       x="49.689053"
+       y="37.570499"
+       id="text2188"
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/lennart/test.png"
+       inkscape:export-xdpi="165.5896"
+       inkscape:export-ydpi="165.5896"><tspan
+         sodipodi:role="line"
+         id="tspan2190"
+         x="49.689053"
+         y="37.570499"
+         style="font-size:30.33161926px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Tuffy"><tspan
+   style="font-size:30.33161926px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Tuffy"
+   id="tspan2196">Pulse</tspan>Audio</tspan></text>
+  </g>
+</svg>
diff --git a/pulseaudio.svg b/pulseaudio.svg
new file mode 100644 (file)
index 0000000..a79e03d
--- /dev/null
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48px"
+   height="48px"
+   id="svg2161"
+   sodipodi:version="0.32"
+   inkscape:version="0.44"
+   sodipodi:docbase="/home/ossman/devel/pulseaudio"
+   sodipodi:docname="pulseaudio.svg">
+  <defs
+     id="defs2163">
+    <linearGradient
+       id="linearGradient3093">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop3095" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop3097" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3093"
+       id="radialGradient2472"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.266476,0,283.9565)"
+       cx="224.5"
+       cy="387.11252"
+       fx="224.5"
+       fy="387.11252"
+       r="174.5" />
+    <linearGradient
+       id="linearGradient2503">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop2505" />
+      <stop
+         style="stop-color:#141413;stop-opacity:1;"
+         offset="1"
+         id="stop2507" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2503"
+       id="linearGradient1476"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       id="linearGradient2495">
+      <stop
+         style="stop-color:#0a0a09;stop-opacity:1;"
+         offset="0"
+         id="stop2497" />
+      <stop
+         style="stop-color:#282927;stop-opacity:1;"
+         offset="1"
+         id="stop2499" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2495"
+       id="linearGradient1474"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.124741,0,0,0.124741,-49.78411,-8.952609)"
+       x1="674"
+       y1="276.11252"
+       x2="505"
+       y2="199.11252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2399"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="390.61252"
+       x2="585"
+       y2="85.376541" />
+    <linearGradient
+       id="linearGradient2535">
+      <stop
+         id="stop2537"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:0.36078432;" />
+      <stop
+         id="stop2539"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2535"
+       id="linearGradient2397"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-291.933,627.3998)"
+       x1="532"
+       y1="131.40625"
+       x2="667.5"
+       y2="357.40625" />
+    <linearGradient
+       id="linearGradient3072">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3074" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3076" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3072"
+       id="linearGradient2395"
+       gradientUnits="userSpaceOnUse"
+       x1="585"
+       y1="76.360481"
+       x2="585"
+       y2="170.3912" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="4.9497475"
+     inkscape:cx="20.060638"
+     inkscape:cy="18.992734"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="872"
+     inkscape:window-height="624"
+     inkscape:window-x="325"
+     inkscape:window-y="224" />
+  <metadata
+     id="metadata2166">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <cc:license
+           rdf:resource="http://www.gnu.org/copyleft/gpl.html" />
+        <dc:title>PulseAudio logotype</dc:title>
+        <dc:date>2006-08-28</dc:date>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Pierre Ossman &lt;ossman@cendio.se&gt; for Cendio AB</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:rights>
+          <cc:Agent>
+            <dc:title></dc:title>
+          </cc:Agent>
+        </dc:rights>
+        <dc:contributor>
+          <cc:Agent>
+            <dc:title>Rafael Jannone (basic idea)</dc:title>
+          </cc:Agent>
+        </dc:contributor>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/SourceCode" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.36679538;color:black;fill:url(#radialGradient2472);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:38.81499863;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       id="path2470"
+       sodipodi:cx="224.5"
+       sodipodi:cy="387.11252"
+       sodipodi:rx="174.5"
+       sodipodi:ry="46.5"
+       d="M 399 387.11252 A 174.5 46.5 0 1 1  50,387.11252 A 174.5 46.5 0 1 1  399 387.11252 z"
+       transform="matrix(0.137443,0,0,0.154237,-6.855952,-20.43595)"
+       inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+       inkscape:export-xdpi="44.099998"
+       inkscape:export-ydpi="44.099998" />
+    <rect
+       ry="6.5049205"
+       y="2.2257283"
+       x="5.4760308"
+       height="37.047943"
+       width="37.047943"
+       id="rect2371"
+       style="fill:url(#linearGradient1474);fill-opacity:1;stroke:url(#linearGradient1476);stroke-width:0.99792439;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+       inkscape:export-xdpi="44.099998"
+       inkscape:export-ydpi="44.099998" />
+    <g
+       transform="matrix(0.124741,0,0,0.124741,-61.69688,-99.94425)"
+       id="g2415"
+       inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+       inkscape:export-xdpi="44.099998"
+       inkscape:export-ydpi="44.099998">
+      <path
+         sodipodi:type="arc"
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path2417"
+         sodipodi:cx="-1"
+         sodipodi:cy="863.61249"
+         sodipodi:rx="23"
+         sodipodi:ry="23"
+         d="M 22 863.61249 A 23 23 0 1 1  -24,863.61249 A 23 23 0 1 1  22 863.61249 z"
+         transform="matrix(1.676363,0,0,1.676363,688.6772,-480.168)" />
+      <path
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         d="M 666.92273,892.01313 C 633.50485,900.88553 608.86021,931.34683 608.86023,967.54442 C 608.86023,1003.7419 633.50486,1034.2345 666.92273,1043.1069 C 642.81497,1032.2877 625.48523,1002.5195 625.48523,967.54442 C 625.48522,932.56943 642.81496,902.83233 666.92273,892.01313 z M 707.07898,892.01313 C 731.18675,902.83233 748.51648,932.56933 748.51648,967.54442 C 748.51648,1002.5195 731.18674,1032.2877 707.07898,1043.1069 C 740.49686,1034.2345 765.1415,1003.7419 765.14148,967.54442 C 765.14148,931.34693 740.49687,900.88553 707.07898,892.01313 z "
+         id="path2419" />
+      <path
+         id="path2421"
+         d="M 655.64705,849.58672 C 603.46201,863.44178 564.97718,911.00985 564.97721,967.53562 C 564.97721,1024.0613 603.46203,1071.6783 655.64705,1085.5333 C 618.0006,1068.6381 590.93865,1022.1524 590.93865,967.53562 C 590.93863,912.91905 618.00059,866.48188 655.64705,849.58672 z M 718.35466,849.58672 C 756.00112,866.48188 783.06306,912.91889 783.06306,967.53562 C 783.06306,1022.1524 756.00111,1068.6381 718.35466,1085.5333 C 770.5397,1071.6783 809.02453,1024.0613 809.0245,967.53562 C 809.0245,911.01001 770.53972,863.44178 718.35466,849.58672 z "
+         style="opacity:1;fill:#729fcf;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+    <g
+       id="g1494"
+       transform="matrix(0.124741,0,0,0.124741,-13.36814,-87.21636)">
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         d="M 495.15625,93.84375 C 468.52243,93.84375 447.21875,115.11921 447.21875,141.75 L 447.21875,334.46875 C 447.21875,361.09954 468.52545,382.40625 495.15625,382.40625 L 687.84375,382.40625 C 714.47454,382.40625 735.78125,361.09955 735.78125,334.46875 L 735.78125,141.75 C 735.78125,115.11921 714.47755,93.84375 687.84375,93.84375 L 495.15625,93.84375 z "
+         id="path2373"
+         style="fill:url(#linearGradient2395);fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         inkscape:original="M 495.15625 89.625 C 466.26648 89.625 443 112.86023 443 141.75 L 443 334.46875 C 443 363.35852 466.26647 386.625 495.15625 386.625 L 687.84375 386.625 C 716.73352 386.625 740 363.35853 740 334.46875 L 740 141.75 C 740 112.86023 716.7335 89.625 687.84375 89.625 L 495.15625 89.625 z "
+         inkscape:radius="-4.2074337"
+         sodipodi:type="inkscape:offset"
+         transform="translate(-291.933,627.3998)" />
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         style="fill:url(#linearGradient2397);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="M 436.05138,821.4365 C 397.62524,862.62866 358.12861,865.874 299.93097,865.874 C 242.63828,865.874 199.11564,893.22114 163.06701,927.96775 L 163.06701,961.86851 C 163.06701,985.0884 181.12504,1001.9935 203.22326,1001.9935 L 395.91076,1006.0248 C 420.50531,1006.0248 436.03576,986.46307 436.03576,961.86851 L 436.05138,821.4365 z "
+         id="path2375"
+         sodipodi:nodetypes="cscccccc" />
+      <path
+         inkscape:export-ydpi="44.099998"
+         inkscape:export-xdpi="44.099998"
+         inkscape:export-filename="/home/ossman/Desktop/pa4.png"
+         sodipodi:type="inkscape:offset"
+         inkscape:radius="-8"
+         inkscape:original="M 495.15625 89.625 C 466.26648 89.625 443 112.86023 443 141.75 L 443 334.46875 C 443 363.35852 466.26647 386.625 495.15625 386.625 L 687.84375 386.625 C 716.73352 386.625 740 363.35853 740 334.46875 L 740 141.75 C 740 112.86023 716.7335 89.625 687.84375 89.625 L 495.15625 89.625 z "
+         style="fill:none;fill-opacity:1;stroke:url(#linearGradient2399);stroke-width:8;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path2377"
+         d="M 495.15625,97.625 C 470.55593,97.625 451,117.15545 451,141.75 L 451,334.46875 C 451,359.0633 470.56169,378.625 495.15625,378.625 L 687.84375,378.625 C 712.4383,378.625 732,359.06331 732,334.46875 L 732,141.75 C 732,117.15545 712.44405,97.625 687.84375,97.625 L 495.15625,97.625 z "
+         transform="translate(-291.933,627.3998)" />
+    </g>
+  </g>
+</svg>
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644 (file)
index 0000000..f3ed2e2
--- /dev/null
@@ -0,0 +1,56 @@
+*.lo
+*.o
+*.la
+.deps
+.libs
+Makefile
+Makefile.in
+asyncmsgq-test
+asyncq-test
+bt-proximity-helper
+channelmap-test
+client.conf
+close-test
+cpulimit-test
+cpulimit-test2
+daemon.conf
+default.pa
+envelope-test
+esdcompat
+flist-test
+gconf-helper
+get-binary-name-test
+hook-list-test
+interpol-test
+ipacl-test
+mainloop-test
+mainloop-test-glib
+mcalign-test
+memblock-test
+memblockq-test
+mix-test
+pabrowse
+pacat
+pacat-simple
+pacmd
+pactl
+paplay
+parec-simple
+pasuspender
+pax11publish
+proplist-test
+pulseaudio
+queue-test
+remix-test
+resampler-test
+rtpoll-test
+rtstutter
+sig2str-test
+smoother-test
+stripnul
+strlist-test
+sync-playback
+thread-mainloop-test
+thread-test
+utf8-test
+voltest
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..9cce6ed
--- /dev/null
@@ -0,0 +1,1607 @@
+# This file is part of PulseAudio.
+#
+# Copyright 2004-2006 Lennart Poettering
+# Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+# Copyright 2006 Diego Pettenò
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+
+###################################
+#       Extra directories         #
+###################################
+
+pulseincludedir=$(includedir)/pulse
+pulsecoreincludedir=$(includedir)/pulsecore
+pulseconfdir=$(sysconfdir)/pulse
+pulselibexecdir=$(libexecdir)/pulse
+xdgautostartdir=$(sysconfdir)/xdg/autostart
+
+###################################
+#            Defines              #
+###################################
+
+PA_BINARY=$(bindir)/pulseaudio$(EXEEXT)
+if OS_IS_WIN32
+PA_DEFAULT_CONFIG_DIR=%PULSE_ROOT%
+else
+PA_DEFAULT_CONFIG_DIR=$(pulseconfdir)
+endif
+
+###################################
+#     Compiler/linker flags       #
+###################################
+
+AM_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src/modules -I$(top_builddir)/src/modules/rtp -I$(top_builddir)/src/modules/gconf
+AM_CFLAGS += $(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS
+AM_CFLAGS += $(LTDLINCL)
+AM_CFLAGS += $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS)
+AM_CFLAGS += -DPA_DLSEARCHPATH=\"$(modlibexecdir)\"
+AM_CFLAGS += -DPA_DEFAULT_CONFIG_DIR=\"$(PA_DEFAULT_CONFIG_DIR)\"
+AM_CFLAGS += -DPA_BINARY=\"$(PA_BINARY)\"
+AM_CFLAGS += -DPA_SYSTEM_RUNTIME_PATH=\"$(PA_SYSTEM_RUNTIME_PATH)\"
+AM_CFLAGS += -DPA_SYSTEM_CONFIG_PATH=\"$(PA_SYSTEM_CONFIG_PATH)\"
+AM_CFLAGS += -DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\"
+AM_CFLAGS += -DAO_REQUIRE_CAS
+
+# This cool debug trap works on i386/gcc only
+AM_CFLAGS += '-DDEBUG_TRAP=__asm__("int $$3")'
+
+AM_LIBADD = $(PTHREAD_LIBS)
+AM_LDADD = $(PTHREAD_LIBS)
+
+# Only required on some platforms but defined for all to avoid errors
+AM_LDFLAGS = -Wl,-no-undefined -Wl,--gc-sections
+
+if STATIC_BINS
+BINLDFLAGS = -static
+endif
+
+if OS_IS_WIN32
+AM_LDFLAGS+=-Wl,--export-all-symbols
+WINSOCK_LIBS=-lwsock32 -lws2_32 -lwininet
+endif
+
+if OS_IS_WIN32
+PA_THREAD_OBJS = \
+               pulsecore/mutex-win32.c pulsecore/mutex.h \
+               pulsecore/thread-win32.c pulsecore/thread.h \
+               pulsecore/semaphore-win32.c pulsecore/semaphore.h
+else
+PA_THREAD_OBJS = \
+               pulsecore/mutex-posix.c pulsecore/mutex.h \
+               pulsecore/thread-posix.c pulsecore/thread.h \
+               pulsecore/semaphore-posix.c pulsecore/semaphore.h
+endif
+
+###################################
+#          Extra files            #
+###################################
+
+EXTRA_DIST = \
+               pulse/client.conf.in \
+               pulse/version.h.in \
+               daemon/daemon.conf.in \
+               daemon/default.pa.in \
+               daemon/default.pa.win32 \
+               depmod.py \
+               daemon/esdcompat.in \
+               utils/padsp \
+               modules/module-defs.h.m4 \
+               daemon/pulseaudio-module-xsmp.desktop \
+               map-file \
+               daemon/org.pulseaudio.policy
+
+pulseconf_DATA = \
+               default.pa \
+               daemon.conf \
+               client.conf
+
+if HAVE_X11
+xdgautostart_DATA = \
+               daemon/pulseaudio-module-xsmp.desktop
+endif
+
+BUILT_SOURCES = \
+               pulse/version.h
+
+###################################
+#          Main daemon            #
+###################################
+
+bin_PROGRAMS = pulseaudio
+
+pulseaudio_SOURCES = \
+               daemon/caps.h daemon/caps.c \
+               daemon/cmdline.c daemon/cmdline.h \
+               daemon/cpulimit.c daemon/cpulimit.h \
+               daemon/daemon-conf.c daemon/daemon-conf.h \
+               daemon/dumpmodules.c daemon/dumpmodules.h \
+               daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
+               daemon/main.c
+
+pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)
+pulseaudio_CPPFLAGS = $(AM_CPPFLAGS)
+pulseaudio_LDADD = $(AM_LDADD) libpulsecore.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS)
+# This is needed because automake doesn't properly expand the foreach below
+pulseaudio_DEPENDENCIES = libpulsecore.la $(PREOPEN_LIBS)
+
+if PREOPEN_MODS
+PREOPEN_LIBS = $(PREOPEN_MODS)
+else
+PREOPEN_LIBS = $(modlibexec_LTLIBRARIES)
+endif
+
+if FORCE_PREOPEN
+pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlpreopen force $(foreach f,$(PREOPEN_LIBS),-dlpreopen $(f))
+else
+pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f))
+endif
+
+if HAVE_POLKIT
+
+policy_DATA = daemon/org.pulseaudio.policy
+
+pulseaudio_SOURCES += daemon/polkit.c daemon/polkit.h
+pulseaudio_CFLAGS += $(POLKIT_CFLAGS)
+pulseaudio_LDADD += $(POLKIT_LIBS)
+
+
+endif
+
+###################################
+#       Utility programs          #
+###################################
+
+bin_PROGRAMS += \
+               pacat \
+               pactl \
+               paplay \
+               pasuspender
+
+if HAVE_AF_UNIX
+bin_PROGRAMS += pacmd
+endif
+
+if HAVE_X11
+bin_PROGRAMS += pax11publish
+endif
+
+if HAVE_AVAHI
+bin_PROGRAMS += pabrowse
+endif
+
+bin_SCRIPTS = esdcompat
+
+pacat_SOURCES = utils/pacat.c
+pacat_LDADD = $(AM_LDADD) libpulse.la
+pacat_CFLAGS = $(AM_CFLAGS)
+pacat_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+paplay_SOURCES = utils/paplay.c
+paplay_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
+paplay_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
+paplay_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pactl_SOURCES = utils/pactl.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
+pactl_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
+pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
+pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pasuspender_SOURCES = utils/pasuspender.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
+pasuspender_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
+pasuspender_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
+pasuspender_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pacmd_SOURCES = utils/pacmd.c pulsecore/pid.c pulsecore/pid.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
+pacmd_CFLAGS = $(AM_CFLAGS)
+pacmd_LDADD = $(AM_LDADD) libpulse.la
+pacmd_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pax11publish_SOURCES = utils/pax11publish.c pulsecore/x11prop.c pulsecore/x11prop.h pulse/client-conf.c pulse/client-conf.h pulsecore/authkey.h pulsecore/authkey.c pulsecore/random.h pulsecore/random.c pulsecore/conf-parser.c pulsecore/conf-parser.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h  pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
+pax11publish_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+pax11publish_LDADD = $(AM_LDADD) libpulse.la $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
+pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pabrowse_SOURCES = utils/pabrowse.c
+pabrowse_LDADD = $(AM_LDADD) libpulse.la libpulse-browse.la
+pabrowse_CFLAGS = $(AM_CFLAGS)
+pabrowse_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+###################################
+#         Test programs           #
+###################################
+
+noinst_PROGRAMS = \
+               mainloop-test \
+               mcalign-test \
+               pacat-simple \
+               parec-simple \
+               strlist-test \
+               close-test \
+               voltest \
+               memblockq-test \
+               sync-playback \
+               interpol-test \
+               channelmap-test \
+               thread-mainloop-test \
+               utf8-test \
+               get-binary-name-test \
+               ipacl-test \
+               hook-list-test \
+               memblock-test \
+               thread-test \
+               flist-test \
+               asyncq-test \
+               asyncmsgq-test \
+               queue-test \
+               rtpoll-test \
+               sig2str-test \
+               resampler-test \
+               smoother-test \
+               mix-test \
+               remix-test \
+               envelope-test \
+               proplist-test \
+               rtstutter \
+               stripnul
+
+if HAVE_SIGXCPU
+noinst_PROGRAMS += \
+               cpulimit-test \
+               cpulimit-test2
+endif
+
+if HAVE_GLIB20
+noinst_PROGRAMS += \
+               mainloop-test-glib
+endif
+
+mainloop_test_SOURCES = tests/mainloop-test.c
+mainloop_test_CFLAGS = $(AM_CFLAGS)
+mainloop_test_LDADD = $(AM_LDADD) libpulse.la
+mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+thread_mainloop_test_SOURCES = tests/thread-mainloop-test.c
+thread_mainloop_test_CFLAGS = $(AM_CFLAGS)
+thread_mainloop_test_LDADD = $(AM_LDADD) libpulsecore.la libpulse.la
+thread_mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+utf8_test_SOURCES = tests/utf8-test.c
+utf8_test_CFLAGS = $(AM_CFLAGS)
+utf8_test_LDADD = $(AM_LDADD) libpulsecore.la
+utf8_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+get_binary_name_test_SOURCES = tests/get-binary-name-test.c
+get_binary_name_test_CFLAGS = $(AM_CFLAGS)
+get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la
+get_binary_name_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+ipacl_test_SOURCES = tests/ipacl-test.c \
+               pulsecore/ipacl.c pulsecore/ipacl.h \
+               pulsecore/inet_pton.c pulsecore/inet_pton.h
+ipacl_test_CFLAGS = $(AM_CFLAGS)
+ipacl_test_LDADD = $(AM_LDADD) libpulsecore.la
+ipacl_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+hook_list_test_SOURCES = tests/hook-list-test.c
+hook_list_test_CFLAGS = $(AM_CFLAGS)
+hook_list_test_LDADD = $(AM_LDADD) libpulsecore.la
+hook_list_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+memblock_test_SOURCES = tests/memblock-test.c
+memblock_test_CFLAGS = $(AM_CFLAGS)
+memblock_test_LDADD = $(AM_LDADD) libpulsecore.la
+memblock_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+thread_test_SOURCES = tests/thread-test.c
+thread_test_CFLAGS = $(AM_CFLAGS)
+thread_test_LDADD = $(AM_LDADD) libpulsecore.la
+thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+flist_test_SOURCES = tests/flist-test.c
+flist_test_CFLAGS = $(AM_CFLAGS)
+flist_test_LDADD = $(AM_LDADD) libpulsecore.la
+flist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+asyncq_test_SOURCES = tests/asyncq-test.c
+asyncq_test_CFLAGS = $(AM_CFLAGS)
+asyncq_test_LDADD = $(AM_LDADD) libpulsecore.la
+asyncq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+asyncmsgq_test_SOURCES = tests/asyncmsgq-test.c
+asyncmsgq_test_CFLAGS = $(AM_CFLAGS)
+asyncmsgq_test_LDADD = $(AM_LDADD) libpulsecore.la
+asyncmsgq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+queue_test_SOURCES = tests/queue-test.c
+queue_test_CFLAGS = $(AM_CFLAGS)
+queue_test_LDADD = $(AM_LDADD) libpulsecore.la
+queue_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+rtpoll_test_SOURCES = tests/rtpoll-test.c
+rtpoll_test_CFLAGS = $(AM_CFLAGS)
+rtpoll_test_LDADD = $(AM_LDADD) libpulsecore.la
+rtpoll_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+mcalign_test_SOURCES = tests/mcalign-test.c
+mcalign_test_CFLAGS = $(AM_CFLAGS)
+mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la
+mcalign_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+pacat_simple_SOURCES = tests/pacat-simple.c
+pacat_simple_LDADD = $(AM_LDADD) libpulse.la libpulse-simple.la
+pacat_simple_CFLAGS = $(AM_CFLAGS)
+pacat_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+parec_simple_SOURCES = tests/parec-simple.c
+parec_simple_LDADD = $(AM_LDADD) libpulse.la libpulse-simple.la
+parec_simple_CFLAGS = $(AM_CFLAGS)
+parec_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+strlist_test_SOURCES = tests/strlist-test.c
+strlist_test_CFLAGS = $(AM_CFLAGS)
+strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la
+strlist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+close_test_SOURCES = tests/close-test.c
+close_test_CFLAGS = $(AM_CFLAGS)
+close_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la
+close_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+voltest_SOURCES = tests/voltest.c
+voltest_CFLAGS = $(AM_CFLAGS)
+voltest_LDADD = $(AM_LDADD) libpulse.la
+voltest_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+channelmap_test_SOURCES = tests/channelmap-test.c
+channelmap_test_CFLAGS = $(AM_CFLAGS)
+channelmap_test_LDADD = $(AM_LDADD) libpulse.la
+channelmap_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+cpulimit_test_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h
+cpulimit_test_CFLAGS = $(AM_CFLAGS)
+cpulimit_test_LDADD = $(AM_LDADD) libpulsecore.la
+cpulimit_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+cpulimit_test2_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h
+cpulimit_test2_CFLAGS = $(AM_CFLAGS) -DTEST2
+cpulimit_test2_LDADD = $(AM_LDADD) libpulsecore.la
+cpulimit_test2_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+mainloop_test_glib_SOURCES = $(mainloop_test_SOURCES)
+mainloop_test_glib_CFLAGS = $(mainloop_test_CFLAGS) $(GLIB20_CFLAGS) -DGLIB_MAIN_LOOP
+mainloop_test_glib_LDADD = $(mainloop_test_LDADD) $(GLIB20_LIBS) libpulse-mainloop-glib.la
+mainloop_test_glib_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+memblockq_test_SOURCES = tests/memblockq-test.c
+memblockq_test_CFLAGS = $(AM_CFLAGS)
+memblockq_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la
+memblockq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+sync_playback_SOURCES = tests/sync-playback.c
+sync_playback_LDADD = $(AM_LDADD) libpulse.la
+sync_playback_CFLAGS = $(AM_CFLAGS)
+sync_playback_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+interpol_test_SOURCES = tests/interpol-test.c
+interpol_test_LDADD = $(AM_LDADD) libpulse.la libpulsecore.la
+interpol_test_CFLAGS = $(AM_CFLAGS)
+interpol_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+sig2str_test_SOURCES = tests/sig2str-test.c
+sig2str_test_LDADD = $(AM_LDADD) libpulsecore.la
+sig2str_test_CFLAGS = $(AM_CFLAGS)
+sig2str_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+resampler_test_SOURCES = tests/resampler-test.c
+resampler_test_LDADD = $(AM_LDADD) libpulsecore.la
+resampler_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+resampler_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+mix_test_SOURCES = tests/mix-test.c
+mix_test_LDADD = $(AM_LDADD) libpulsecore.la
+mix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+mix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+remix_test_SOURCES = tests/remix-test.c
+remix_test_LDADD = $(AM_LDADD) libpulsecore.la
+remix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+remix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+smoother_test_SOURCES = tests/smoother-test.c
+smoother_test_LDADD = $(AM_LDADD) libpulsecore.la
+smoother_test_CFLAGS = $(AM_CFLAGS)
+smoother_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+envelope_test_SOURCES = tests/envelope-test.c
+envelope_test_LDADD = $(AM_LDADD) libpulsecore.la
+envelope_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+proplist_test_SOURCES = tests/proplist-test.c
+proplist_test_LDADD = $(AM_LDADD) libpulsecore.la
+proplist_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+proplist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+rtstutter_SOURCES = tests/rtstutter.c
+rtstutter_LDADD = $(AM_LDADD) libpulsecore.la
+rtstutter_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+stripnul_SOURCES = tests/stripnul.c
+stripnul_LDADD = $(AM_LDADD) libpulsecore.la
+stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
+
+###################################
+#         Client library          #
+###################################
+
+pulseinclude_HEADERS = \
+               pulse/cdecl.h \
+               pulse/channelmap.h \
+               pulse/context.h \
+               pulse/def.h \
+               pulse/error.h \
+               pulse/introspect.h \
+               pulse/mainloop.h \
+               pulse/mainloop-api.h \
+               pulse/mainloop-signal.h \
+               pulse/operation.h \
+               pulse/pulseaudio.h \
+               pulse/sample.h \
+               pulse/scache.h \
+               pulse/simple.h \
+               pulse/stream.h \
+               pulse/subscribe.h \
+               pulse/thread-mainloop.h \
+               pulse/timeval.h \
+               pulse/utf8.h \
+               pulse/util.h \
+               pulse/version.h \
+               pulse/volume.h \
+               pulse/xmalloc.h \
+               pulse/proplist.h \
+               pulse/gccmacro.h
+
+if HAVE_AVAHI
+pulseinclude_HEADERS += \
+               pulse/browser.h
+endif
+
+if HAVE_GLIB20
+pulseinclude_HEADERS += \
+               pulse/glib-mainloop.h
+endif
+
+lib_LTLIBRARIES = \
+               libpulse.la \
+               libpulse-simple.la
+
+if HAVE_AVAHI
+lib_LTLIBRARIES += \
+               libpulse-browse.la
+endif
+
+if HAVE_GLIB20
+lib_LTLIBRARIES += \
+               libpulse-mainloop-glib.la
+endif
+
+# Public interface
+libpulse_la_SOURCES = \
+               pulse/cdecl.h \
+               pulse/channelmap.c pulse/channelmap.h \
+               pulse/client-conf.c pulse/client-conf.h \
+               pulse/context.c pulse/context.h \
+               pulse/def.h \
+               pulse/error.c pulse/error.h \
+               pulse/internal.h \
+               pulse/introspect.c pulse/introspect.h \
+               pulse/mainloop.c pulse/mainloop.h \
+               pulse/mainloop-api.c pulse/mainloop-api.h \
+               pulse/mainloop-signal.c pulse/mainloop-signal.h \
+               pulse/operation.c pulse/operation.h \
+               pulse/pulseaudio.h \
+               pulse/sample.c pulse/sample.h \
+               pulse/scache.c pulse/scache.h \
+               pulse/stream.c pulse/stream.h \
+               pulse/subscribe.c pulse/subscribe.h \
+               pulse/thread-mainloop.c pulse/thread-mainloop.h \
+               pulse/timeval.c pulse/timeval.h \
+               pulse/utf8.c pulse/utf8.h \
+               pulse/util.c pulse/util.h \
+               pulse/volume.c pulse/volume.h \
+               pulse/xmalloc.c pulse/xmalloc.h \
+               pulse/proplist.c pulse/proplist.h
+
+# Internal stuff that is shared with libpulsecore
+libpulse_la_SOURCES += \
+               pulsecore/authkey.c pulsecore/authkey.h \
+               pulsecore/conf-parser.c pulsecore/conf-parser.h \
+               pulsecore/core-util.c pulsecore/core-util.h \
+               pulsecore/dynarray.c pulsecore/dynarray.h \
+               pulsecore/hashmap.c pulsecore/hashmap.h \
+               pulsecore/idxset.c pulsecore/idxset.h \
+               pulsecore/inet_ntop.c pulsecore/inet_ntop.h \
+               pulsecore/iochannel.c pulsecore/iochannel.h \
+               pulsecore/llist.h \
+               pulsecore/log.c pulsecore/log.h \
+               pulsecore/mcalign.c pulsecore/mcalign.h \
+               pulsecore/memblock.c pulsecore/memblock.h \
+               pulsecore/memblockq.c pulsecore/memblockq.h \
+               pulsecore/memchunk.c pulsecore/memchunk.h \
+               pulsecore/native-common.h \
+               pulsecore/packet.c pulsecore/packet.h \
+               pulsecore/parseaddr.c pulsecore/parseaddr.h \
+               pulsecore/pdispatch.c pulsecore/pdispatch.h \
+               pulsecore/pipe.c pulsecore/pipe.h \
+               pulsecore/poll.c pulsecore/poll.h \
+               pulsecore/pstream.c pulsecore/pstream.h \
+               pulsecore/pstream-util.c pulsecore/pstream-util.h \
+               pulsecore/queue.c pulsecore/queue.h \
+               pulsecore/random.c pulsecore/random.h \
+               pulsecore/socket-client.c pulsecore/socket-client.h \
+               pulsecore/socket-util.c pulsecore/socket-util.h \
+               pulsecore/strbuf.c pulsecore/strbuf.h \
+               pulsecore/strlist.c pulsecore/strlist.h \
+               pulsecore/tagstruct.c pulsecore/tagstruct.h \
+               pulsecore/core-error.c pulsecore/core-error.h \
+               pulsecore/winsock.h pulsecore/creds.h \
+               pulsecore/shm.c pulsecore/shm.h \
+               pulsecore/flist.c pulsecore/flist.h \
+               pulsecore/object.c pulsecore/object.h \
+               pulsecore/msgobject.c pulsecore/msgobject.h \
+               pulsecore/once.c pulsecore/once.h \
+               pulsecore/rtclock.c pulsecore/rtclock.h \
+               pulsecore/time-smoother.c pulsecore/time-smoother.h \
+               pulsecore/proplist-util.c pulsecore/proplist-util.h \
+               $(PA_THREAD_OBJS)
+
+if OS_IS_WIN32
+libpulse_la_SOURCES += \
+               pulsecore/dllmain.c
+endif
+
+if HAVE_X11
+libpulse_la_SOURCES += \
+               pulse/client-conf-x11.c pulse/client-conf-x11.h \
+               pulsecore/x11prop.c pulsecore/x11prop.h
+endif
+
+libpulse_la_CFLAGS = $(AM_CFLAGS)
+libpulse_la_LDFLAGS = -version-info $(LIBPULSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LIBICONV)
+
+if HAVE_X11
+libpulse_la_CFLAGS += $(X_CFLAGS)
+libpulse_la_LDFLAGS += $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
+endif
+
+if HAVE_LIBASYNCNS
+libpulse_la_CFLAGS += $(LIBASYNCNS_CFLAGS)
+libpulse_la_LIBADD += $(LIBASYNCNS_LIBS)
+endif
+
+libpulse_simple_la_SOURCES = \
+               pulse/simple.c pulse/simple.h   \
+               pulsecore/log.c pulsecore/log.h \
+               pulsecore/core-util.c pulsecore/core-util.h  \
+               pulsecore/core-error.c pulsecore/core-error.h \
+               pulsecore/once.c pulsecore/once.h \
+               $(PA_THREAD_OBJS)
+
+libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
+libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la
+libpulse_simple_la_LDFLAGS = -version-info $(LIBPULSE_SIMPLE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+
+libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h  pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
+libpulse_browse_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la $(AVAHI_LIBS)
+libpulse_browse_la_LDFLAGS = -version-info $(LIBPULSE_BROWSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+
+libpulse_mainloop_glib_la_SOURCES = \
+               pulse/glib-mainloop.h pulse/glib-mainloop.c \
+               pulsecore/log.c pulsecore/log.h \
+               pulsecore/core-util.c pulsecore/core-util.h  \
+               pulsecore/core-error.c pulsecore/core-error.h \
+               pulsecore/once.c pulsecore/once.h \
+               $(PA_THREAD_OBJS)
+libpulse_mainloop_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS)
+libpulse_mainloop_glib_la_LIBADD = $(AM_LIBADD) libpulse.la $(GLIB20_LIBS)
+libpulse_mainloop_glib_la_LDFLAGS = -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+
+###################################
+#         OSS emulation           #
+###################################
+
+if HAVE_OSS
+
+lib_LTLIBRARIES += libpulsedsp.la
+
+bin_SCRIPTS += utils/padsp
+
+endif
+
+libpulsedsp_la_SOURCES = utils/padsp.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h  pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
+libpulsedsp_la_CFLAGS = $(AM_CFLAGS)
+libpulsedsp_la_LIBADD = $(AM_LIBADD) libpulse.la
+libpulsedsp_la_LDFLAGS = -avoid-version
+
+###################################
+#      Speex Resampler            #
+###################################
+
+noinst_LTLIBRARIES = libspeex-resampler-fixed.la libspeex-resampler-float.la libffmpeg-resampler.la
+
+libspeex_resampler_fixed_la_CPPFLAGS = $(AM_CPPFLAGS) -DRANDOM_PREFIX=paspfx -DOUTSIDE_SPEEX -DFIXED_POINT
+libspeex_resampler_fixed_la_SOURCES = pulsecore/speex/resample.c pulsecore/speex/speex_resampler.h pulsecore/speex/arch.h pulsecore/speex/fixed_generic.h pulsecore/speexwrap.h
+
+libspeex_resampler_float_la_CPPFLAGS = $(AM_CPPFLAGS) -DRANDOM_PREFIX=paspfl -DOUTSIDE_SPEEX -DFLOATING_POINT
+libspeex_resampler_float_la_SOURCES = pulsecore/speex/resample.c pulsecore/speex/speex_resampler.h pulsecore/speex/arch.h
+
+libffmpeg_resampler_la_CPPFLAGS = $(AM_CPPFLAGS)
+libffmpeg_resampler_la_SOURCES = pulsecore/ffmpeg/resample2.c pulsecore/ffmpeg/avcodec.h pulsecore/ffmpeg/dsputil.h
+
+###################################
+#      Daemon core library        #
+###################################
+
+#pulsecoreinclude_HEADERS =
+noinst_HEADERS = \
+               pulsecore/autoload.h \
+               pulsecore/atomic.h \
+               pulsecore/cli-command.h \
+               pulsecore/cli-text.h \
+               pulsecore/client.h \
+               pulsecore/core.h \
+               pulsecore/core-scache.h \
+               pulsecore/core-subscribe.h \
+               pulsecore/conf-parser.h \
+               pulsecore/core-util.h \
+               pulsecore/dynarray.h \
+               pulsecore/g711.h \
+               pulsecore/hashmap.h \
+               pulsecore/idxset.h \
+               pulsecore/log.h \
+               pulsecore/mcalign.h \
+               pulsecore/memblock.h \
+               pulsecore/memblockq.h \
+               pulsecore/memchunk.h \
+               pulsecore/modargs.h \
+               pulsecore/modinfo.h \
+               pulsecore/module.h \
+               pulsecore/namereg.h \
+               pulsecore/pid.h \
+               pulsecore/play-memchunk.h \
+               pulsecore/play-memblockq.h \
+               pulsecore/props.h \
+               pulsecore/queue.h \
+               pulsecore/random.h \
+               pulsecore/resampler.h \
+               pulsecore/sample-util.h \
+               pulsecore/sconv.h \
+               pulsecore/sink.h \
+               pulsecore/sink-input.h \
+               pulsecore/sioman.h \
+               pulsecore/sound-file.h \
+               pulsecore/sound-file-stream.h \
+               pulsecore/source.h \
+               pulsecore/source-output.h \
+               pulsecore/strbuf.h \
+               pulsecore/tokenizer.h \
+               pulsecore/creds.h \
+               pulsecore/shm.h \
+               pulsecore/llist.h \
+               pulsecore/refcnt.h \
+               pulsecore/mutex.h \
+               pulsecore/thread.h \
+               pulsecore/semaphore.h \
+               pulsecore/once.h
+
+lib_LTLIBRARIES += libpulsecore.la
+
+# Some public stuff is used even in the core
+libpulsecore_la_SOURCES = \
+               pulse/channelmap.c pulse/channelmap.h \
+               pulse/error.c pulse/error.h \
+               pulse/mainloop.c pulse/mainloop.h \
+               pulse/mainloop-api.c pulse/mainloop-api.h \
+               pulse/mainloop-signal.c pulse/mainloop-signal.h \
+               pulse/sample.c pulse/sample.h \
+               pulse/timeval.c pulse/timeval.h \
+               pulse/utf8.c pulse/utf8.h \
+               pulse/util.c pulse/util.h \
+               pulse/volume.c pulse/volume.h \
+               pulse/xmalloc.c pulse/xmalloc.h \
+               pulse/proplist.c pulse/proplist.h
+
+# Pure core stuff (some are shared in libpulse though).
+libpulsecore_la_SOURCES += \
+               pulsecore/autoload.c pulsecore/autoload.h \
+               pulsecore/cli-command.c pulsecore/cli-command.h \
+               pulsecore/cli-text.c pulsecore/cli-text.h \
+               pulsecore/client.c pulsecore/client.h \
+               pulsecore/conf-parser.c pulsecore/conf-parser.h \
+               pulsecore/core.c pulsecore/core.h \
+               pulsecore/core-scache.c pulsecore/core-scache.h \
+               pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
+               pulsecore/core-util.c pulsecore/core-util.h \
+               pulsecore/dynarray.c pulsecore/dynarray.h \
+               pulsecore/endianmacros.h \
+               pulsecore/g711.c pulsecore/g711.h \
+               pulsecore/hashmap.c pulsecore/hashmap.h \
+               pulsecore/idxset.c pulsecore/idxset.h \
+               pulsecore/log.c pulsecore/log.h \
+               pulsecore/mcalign.c pulsecore/mcalign.h \
+               pulsecore/memblock.c pulsecore/memblock.h \
+               pulsecore/memblockq.c pulsecore/memblockq.h \
+               pulsecore/memchunk.c pulsecore/memchunk.h \
+               pulsecore/modargs.c pulsecore/modargs.h \
+               pulsecore/modinfo.c pulsecore/modinfo.h \
+               pulsecore/ltdl-helper.c pulsecore/ltdl-helper.h \
+               pulsecore/module.c pulsecore/module.h \
+               pulsecore/namereg.c pulsecore/namereg.h \
+               pulsecore/pid.c pulsecore/pid.h \
+               pulsecore/pipe.c pulsecore/pipe.h \
+               pulsecore/play-memchunk.c pulsecore/play-memchunk.h \
+               pulsecore/play-memblockq.c pulsecore/play-memblockq.h \
+               pulsecore/poll.c pulsecore/poll.h \
+               pulsecore/props.c pulsecore/props.h \
+               pulsecore/queue.c pulsecore/queue.h \
+               pulsecore/random.c pulsecore/random.h \
+               pulsecore/resampler.c pulsecore/resampler.h \
+               pulsecore/sample-util.c pulsecore/sample-util.h \
+               pulsecore/sconv.c pulsecore/sconv.h \
+               pulsecore/sconv-s16be.c pulsecore/sconv-s16be.h \
+               pulsecore/sconv-s16le.c pulsecore/sconv-s16le.h \
+               pulsecore/sink.c pulsecore/sink.h \
+               pulsecore/sink-input.c pulsecore/sink-input.h \
+               pulsecore/sioman.c pulsecore/sioman.h \
+               pulsecore/sound-file.c pulsecore/sound-file.h \
+               pulsecore/sound-file-stream.c pulsecore/sound-file-stream.h \
+               pulsecore/source.c pulsecore/source.h \
+               pulsecore/source-output.c pulsecore/source-output.h \
+               pulsecore/strbuf.c pulsecore/strbuf.h \
+               pulsecore/tokenizer.c pulsecore/tokenizer.h \
+               pulsecore/winsock.h \
+               pulsecore/core-error.c pulsecore/core-error.h \
+               pulsecore/hook-list.c pulsecore/hook-list.h \
+               pulsecore/shm.c pulsecore/shm.h \
+               pulsecore/flist.c pulsecore/flist.h \
+               pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \
+               pulsecore/asyncq.c pulsecore/asyncq.h \
+               pulsecore/thread-mq.c pulsecore/thread-mq.h \
+               pulsecore/fdsem.c pulsecore/fdsem.h \
+               pulsecore/object.c pulsecore/object.h \
+               pulsecore/msgobject.c pulsecore/msgobject.h \
+               pulsecore/rtsig.c pulsecore/rtsig.h \
+               pulsecore/rtpoll.c pulsecore/rtpoll.h \
+               pulsecore/rtclock.c pulsecore/rtclock.h \
+               pulsecore/macro.h \
+               pulsecore/once.c pulsecore/once.h \
+               pulsecore/time-smoother.c pulsecore/time-smoother.h \
+               pulsecore/start-child.c pulsecore/start-child.h \
+               pulsecore/envelope.c pulsecore/envelope.h \
+               pulsecore/proplist-util.c pulsecore/proplist-util.h \
+               $(PA_THREAD_OBJS)
+
+if OS_IS_WIN32
+libpulsecore_la_SOURCES += \
+               pulsecore/dllmain.c
+endif
+
+libpulsecore_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBOIL_CFLAGS)
+libpulsecore_la_LDFLAGS = -version-info $(LIBPULSECORE_VERSION_INFO)
+libpulsecore_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) $(LIBICONV) libspeex-resampler-fixed.la libspeex-resampler-float.la libffmpeg-resampler.la
+
+###################################
+#   Plug-in support libraries     #
+###################################
+
+#pulsecoreinclude_HEADERS +=
+noinst_HEADERS += \
+               pulsecore/socket-util.h \
+               pulsecore/iochannel.h \
+               pulsecore/socket-server.h \
+               pulsecore/ipacl.h \
+               pulsecore/socket-client.h \
+               pulsecore/parseaddr.h \
+               pulsecore/packet.h \
+               pulsecore/pstream.h \
+               pulsecore/ioline.h \
+               pulsecore/cli.h \
+               pulsecore/protocol-cli.h \
+               pulsecore/tagstruct.h \
+               pulsecore/pstream-util.h \
+               pulsecore/pdispatch.h \
+               pulsecore/authkey.h \
+               pulsecore/authkey-prop.h \
+               pulsecore/strlist.h \
+               pulsecore/protocol-simple.h \
+               pulsecore/esound.h \
+               pulsecore/protocol-esound.h \
+               pulsecore/native-common.h \
+               pulsecore/protocol-native.h \
+               pulsecore/protocol-http.h
+
+### Warning! Due to an obscure bug in libtool/automake it is required
+### that the libraries in modlibexec_LTLIBRARIES are specified in-order,
+### i.e. libraries near the end of the list depend on libraries near
+### the head, and not the other way!
+
+modlibexec_LTLIBRARIES = \
+               libsocket-util.la \
+               libiochannel.la \
+               libsocket-server.la \
+               libipacl.la \
+               libparseaddr.la \
+               libsocket-client.la \
+               libpacket.la \
+               libpstream.la \
+               libioline.la \
+               libcli.la \
+               libprotocol-cli.la \
+               libtagstruct.la \
+               libpstream-util.la \
+               libpdispatch.la \
+               libauthkey.la \
+               libauthkey-prop.la \
+               libstrlist.la \
+               libprotocol-simple.la \
+               libprotocol-http.la \
+               libprotocol-native.la \
+               libprotocol-esound.la
+
+# We need to emulate sendmsg/recvmsg to support this on Win32
+if !OS_IS_WIN32
+modlibexec_LTLIBRARIES += \
+               librtp.la
+endif
+
+if HAVE_X11
+#pulsecoreinclude_HEADERS +=
+noinst_HEADERS += \
+               pulsecore/x11wrap.h \
+               pulsecore/x11prop.h
+
+modlibexec_LTLIBRARIES += \
+               libx11wrap.la \
+               libx11prop.la
+endif
+
+if HAVE_AVAHI
+#pulsecoreinclude_HEADERS +=
+noinst_HEADERS += \
+               pulsecore/avahi-wrap.h
+
+modlibexec_LTLIBRARIES += \
+               libavahi-wrap.la
+endif
+
+libprotocol_simple_la_SOURCES = pulsecore/protocol-simple.c pulsecore/protocol-simple.h
+libprotocol_simple_la_LDFLAGS = -avoid-version
+libprotocol_simple_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-server.la libiochannel.la
+
+libsocket_server_la_SOURCES = \
+               pulsecore/inet_ntop.c pulsecore/inet_ntop.h \
+               pulsecore/inet_pton.c pulsecore/inet_pton.h \
+               pulsecore/socket-server.c pulsecore/socket-server.h
+libsocket_server_la_LDFLAGS = -avoid-version
+libsocket_server_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-util.la $(LIBWRAP_LIBS) $(WINSOCK_LIBS)
+
+libipacl_la_SOURCES = pulsecore/ipacl.h pulsecore/ipacl.c \
+               pulsecore/inet_pton.c pulsecore/inet_pton.h
+libipacl_la_LDFLAGS = -avoid-version
+libipacl_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(WINSOCK_LIBS)
+
+libsocket_client_la_SOURCES = pulsecore/socket-client.c pulsecore/socket-client.h
+libsocket_client_la_LDFLAGS = -avoid-version
+libsocket_client_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-util.la libparseaddr.la $(LIBASYNCNS_LIBS) $(WINSOCK_LIBS)
+libsocket_client_la_CFLAGS = $(AM_CFLAGS) $(LIBASYNCNS_CFLAGS)
+
+libparseaddr_la_SOURCES = pulsecore/parseaddr.c pulsecore/parseaddr.h
+libparseaddr_la_LDFLAGS = -avoid-version
+libparseaddr_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+libpstream_la_SOURCES = pulsecore/pstream.c pulsecore/pstream.h
+libpstream_la_LDFLAGS = -avoid-version
+libpstream_la_LIBADD = $(AM_LIBADD) libpulsecore.la libpacket.la libiochannel.la $(WINSOCK_LIBS)
+
+libpstream_util_la_SOURCES = pulsecore/pstream-util.c pulsecore/pstream-util.h
+libpstream_util_la_LDFLAGS = -avoid-version
+libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la libpulsecore.la
+
+libpdispatch_la_SOURCES = pulsecore/pdispatch.c pulsecore/pdispatch.h
+libpdispatch_la_LDFLAGS = -avoid-version
+libpdispatch_la_LIBADD = $(AM_LIBADD) libtagstruct.la libpulsecore.la
+
+libiochannel_la_SOURCES = pulsecore/iochannel.c pulsecore/iochannel.h
+libiochannel_la_LDFLAGS = -avoid-version
+libiochannel_la_LIBADD = $(AM_LIBADD) libsocket-util.la libpulsecore.la $(WINSOCK_LIBS)
+
+libpacket_la_SOURCES = pulsecore/packet.c pulsecore/packet.h
+libpacket_la_LDFLAGS = -avoid-version
+libpacket_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+libioline_la_SOURCES = pulsecore/ioline.c pulsecore/ioline.h
+libioline_la_LDFLAGS = -avoid-version
+libioline_la_LIBADD = $(AM_LIBADD) libiochannel.la libpulsecore.la
+
+libcli_la_SOURCES = pulsecore/cli.c pulsecore/cli.h
+libcli_la_CPPFLAGS = $(AM_CPPFLAGS)
+libcli_la_LDFLAGS = -avoid-version
+libcli_la_LIBADD = $(AM_LIBADD) libiochannel.la libioline.la libpulsecore.la
+
+libstrlist_la_SOURCES = pulsecore/strlist.c pulsecore/strlist.h
+libstrlist_la_LDFLAGS = -avoid-version
+libstrlist_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+libprotocol_cli_la_SOURCES = pulsecore/protocol-cli.c pulsecore/protocol-cli.h
+libprotocol_cli_la_LDFLAGS = -avoid-version
+libprotocol_cli_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libcli.la libpulsecore.la
+
+libprotocol_http_la_SOURCES = pulsecore/protocol-http.c pulsecore/protocol-http.h
+libprotocol_http_la_LDFLAGS = -avoid-version
+libprotocol_http_la_LIBADD = $(AM_LIBADD) libsocket-server.la libioline.la libpulsecore.la libiochannel.la
+
+libprotocol_native_la_SOURCES = pulsecore/protocol-native.c pulsecore/protocol-native.h pulsecore/native-common.h
+libprotocol_native_la_LDFLAGS = -avoid-version
+libprotocol_native_la_LIBADD = $(AM_LIBADD) libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libstrlist.la libpulsecore.la libiochannel.la libipacl.la
+
+libtagstruct_la_SOURCES = pulsecore/tagstruct.c pulsecore/tagstruct.h
+libtagstruct_la_LDFLAGS = -avoid-version
+libtagstruct_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(WINSOCK_LIBS)
+
+libprotocol_esound_la_SOURCES = pulsecore/protocol-esound.c pulsecore/protocol-esound.h pulsecore/esound.h
+libprotocol_esound_la_LDFLAGS = -avoid-version
+libprotocol_esound_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libauthkey.la libpulsecore.la libipacl.la
+
+libauthkey_la_SOURCES = pulsecore/authkey.c pulsecore/authkey.h
+libauthkey_la_LDFLAGS = -avoid-version
+libauthkey_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+libauthkey_prop_la_SOURCES = pulsecore/authkey-prop.c pulsecore/authkey-prop.h
+libauthkey_prop_la_LDFLAGS = -avoid-version
+libauthkey_prop_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+libsocket_util_la_SOURCES = \
+               pulsecore/inet_ntop.c pulsecore/inet_ntop.h \
+               pulsecore/socket-util.c pulsecore/socket-util.h
+libsocket_util_la_LDFLAGS = -avoid-version
+libsocket_util_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) libpulsecore.la
+
+librtp_la_SOURCES = modules/rtp/rtp.c modules/rtp/rtp.h modules/rtp/sdp.c modules/rtp/sdp.h modules/rtp/sap.c modules/rtp/sap.h
+librtp_la_LDFLAGS = -avoid-version
+librtp_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+# X11
+
+libx11wrap_la_SOURCES = pulsecore/x11wrap.c pulsecore/x11wrap.h
+libx11wrap_la_LDFLAGS = -avoid-version
+libx11wrap_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+libx11wrap_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libpulsecore.la
+
+libx11prop_la_SOURCES = pulsecore/x11prop.c pulsecore/x11prop.h
+libx11prop_la_LDFLAGS = -avoid-version
+libx11prop_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+libx11prop_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
+
+# Avahi
+
+libavahi_wrap_la_SOURCES = pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h
+libavahi_wrap_la_LDFLAGS = -avoid-version
+libavahi_wrap_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore.la
+
+###################################
+#        Plug-in libraries        #
+###################################
+
+modlibexec_LTLIBRARIES += \
+               module-cli.la \
+               module-cli-protocol-tcp.la \
+               module-simple-protocol-tcp.la \
+               module-null-sink.la \
+               module-detect.la \
+               module-volume-restore.la \
+               module-device-restore.la \
+               module-default-device-restore.la \
+               module-always-sink.la \
+               module-rescue-streams.la \
+               module-suspend-on-idle.la \
+               module-http-protocol-tcp.la \
+               module-sine.la \
+               module-native-protocol-tcp.la \
+               module-native-protocol-fd.la \
+               module-esound-protocol-tcp.la \
+               module-combine.la \
+               module-remap-sink.la \
+               module-ladspa-sink.la \
+               module-esound-sink.la \
+               module-tunnel-sink.la \
+               module-tunnel-source.la \
+               module-position-event-sounds.la
+
+
+# See comment at librtp.la above
+if !OS_IS_WIN32
+modlibexec_LTLIBRARIES += \
+               module-rtp-send.la \
+               module-rtp-recv.la
+endif
+
+if HAVE_AF_UNIX
+modlibexec_LTLIBRARIES += \
+               module-cli-protocol-unix.la \
+               module-simple-protocol-unix.la \
+               module-http-protocol-unix.la \
+               module-native-protocol-unix.la \
+               module-esound-protocol-unix.la
+endif
+
+if HAVE_MKFIFO
+modlibexec_LTLIBRARIES += \
+               module-pipe-sink.la \
+               module-pipe-source.la
+endif
+
+if !OS_IS_WIN32
+modlibexec_LTLIBRARIES += \
+               module-esound-compat-spawnfd.la \
+               module-esound-compat-spawnpid.la
+endif
+
+if HAVE_REGEX
+modlibexec_LTLIBRARIES += \
+               module-match.la
+endif
+
+if HAVE_X11
+modlibexec_LTLIBRARIES += \
+               module-x11-bell.la \
+               module-x11-publish.la \
+               module-x11-xsmp.la
+endif
+
+if HAVE_OSS
+modlibexec_LTLIBRARIES += \
+               liboss-util.la \
+               module-oss.la
+endif
+
+if HAVE_ALSA
+modlibexec_LTLIBRARIES += \
+               libalsa-util.la \
+               module-alsa-sink.la \
+               module-alsa-source.la
+endif
+
+if HAVE_SOLARIS
+modlibexec_LTLIBRARIES += \
+               module-solaris.la
+endif
+
+if HAVE_AVAHI
+modlibexec_LTLIBRARIES += \
+               module-zeroconf-publish.la \
+               module-zeroconf-discover.la
+endif
+
+if HAVE_LIRC
+modlibexec_LTLIBRARIES += \
+               module-lirc.la
+endif
+
+if HAVE_EVDEV
+modlibexec_LTLIBRARIES += \
+               module-mmkbd-evdev.la
+endif
+
+if HAVE_JACK
+modlibexec_LTLIBRARIES += \
+               module-jack-sink.la \
+               module-jack-source.la
+endif
+
+pulselibexec_PROGRAMS =
+
+if HAVE_GCONF
+modlibexec_LTLIBRARIES += \
+               module-gconf.la
+
+pulselibexec_PROGRAMS += \
+               gconf-helper
+endif
+
+#if OS_IS_WIN32
+#modlibexec_LTLIBRARIES += \
+#              module-waveout.la
+#endif
+
+if HAVE_HAL
+modlibexec_LTLIBRARIES += \
+               libdbus-util.la \
+               module-hal-detect.la
+endif
+
+if HAVE_DBUS
+modlibexec_LTLIBRARIES += \
+               libdbus-util.la \
+               module-console-kit.la
+endif
+
+if HAVE_BLUEZ
+modlibexec_LTLIBRARIES += \
+               module-bt-proximity.la
+
+pulselibexec_PROGRAMS += \
+               bt-proximity-helper
+endif
+
+# These are generated by a M4 script
+
+SYMDEF_FILES = \
+               modules/module-cli-symdef.h \
+               modules/module-cli-protocol-tcp-symdef.h \
+               modules/module-cli-protocol-unix-symdef.h \
+               modules/module-pipe-sink-symdef.h \
+               modules/module-pipe-source-symdef.h \
+               modules/module-simple-protocol-tcp-symdef.h \
+               modules/module-simple-protocol-unix-symdef.h \
+               modules/module-esound-protocol-tcp-symdef.h \
+               modules/module-esound-protocol-unix-symdef.h \
+               modules/module-native-protocol-tcp-symdef.h \
+               modules/module-native-protocol-unix-symdef.h \
+               modules/module-native-protocol-fd-symdef.h \
+               modules/module-sine-symdef.h \
+               modules/module-combine-symdef.h \
+               modules/module-remap-sink-symdef.h \
+               modules/module-ladspa-sink-symdef.h \
+               modules/module-esound-compat-spawnfd-symdef.h \
+               modules/module-esound-compat-spawnpid-symdef.h \
+               modules/module-match-symdef.h \
+               modules/module-tunnel-sink-symdef.h \
+               modules/module-tunnel-source-symdef.h \
+               modules/module-null-sink-symdef.h \
+               modules/module-esound-sink-symdef.h \
+               modules/module-zeroconf-publish-symdef.h \
+               modules/module-zeroconf-discover-symdef.h \
+               modules/module-lirc-symdef.h \
+               modules/module-mmkbd-evdev-symdef.h \
+               modules/module-http-protocol-tcp-symdef.h \
+               modules/module-http-protocol-unix-symdef.h \
+               modules/module-x11-bell-symdef.h \
+               modules/module-x11-publish-symdef.h \
+               modules/module-x11-xsmp-symdef.h \
+               modules/module-oss-symdef.h \
+               modules/module-alsa-sink-symdef.h \
+               modules/module-alsa-source-symdef.h \
+               modules/module-solaris-symdef.h \
+               modules/module-waveout-symdef.h \
+               modules/module-detect-symdef.h \
+               modules/rtp/module-rtp-send-symdef.h \
+               modules/rtp/module-rtp-recv-symdef.h \
+               modules/module-jack-sink-symdef.h \
+               modules/module-jack-source-symdef.h \
+               modules/module-volume-restore-symdef.h \
+               modules/module-device-restore-symdef.h \
+               modules/module-default-device-restore-symdef.h \
+               modules/module-always-sink-symdef.h \
+               modules/module-rescue-streams-symdef.h \
+               modules/module-suspend-on-idle-symdef.h \
+               modules/module-hal-detect-symdef.h \
+               modules/module-bt-proximity-symdef.h \
+               modules/gconf/module-gconf-symdef.h \
+               modules/module-position-event-sounds-symdef.h \
+               modules/module-console-kit-symdef.h
+
+EXTRA_DIST += $(SYMDEF_FILES)
+BUILT_SOURCES += $(SYMDEF_FILES)
+
+$(SYMDEF_FILES): modules/module-defs.h.m4
+       $(MKDIR_P) modules
+       $(MKDIR_P) modules/gconf
+       $(MKDIR_P) modules/rtp
+       $(M4) -Dfname="$@" $< > $@
+
+# Simple protocol
+
+module_simple_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
+module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_simple_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-simple.la libsocket-server.la
+
+module_simple_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
+module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-simple.la libsocket-server.la libsocket-util.la
+
+# CLI protocol
+
+module_cli_la_SOURCES = modules/module-cli.c
+module_cli_la_LDFLAGS = -module -avoid-version
+module_cli_la_LIBADD = $(AM_LIBADD) libcli.la libiochannel.la libpulsecore.la
+
+module_cli_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
+module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_cli_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-cli.la libsocket-server.la
+
+module_cli_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
+module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_cli_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-cli.la libsocket-server.la libsocket-util.la
+
+# HTTP protocol
+
+module_http_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_http_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
+module_http_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_http_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-http.la libsocket-server.la
+
+module_http_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
+module_http_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-http.la libsocket-server.la libsocket-util.la
+
+# Native protocol
+
+module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
+module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_native_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-native.la libsocket-server.la
+
+module_native_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
+module_native_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_native_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-native.la libsocket-server.la libsocket-util.la
+
+module_native_protocol_fd_la_SOURCES = modules/module-native-protocol-fd.c
+module_native_protocol_fd_la_CFLAGS = $(AM_CFLAGS)
+module_native_protocol_fd_la_LDFLAGS = -module -avoid-version
+module_native_protocol_fd_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-native.la libsocket-server.la libsocket-util.la libiochannel.la
+
+# EsounD protocol
+
+module_esound_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
+module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
+module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_esound_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-esound.la libsocket-server.la
+
+module_esound_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
+module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
+module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_esound_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-esound.la libsocket-server.la libsocket-util.la
+
+module_esound_compat_spawnfd_la_SOURCES = modules/module-esound-compat-spawnfd.c
+module_esound_compat_spawnfd_la_LDFLAGS = -module -avoid-version
+module_esound_compat_spawnfd_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+module_esound_compat_spawnpid_la_SOURCES = modules/module-esound-compat-spawnpid.c
+module_esound_compat_spawnpid_la_LDFLAGS = -module -avoid-version
+module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+module_esound_sink_la_SOURCES = modules/module-esound-sink.c
+module_esound_sink_la_LDFLAGS = -module -avoid-version
+module_esound_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-client.la libauthkey.la libsocket-util.la
+
+# Pipes
+
+module_pipe_sink_la_SOURCES = modules/module-pipe-sink.c
+module_pipe_sink_la_LDFLAGS = -module -avoid-version
+module_pipe_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la
+
+module_pipe_source_la_SOURCES = modules/module-pipe-source.c
+module_pipe_source_la_LDFLAGS = -module -avoid-version
+module_pipe_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la
+
+# Fake sources/sinks
+
+module_sine_la_SOURCES = modules/module-sine.c
+module_sine_la_LDFLAGS = -module -avoid-version
+module_sine_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+module_null_sink_la_SOURCES = modules/module-null-sink.c
+module_null_sink_la_LDFLAGS = -module -avoid-version
+module_null_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+# Couplings
+
+module_combine_la_SOURCES = modules/module-combine.c
+module_combine_la_LDFLAGS = -module -avoid-version
+module_combine_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+module_remap_sink_la_SOURCES = modules/module-remap-sink.c
+module_remap_sink_la_LDFLAGS = -module -avoid-version
+module_remap_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+module_ladspa_sink_la_SOURCES = modules/module-ladspa-sink.c modules/ladspa.h
+module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa\" $(AM_CFLAGS)
+module_ladspa_sink_la_LDFLAGS = -module -avoid-version
+module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore.la
+
+module_match_la_SOURCES = modules/module-match.c
+module_match_la_LDFLAGS = -module -avoid-version
+module_match_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+
+module_tunnel_sink_la_SOURCES = modules/module-tunnel.c
+module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS)
+module_tunnel_sink_la_LDFLAGS = -module -avoid-version
+module_tunnel_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la
+
+module_tunnel_source_la_SOURCES = modules/module-tunnel.c
+module_tunnel_source_la_LDFLAGS = -module -avoid-version
+module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la
+
+# X11
+
+module_x11_bell_la_SOURCES = modules/module-x11-bell.c
+module_x11_bell_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+module_x11_bell_la_LDFLAGS = -module -avoid-version
+module_x11_bell_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libpulsecore.la
+
+module_x11_publish_la_SOURCES = modules/module-x11-publish.c
+module_x11_publish_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+module_x11_publish_la_LDFLAGS = -module -avoid-version
+module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libauthkey.la libauthkey-prop.la libx11prop.la libstrlist.la libpulsecore.la
+
+module_x11_xsmp_la_SOURCES = modules/module-x11-xsmp.c
+module_x11_xsmp_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
+module_x11_xsmp_la_LDFLAGS = -module -avoid-version
+module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libpulsecore.la
+
+# OSS
+
+liboss_util_la_SOURCES = modules/oss-util.c modules/oss-util.h
+liboss_util_la_LDFLAGS = -avoid-version
+liboss_util_la_LIBADD = libpulsecore.la
+
+module_oss_la_SOURCES = modules/module-oss.c
+module_oss_la_LDFLAGS = -module -avoid-version
+module_oss_la_LIBADD = $(AM_LIBADD) libiochannel.la liboss-util.la libpulsecore.la
+
+# ALSA
+
+libalsa_util_la_SOURCES = modules/alsa-util.c modules/alsa-util.h
+libalsa_util_la_LDFLAGS = -avoid-version
+libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore.la
+libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+module_alsa_sink_la_SOURCES = modules/module-alsa-sink.c
+module_alsa_sink_la_LDFLAGS = -module -avoid-version
+module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore.la
+module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+module_alsa_source_la_SOURCES = modules/module-alsa-source.c
+module_alsa_source_la_LDFLAGS = -module -avoid-version
+module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore.la
+module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+# Solaris
+
+module_solaris_la_SOURCES = modules/module-solaris.c
+module_solaris_la_LDFLAGS = -module -avoid-version
+module_solaris_la_LIBADD = $(AM_LIBADD) libiochannel.la libpulsecore.la
+
+# Avahi
+
+module_zeroconf_publish_la_SOURCES = modules/module-zeroconf-publish.c
+module_zeroconf_publish_la_LDFLAGS = -module -avoid-version
+module_zeroconf_publish_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la
+module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+
+module_zeroconf_discover_la_SOURCES = modules/module-zeroconf-discover.c
+module_zeroconf_discover_la_LDFLAGS = -module -avoid-version
+module_zeroconf_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la
+module_zeroconf_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+
+# LIRC
+
+module_lirc_la_SOURCES = modules/module-lirc.c
+module_lirc_la_LDFLAGS = -module -avoid-version
+module_lirc_la_LIBADD = $(AM_LIBADD) $(LIRC_LIBS) libpulsecore.la
+module_lirc_la_CFLAGS = $(AM_CFLAGS) $(LIRC_CFLAGS)
+
+# Linux evdev
+
+module_mmkbd_evdev_la_SOURCES = modules/module-mmkbd-evdev.c
+module_mmkbd_evdev_la_LDFLAGS = -module -avoid-version
+module_mmkbd_evdev_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_mmkbd_evdev_la_CFLAGS = $(AM_CFLAGS)
+
+# Windows waveout
+
+#module_waveout_la_SOURCES = modules/module-waveout.c
+#module_waveout_la_LDFLAGS = -module -avoid-version
+#module_waveout_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lwinmm
+#module_waveout_la_CFLAGS = $(AM_CFLAGS)
+
+# Hardware autodetection module
+module_detect_la_SOURCES = modules/module-detect.c
+module_detect_la_LDFLAGS = -module -avoid-version
+module_detect_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_detect_la_CFLAGS = $(AM_CFLAGS)
+
+# Volume restore module
+module_volume_restore_la_SOURCES = modules/module-volume-restore.c
+module_volume_restore_la_LDFLAGS = -module -avoid-version
+module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_volume_restore_la_CFLAGS = $(AM_CFLAGS)
+
+# Position event sounds in space
+module_position_event_sounds_la_SOURCES = modules/module-position-event-sounds.c
+module_position_event_sounds_la_LDFLAGS = -module -avoid-version
+module_position_event_sounds_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_position_event_sounds_CFLAGS = $(AM_CFLAGS)
+
+# Device volume restore module
+module_device_restore_la_SOURCES = modules/module-device-restore.c
+module_device_restore_la_LDFLAGS = -module -avoid-version
+module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lgdbm
+module_device_restore_la_CFLAGS = $(AM_CFLAGS)
+
+# Default sink/source restore module
+module_default_device_restore_la_SOURCES = modules/module-default-device-restore.c
+module_default_device_restore_la_LDFLAGS = -module -avoid-version
+module_default_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_default_device_restore_la_CFLAGS = $(AM_CFLAGS)
+
+# Always Sink module
+module_always_sink_la_SOURCES = modules/module-always-sink.c
+module_always_sink_la_LDFLAGS = -module -avoid-version
+module_always_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_always_sink_la_CFLAGS = $(AM_CFLAGS)
+
+# Rescue streams module
+module_rescue_streams_la_SOURCES = modules/module-rescue-streams.c
+module_rescue_streams_la_LDFLAGS = -module -avoid-version
+module_rescue_streams_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_rescue_streams_la_CFLAGS = $(AM_CFLAGS)
+
+# Suspend-on-idle module
+module_suspend_on_idle_la_SOURCES = modules/module-suspend-on-idle.c
+module_suspend_on_idle_la_LDFLAGS = -module -avoid-version
+module_suspend_on_idle_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_suspend_on_idle_la_CFLAGS = $(AM_CFLAGS)
+
+# RTP modules
+module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c
+module_rtp_send_la_LDFLAGS = -module -avoid-version
+module_rtp_send_la_LIBADD = $(AM_LIBADD) libpulsecore.la librtp.la libsocket-util.la
+module_rtp_send_la_CFLAGS = $(AM_CFLAGS)
+
+module_rtp_recv_la_SOURCES = modules/rtp/module-rtp-recv.c
+module_rtp_recv_la_LDFLAGS = -module -avoid-version
+module_rtp_recv_la_LIBADD = $(AM_LIBADD) libpulsecore.la librtp.la
+module_rtp_recv_la_CFLAGS = $(AM_CFLAGS)
+
+# JACK
+
+module_jack_sink_la_SOURCES = modules/module-jack-sink.c
+module_jack_sink_la_LDFLAGS = -module -avoid-version
+module_jack_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS)
+module_jack_sink_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
+
+module_jack_source_la_SOURCES = modules/module-jack-source.c
+module_jack_source_la_LDFLAGS = -module -avoid-version
+module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS)
+module_jack_source_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
+
+# HAL/D-Bus
+libdbus_util_la_SOURCES = modules/dbus-util.c modules/dbus-util.h
+libdbus_util_la_LDFLAGS = -avoid-version
+libdbus_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la
+libdbus_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
+module_hal_detect_la_SOURCES = modules/module-hal-detect.c
+module_hal_detect_la_LDFLAGS = -module -avoid-version
+module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la
+module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+
+module_console_kit_la_SOURCES = modules/module-console-kit.c
+module_console_kit_la_LDFLAGS = -module -avoid-version
+module_console_kit_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la
+module_console_kit_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
+
+# GConf support
+module_gconf_la_SOURCES = modules/gconf/module-gconf.c
+module_gconf_la_LDFLAGS = -module -avoid-version
+module_gconf_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_gconf_la_CFLAGS = $(AM_CFLAGS) -DPA_GCONF_HELPER=\"$(pulselibexecdir)/gconf-helper\"
+
+gconf_helper_SOURCES = modules/gconf/gconf-helper.c
+gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS) libpulsecore.la
+gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS)
+gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+# Bluetooth proximity
+module_bt_proximity_la_SOURCES = modules/module-bt-proximity.c
+module_bt_proximity_la_LDFLAGS = -module -avoid-version
+module_bt_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
+module_bt_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/bt-proximity-helper\"
+
+bt_proximity_helper_SOURCES = modules/bt-proximity-helper.c
+bt_proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS)
+bt_proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS)
+bt_proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
+###################################
+#        Some minor stuff         #
+###################################
+
+suid: pulseaudio .libs/lt-pulseaudio
+       chown root $^
+       chmod u+s $^
+
+CLEANFILES = esdcompat client.conf default.pa daemon.conf
+
+esdcompat: daemon/esdcompat.in Makefile
+       sed -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \
+               -e 's,@PACKAGE_NAME\@,$(PACKAGE_NAME),g' \
+               -e 's,@PA_BINARY\@,$(PA_BINARY),g' < $< > $@
+       chmod +x esdcompat
+
+client.conf: pulse/client.conf.in Makefile
+       sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' < $< > $@
+
+if OS_IS_WIN32
+default.pa: daemon/default.pa.win32
+       cp $< $@
+else
+default.pa: daemon/default.pa.in Makefile
+       sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \
+            -e 's,@PA_DLSEARCHPATH\@,$(modlibexecdir),g' \
+           -e 's,@PA_SOEXT\@,.so,g' < $< > $@
+endif
+
+daemon.conf: daemon/daemon.conf.in Makefile
+       sed -e 's,@PA_DLSEARCHPATH\@,$(modlibexecdir),g' \
+               -e 's,@PA_DEFAULT_CONFIG_FILE\@,$(DEFAULT_CONFIG_DIR),g' < $< > $@
+
+install-exec-hook:
+       chown root $(DESTDIR)$(bindir)/pulseaudio ; true
+       chmod u+s $(DESTDIR)$(bindir)/pulseaudio
+       -chmod u+s $(DESTDIR)$(pulselibexecdir)/bt-proximity-helper
+       ln -sf pacat $(DESTDIR)$(bindir)/parec
+       rm -f $(DESTDIR)$(modlibexecdir)/*.a
+       rm -f $(DESTDIR)$(libdir)/libpulsedsp.a
+       rm -f $(DESTDIR)$(libdir)/libpulsedsp.la
+       rm -f $(DESTDIR)$(modlibexecdir)/*.la
+
+massif: pulseaudio
+       libtool --mode=execute valgrind --tool=massif --depth=6  --alloc-fn=pa_xmalloc --alloc-fn=pa_xmalloc0 --alloc-fn=pa_xrealloc --alloc-fn=dbus_realloc --alloc-fn=pa_xnew0_internal --alloc-fn=pa_xnew_internal ./pulseaudio
+
+update-speex:
+       wget -O pulsecore/speex/speex_resampler.h http://svn.xiph.org/trunk/speex/include/speex/speex_resampler.h
+       wget -O pulsecore/speex/resample.c http://svn.xiph.org/trunk/speex/libspeex/resample.c
+       wget -O pulsecore/speex/arch.h http://svn.xiph.org/trunk/speex/libspeex/arch.h
+       wget -O pulsecore/speex/fixed_generic.h http://svn.xiph.org/trunk/speex/libspeex/fixed_generic.h
+
+update-ffmpeg:
+       wget -O pulsecore/ffmpeg/resample2.c http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/resample2.c?view=co
+
+# Automatically generate linker version script. We use the same one for all public .sos
+update-map-file:
+       ( echo "PULSE_0 {" ; \
+         echo "global:" ; \
+         ctags -I PA_GCC_PURE,PA_GCC_CONST -f - --c-kinds=p $(pulseinclude_HEADERS) | awk '/^pa_/ { print $$1 ";" }' | sort ; \
+         echo "local:" ;  \
+         echo "*;" ; \
+         echo "};" ) > $(srcdir)/map-file
+
+.PHONY: utils/padsp
diff --git a/src/daemon/Makefile b/src/daemon/Makefile
new file mode 120000 (symlink)
index 0000000..c110232
--- /dev/null
@@ -0,0 +1 @@
+../pulse/Makefile
\ No newline at end of file
diff --git a/src/daemon/caps.c b/src/daemon/caps.c
new file mode 100644 (file)
index 0000000..ae07119
--- /dev/null
@@ -0,0 +1,147 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <sys/types.h>
+#include <pulsecore/macro.h>
+
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+#include <pulsecore/core-error.h>
+
+#include <pulsecore/log.h>
+
+#include "caps.h"
+
+/* Glibc <= 2.2 has broken unistd.h */
+#if defined(linux) && (__GLIBC__ <= 2 && __GLIBC_MINOR__ <= 2)
+int setresgid(gid_t r, gid_t e, gid_t s);
+int setresuid(uid_t r, uid_t e, uid_t s);
+#endif
+
+#ifdef HAVE_GETUID
+
+/* Drop root rights when called SUID root */
+void pa_drop_root(void) {
+    uid_t uid = getuid();
+
+    if (uid == 0 || geteuid() != 0)
+        return;
+
+    pa_log_info("Dropping root priviliges.");
+
+#if defined(HAVE_SETRESUID)
+    pa_assert_se(setresuid(uid, uid, uid) >= 0);
+#elif defined(HAVE_SETREUID)
+    pa_assert_se(setreuid(uid, uid) >= 0);
+#else
+    pa_assert_se(setuid(uid) >= 0);
+    pa_assert_se(seteuid(uid) >= 0);
+#endif
+
+    pa_assert_se(getuid() == uid);
+    pa_assert_se(geteuid() == uid);
+}
+
+#else
+
+void pa_drop_root(void) {
+}
+
+#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_SYS_PRCTL_H)
+
+/* Limit permitted capabilities set to CAPSYS_NICE */
+void pa_limit_caps(void) {
+    cap_t caps;
+    cap_value_t nice_cap = CAP_SYS_NICE;
+
+    pa_assert_se(caps = cap_init());
+    pa_assert_se(cap_clear(caps) == 0);
+    pa_assert_se(cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET) == 0);
+    pa_assert_se(cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET) == 0);
+
+    if (cap_set_proc(caps) < 0)
+        /* Hmm, so we couldn't limit our caps, which probably means we
+         * hadn't any in the first place, so let's just make sure of
+         * that */
+        pa_drop_caps();
+    else
+        pa_log_info("Limited capabilities successfully to CAP_SYS_NICE.");
+
+    pa_assert_se(cap_free(caps) == 0);
+
+    pa_assert_se(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0);
+}
+
+/* Drop all capabilities, effectively becoming a normal user */
+void pa_drop_caps(void) {
+    cap_t caps;
+
+    pa_assert_se(prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == 0);
+
+    pa_assert_se(caps = cap_init());
+    pa_assert_se(cap_clear(caps) == 0);
+    pa_assert_se(cap_set_proc(caps) == 0);
+    pa_assert_se(cap_free(caps) == 0);
+
+    pa_assert_se(!pa_have_caps());
+}
+
+pa_bool_t pa_have_caps(void) {
+    cap_t caps;
+    cap_flag_value_t flag = CAP_CLEAR;
+
+    pa_assert_se(caps = cap_get_proc());
+    pa_assert_se(cap_get_flag(caps, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0);
+    pa_assert_se(cap_free(caps) == 0);
+
+    return flag == CAP_SET;
+}
+
+#else
+
+/* NOOPs in case capabilities are not available. */
+void pa_limit_caps(void) {
+}
+
+void pa_drop_caps(void) {
+    pa_drop_root();
+}
+
+pa_bool_t pa_have_caps(void) {
+    return FALSE;
+}
+
+#endif
similarity index 61%
rename from polyp/caps.h
rename to src/daemon/caps.h
index 3bb861d1d71d1e9eeccef11ad3faab83cc2fae91..176aa90e4600380b867bfd91516cee7b7c49c162 100644 (file)
@@ -1,29 +1,32 @@
 #ifndef foocapshfoo
 #define foocapshfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
+#include <pulsecore/macro.h>
+
 void pa_drop_root(void);
-int pa_limit_caps(void);
-int pa_drop_caps(void);
+void pa_drop_caps(void);
+void pa_limit_caps(void);
+pa_bool_t pa_have_caps(void);
 
 #endif
similarity index 63%
rename from polyp/cmdline.c
rename to src/daemon/cmdline.c
index 0951725a1e38d9364c5d8e91dafa1c6eec172b35..4b2466ce57b06bc6b3492437beaa4a19140b7e91 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #endif
 
 #include <string.h>
-#include <assert.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <getopt.h>
 #include <sys/stat.h>
 
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/macro.h>
+
 #include "cmdline.h"
-#include "util.h"
-#include "strbuf.h"
-#include "xmalloc.h"
 
 /* Argument codes for getopt_long() */
 enum {
@@ -45,6 +47,7 @@ enum {
     ARG_FAIL,
     ARG_LOG_LEVEL,
     ARG_HIGH_PRIORITY,
+    ARG_REALTIME,
     ARG_DISALLOW_MODULE_LOADING,
     ARG_EXIT_IDLE_TIME,
     ARG_MODULE_IDLE_TIME,
@@ -56,11 +59,17 @@ enum {
     ARG_RESAMPLE_METHOD,
     ARG_KILL,
     ARG_USE_PID_FILE,
-    ARG_CHECK
+    ARG_CHECK,
+    ARG_NO_CPU_LIMIT,
+    ARG_DISABLE_SHM,
+    ARG_DUMP_RESAMPLE_METHODS,
+    ARG_SYSTEM,
+    ARG_CLEANUP_SHM,
+    ARG_START
 };
 
 /* Tabel for getopt_long() */
-static struct option long_options[] = {
+static const struct option long_options[] = {
     {"help",                        0, 0, ARG_HELP},
     {"version",                     0, 0, ARG_VERSION},
     {"dump-conf",                   0, 0, ARG_DUMP_CONF},
@@ -70,6 +79,7 @@ static struct option long_options[] = {
     {"verbose",                     2, 0, ARG_LOG_LEVEL},
     {"log-level",                   2, 0, ARG_LOG_LEVEL},
     {"high-priority",               2, 0, ARG_HIGH_PRIORITY},
+    {"realtime",                    2, 0, ARG_REALTIME},
     {"disallow-module-loading",     2, 0, ARG_DISALLOW_MODULE_LOADING},
     {"exit-idle-time",              2, 0, ARG_EXIT_IDLE_TIME},
     {"module-idle-time",            2, 0, ARG_MODULE_IDLE_TIME},
@@ -80,33 +90,49 @@ static struct option long_options[] = {
     {"dl-search-path",              1, 0, ARG_DL_SEARCH_PATH},
     {"resample-method",             1, 0, ARG_RESAMPLE_METHOD},
     {"kill",                        0, 0, ARG_KILL},
+    {"start",                       0, 0, ARG_START},
     {"use-pid-file",                2, 0, ARG_USE_PID_FILE},
     {"check",                       0, 0, ARG_CHECK},
+    {"system",                      2, 0, ARG_SYSTEM},
+    {"no-cpu-limit",                2, 0, ARG_NO_CPU_LIMIT},
+    {"disable-shm",                 2, 0, ARG_DISABLE_SHM},
+    {"dump-resample-methods",       2, 0, ARG_DUMP_RESAMPLE_METHODS},
+    {"cleanup-shm",                 2, 0, ARG_CLEANUP_SHM},
     {NULL, 0, 0, 0}
 };
 
 void pa_cmdline_help(const char *argv0) {
     const char *e;
 
+    pa_assert(argv0);
+
     if ((e = strrchr(argv0, '/')))
         e++;
     else
         e = argv0;
-    
+
     printf("%s [options]\n\n"
            "COMMANDS:\n"
            "  -h, --help                            Show this help\n"
            "      --version                         Show version\n"
            "      --dump-conf                       Dump default configuration\n"
            "      --dump-modules                    Dump list of available modules\n"
+           "      --dump-resample-methods           Dump available resample methods\n"
+           "      --cleanup-shm                     Cleanup stale shared memory segments\n"
+           "      --start                           Start the daemon if it is not running\n"
            "  -k  --kill                            Kill a running daemon\n"
            "      --check                           Check for a running daemon\n\n"
 
            "OPTIONS:\n"
+           "      --system[=BOOL]                   Run as system-wide instance\n"
            "  -D, --daemonize[=BOOL]                Daemonize after startup\n"
            "      --fail[=BOOL]                     Quit when startup fails\n"
-           "      --high-priority[=BOOL]            Try to set high process priority\n"
-           "                                        (only available as root)\n"
+           "      --high-priority[=BOOL]            Try to set high nice level\n"
+           "                                        (only available as root, when SUID or\n"
+           "                                        with elevated RLIMIT_NICE)\n"
+           "      --realtime[=BOOL]                 Try to enable realtime scheduling\n"
+           "                                        (only available as root, when SUID or\n"
+           "                                        with elevated RLIMIT_RTPRIO)\n"
            "      --disallow-module-loading[=BOOL]  Disallow module loading after startup\n"
            "      --exit-idle-time=SECS             Terminate the daemon when idle and this\n"
            "                                        time passed\n"
@@ -115,15 +141,17 @@ void pa_cmdline_help(const char *argv0) {
            "      --scache-idle-time=SECS           Unload autoloaded samples when idle and\n"
            "                                        this time passed\n"
            "      --log-level[=LEVEL]               Increase or set verbosity level\n"
-           "  -v                                    Increase the verbosity level\n" 
+           "  -v                                    Increase the verbosity level\n"
            "      --log-target={auto,syslog,stderr} Specify the log target\n"
            "  -p, --dl-search-path=PATH             Set the search path for dynamic shared\n"
            "                                        objects (plugins)\n"
-           "      --resample-method=[METHOD]        Use the specified resampling method\n"
-           "                                        (one of src-sinc-medium-quality,\n"
-           "                                        src-sinc-best-quality,src-sinc-fastest\n"
-           "                                        src-zero-order-hold,src-linear,trivial)\n"
-           "      --use-pid-file[=BOOL]             Create a PID file\n\n"
+           "      --resample-method=METHOD          Use the specified resampling method\n"
+           "                                        (See --dump-resample-methods for\n"
+           "                                        possible values)\n"
+           "      --use-pid-file[=BOOL]             Create a PID file\n"
+           "      --no-cpu-limit[=BOOL]             Do not install CPU load limiter on\n"
+           "                                        platforms that support it.\n"
+           "      --disable-shm[=BOOL]              Disable shared memory support.\n\n"
 
            "STARTUP SCRIPT:\n"
            "  -L, --load=\"MODULE ARGUMENTS\"         Load the specified plugin module with\n"
@@ -131,20 +159,23 @@ void pa_cmdline_help(const char *argv0) {
            "  -F, --file=FILENAME                   Run the specified script\n"
            "  -C                                    Open a command line on the running TTY\n"
            "                                        after startup\n\n"
-           
+
            "  -n                                    Don't load default script file\n", e);
 }
 
-int pa_cmdline_parse(struct pa_daemon_conf *conf, int argc, char *const argv [], int *d) {
-    struct pa_strbuf *buf = NULL;
+int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) {
+    pa_strbuf *buf = NULL;
     int c;
-    assert(conf && argc && argv);
+
+    pa_assert(conf);
+    pa_assert(argc > 0);
+    pa_assert(argv);
 
     buf = pa_strbuf_new();
 
     if (conf->script_commands)
         pa_strbuf_puts(buf, conf->script_commands);
-    
+
     while ((c = getopt_long(argc, argv, "L:F:ChDnp:kv", long_options, NULL)) != -1) {
         switch (c) {
             case ARG_HELP:
@@ -164,40 +195,56 @@ int pa_cmdline_parse(struct pa_daemon_conf *conf, int argc, char *const argv [],
                 conf->cmd = PA_CMD_DUMP_MODULES;
                 break;
 
+            case ARG_DUMP_RESAMPLE_METHODS:
+                conf->cmd = PA_CMD_DUMP_RESAMPLE_METHODS;
+                break;
+
+            case ARG_CLEANUP_SHM:
+                conf->cmd = PA_CMD_CLEANUP_SHM;
+                break;
+
             case 'k':
             case ARG_KILL:
                 conf->cmd = PA_CMD_KILL;
                 break;
 
+            case ARG_START:
+                conf->cmd = PA_CMD_START;
+                conf->daemonize = TRUE;
+                break;
+
             case ARG_CHECK:
                 conf->cmd = PA_CMD_CHECK;
                 break;
-                
+
             case ARG_LOAD:
             case 'L':
                 pa_strbuf_printf(buf, "load-module %s\n", optarg);
                 break;
-                
+
             case ARG_FILE:
-            case 'F':
-                pa_strbuf_printf(buf, ".include %s\n", optarg);
+            case 'F': {
+                char *p;
+                pa_strbuf_printf(buf, ".include %s\n", p = pa_make_path_absolute(optarg));
+                pa_xfree(p);
                 break;
-                
+            }
+
             case 'C':
-                pa_strbuf_puts(buf, "load-module module-cli\n");
+                pa_strbuf_puts(buf, "load-module module-cli exit_on_eof=1\n");
                 break;
-                
+
             case ARG_DAEMONIZE:
             case 'D':
-                if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
-                    pa_log(__FILE__": --daemonize expects boolean argument\n");
+                if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                    pa_log("--daemonize expects boolean argument");
                     goto fail;
                 }
                 break;
 
             case ARG_FAIL:
-                if ((conf->fail = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
-                    pa_log(__FILE__": --fail expects boolean argument\n");
+                if ((conf->fail = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                    pa_log("--fail expects boolean argument");
                     goto fail;
                 }
                 break;
@@ -207,51 +254,57 @@ int pa_cmdline_parse(struct pa_daemon_conf *conf, int argc, char *const argv [],
 
                 if (optarg) {
                     if (pa_daemon_conf_set_log_level(conf, optarg) < 0) {
-                        pa_log(__FILE__": --log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error).\n");
+                        pa_log("--log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error).");
                         goto fail;
                     }
                 } else {
                     if (conf->log_level < PA_LOG_LEVEL_MAX-1)
                         conf->log_level++;
                 }
-                
+
                 break;
 
             case ARG_HIGH_PRIORITY:
-                if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
-                    pa_log(__FILE__": --high-priority expects boolean argument\n");
+                if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                    pa_log("--high-priority expects boolean argument");
+                    goto fail;
+                }
+                break;
+
+            case ARG_REALTIME:
+                if ((conf->realtime_scheduling = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                    pa_log("--realtime expects boolean argument");
                     goto fail;
                 }
                 break;
 
             case ARG_DISALLOW_MODULE_LOADING:
-                if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
-                    pa_log(__FILE__": --disallow-module-loading expects boolean argument\n");
+                if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                    pa_log("--disallow-module-loading expects boolean argument");
                     goto fail;
                 }
                 break;
 
             case ARG_USE_PID_FILE:
-                if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
-                    pa_log(__FILE__": --use-pid-file expects boolean argument\n");
+                if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                    pa_log("--use-pid-file expects boolean argument");
                     goto fail;
                 }
                 break;
-                
+
             case 'p':
             case ARG_DL_SEARCH_PATH:
                 pa_xfree(conf->dl_search_path);
                 conf->dl_search_path = *optarg ? pa_xstrdup(optarg) : NULL;
                 break;
-                
+
             case 'n':
-                pa_xfree(conf->default_script_file);
-                conf->default_script_file = NULL;
+                conf->load_default_script_file = FALSE;
                 break;
 
             case ARG_LOG_TARGET:
                 if (pa_daemon_conf_set_log_target(conf, optarg) < 0) {
-                    pa_log(__FILE__": Invalid log target: use either 'syslog', 'stderr' or 'auto'.\n");
+                    pa_log("Invalid log target: use either 'syslog', 'stderr' or 'auto'.");
                     goto fail;
                 }
                 break;
@@ -270,11 +323,32 @@ int pa_cmdline_parse(struct pa_daemon_conf *conf, int argc, char *const argv [],
 
             case ARG_RESAMPLE_METHOD:
                 if (pa_daemon_conf_set_resample_method(conf, optarg) < 0) {
-                    pa_log(__FILE__": Invalid resample method '%s'.\n", optarg);
+                    pa_log("Invalid resample method '%s'.", optarg);
+                    goto fail;
+                }
+                break;
+
+            case ARG_SYSTEM:
+                if ((conf->system_instance = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                    pa_log("--system expects boolean argument");
+                    goto fail;
+                }
+                break;
+
+            case ARG_NO_CPU_LIMIT:
+                if ((conf->no_cpu_limit = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                    pa_log("--no-cpu-limit expects boolean argument");
                     goto fail;
                 }
                 break;
-                
+
+            case ARG_DISABLE_SHM:
+                if ((conf->disable_shm = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+                    pa_log("--disable-shm expects boolean argument");
+                    goto fail;
+                }
+                break;
+
             default:
                 goto fail;
         }
@@ -289,12 +363,12 @@ int pa_cmdline_parse(struct pa_daemon_conf *conf, int argc, char *const argv [],
     }
 
     *d = optind;
-    
+
     return 0;
-    
+
 fail:
     if (buf)
         pa_strbuf_free(buf);
-    
+
     return -1;
 }
similarity index 71%
rename from polyp/cmdline.h
rename to src/daemon/cmdline.h
index ca100d1d89c20d26212e94714d9a15c0269028f8..fd72a6d3a5b973e9371397ecf9d130e49c30fd5f 100644 (file)
@@ -1,23 +1,23 @@
 #ifndef foocmdlinehfoo
 #define foocmdlinehfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
@@ -26,7 +26,7 @@
 
 /* Parese the command line and store its data in *c. Return the index
  * of the first unparsed argument in *d. */
-int pa_cmdline_parse(struct pa_daemon_conf*c, int argc, char *const argv [], int *d);
+int pa_cmdline_parse(pa_daemon_conf*c, int argc, char *const argv [], int *d);
 
 /* Show the command line help. The command name is extracted from
  * argv[0] which should be passed in argv0. */
similarity index 66%
rename from polyp/cpulimit.c
rename to src/daemon/cpulimit.c
index 0fab98a029b5d695c315daa00332631340ea1bd5..42a71f7e156414023f33b8003cc604314a560741 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <config.h>
 #endif
 
+#include <pulse/error.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cpulimit.h"
+
+#ifdef HAVE_SIGXCPU
+
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
-#include <assert.h>
 #include <sys/time.h>
-#include <sys/resource.h>
 #include <unistd.h>
 #include <signal.h>
 
-#include "cpulimit.h"
-#include "util.h"
-#include "log.h"
-
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
 
 /* This module implements a watchdog that makes sure that the current
  * process doesn't consume more than 70% CPU time for 10 seconds. This
@@ -55,8 +63,8 @@
 /* Check every 10s */
 #define CPUTIME_INTERVAL_SOFT (10)
 
-/* Recheck after 2s */
-#define CPUTIME_INTERVAL_HARD (2)
+/* Recheck after 5s */
+#define CPUTIME_INTERVAL_HARD (5)
 
 /* Time of the last CPU load check */
 static time_t last_time = 0;
@@ -65,14 +73,14 @@ static time_t last_time = 0;
 static int the_pipe[2] = {-1, -1};
 
 /* Main event loop and IO event for the FIFO */
-static struct pa_mainloop_api *api = NULL;
-static struct pa_io_event *io_event = NULL;
+static pa_mainloop_api *api = NULL;
+static pa_io_event *io_event = NULL;
 
 /* Saved sigaction struct for SIGXCPU */
 static struct sigaction sigaction_prev;
 
 /* Nonzero after pa_cpu_limit_init() */
-static int installed = 0; 
+static pa_bool_t installed = FALSE;
 
 /* The current state of operation */
 static enum  {
@@ -82,33 +90,31 @@ static enum  {
 
 /* Reset the SIGXCPU timer to the next t seconds */
 static void reset_cpu_time(int t) {
-    int r;
     long n;
     struct rlimit rl;
     struct rusage ru;
 
     /* Get the current CPU time of the current process */
-    r = getrusage(RUSAGE_SELF, &ru);
-    assert(r >= 0);
+    pa_assert_se(getrusage(RUSAGE_SELF, &ru) >= 0);
 
     n = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec + t;
-
-    r = getrlimit(RLIMIT_CPU, &rl);
-    assert(r >= 0);
+    pa_assert_se(getrlimit(RLIMIT_CPU, &rl) >= 0);
 
     rl.rlim_cur = n;
-    r = setrlimit(RLIMIT_CPU, &rl);
-    assert(r >= 0);
+    pa_assert_se(setrlimit(RLIMIT_CPU, &rl) >= 0);
 }
 
 /* A simple, thread-safe puts() work-alike */
 static void write_err(const char *p) {
-    pa_loop_write(2, p, strlen(p));
+    pa_loop_write(2, p, strlen(p), NULL);
 }
 
 /* The signal handler, called on every SIGXCPU */
 static void signal_handler(int sig) {
-    assert(sig == SIGXCPU);
+    int saved_errno;
+
+    saved_errno = errno;
+    pa_assert(sig == SIGXCPU);
 
     if (phase == PHASE_IDLE) {
         time_t now;
@@ -120,58 +126,71 @@ static void signal_handler(int sig) {
         time(&now);
 
 #ifdef PRINT_CPU_LOAD
-        snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", (double)CPUTIME_INTERVAL_SOFT/(now-last_time)*100);
+        pa_snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", (double)CPUTIME_INTERVAL_SOFT/(now-last_time)*100);
         write_err(t);
 #endif
-        
+
         if (CPUTIME_INTERVAL_SOFT >= ((now-last_time)*(double)CPUTIME_PERCENT/100)) {
             static const char c = 'X';
 
             write_err("Soft CPU time limit exhausted, terminating.\n");
-            
+
             /* Try a soft cleanup */
             write(the_pipe[1], &c, sizeof(c));
             phase = PHASE_SOFT;
             reset_cpu_time(CPUTIME_INTERVAL_HARD);
-            
+
         } else {
 
             /* Everything's fine */
             reset_cpu_time(CPUTIME_INTERVAL_SOFT);
             last_time = now;
         }
-        
+
     } else if (phase == PHASE_SOFT) {
         write_err("Hard CPU time limit exhausted, terminating forcibly.\n");
-        _exit(1); /* Forced exit */
+        abort(); /* Forced exit */
     }
+
+    errno = saved_errno;
 }
 
 /* Callback for IO events on the FIFO */
-static void callback(struct pa_mainloop_api*m, struct pa_io_event*e, int fd, enum pa_io_event_flags f, void *userdata) {
+static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata) {
     char c;
-    assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]);
-    read(the_pipe[0], &c, sizeof(c));
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(f == PA_IO_EVENT_INPUT);
+    pa_assert(e == io_event);
+    pa_assert(fd == the_pipe[0]);
+
+    pa_read(the_pipe[0], &c, sizeof(c), NULL);
     m->quit(m, 1); /* Quit the main loop */
 }
 
 /* Initializes CPU load limiter */
-int pa_cpu_limit_init(struct pa_mainloop_api *m) {
+int pa_cpu_limit_init(pa_mainloop_api *m) {
     struct sigaction sa;
-    assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1 && !installed);
-    
+
+    pa_assert(m);
+    pa_assert(!api);
+    pa_assert(!io_event);
+    pa_assert(the_pipe[0] == -1);
+    pa_assert(the_pipe[1] == -1);
+    pa_assert(!installed);
+
     time(&last_time);
 
     /* Prepare the main loop pipe */
     if (pipe(the_pipe) < 0) {
-        pa_log(__FILE__": pipe() failed: %s\n", strerror(errno));
+        pa_log("pipe() failed: %s", pa_cstrerror(errno));
         return -1;
     }
 
-    pa_make_nonblock_fd(the_pipe[0]);
-    pa_make_nonblock_fd(the_pipe[1]);
-    pa_fd_set_cloexec(the_pipe[0], 1);
-    pa_fd_set_cloexec(the_pipe[1], 1);
+    pa_make_fd_nonblock(the_pipe[0]);
+    pa_make_fd_nonblock(the_pipe[1]);
+    pa_make_fd_cloexec(the_pipe[0]);
+    pa_make_fd_cloexec(the_pipe[1]);
 
     api = m;
     io_event = api->io_new(m, the_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
@@ -183,39 +202,44 @@ int pa_cpu_limit_init(struct pa_mainloop_api *m) {
     sa.sa_handler = signal_handler;
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = SA_RESTART;
-    
+
     if (sigaction(SIGXCPU, &sa, &sigaction_prev) < 0) {
         pa_cpu_limit_done();
         return -1;
     }
 
-    installed = 1;
+    installed = TRUE;
 
     reset_cpu_time(CPUTIME_INTERVAL_SOFT);
-    
+
     return 0;
 }
 
 /* Shutdown CPU load limiter */
 void pa_cpu_limit_done(void) {
-    int r;
 
     if (io_event) {
-        assert(api);
+        pa_assert(api);
         api->io_free(io_event);
         io_event = NULL;
         api = NULL;
     }
 
-    if (the_pipe[0] >= 0)
-        close(the_pipe[0]);
-    if (the_pipe[1] >= 0)
-        close(the_pipe[1]);
-    the_pipe[0] = the_pipe[1] = -1;
+    pa_close_pipe(the_pipe);
 
     if (installed) {
-        r = sigaction(SIGXCPU, &sigaction_prev, NULL);
-        assert(r >= 0);
-        installed = 0;
+        pa_assert_se(sigaction(SIGXCPU, &sigaction_prev, NULL) >= 0);
+        installed = FALSE;
     }
 }
+
+#else /* HAVE_SIGXCPU */
+
+int pa_cpu_limit_init(PA_GCC_UNUSED pa_mainloop_api *m) {
+    return 0;
+}
+
+void pa_cpu_limit_done(void) {
+}
+
+#endif
similarity index 65%
rename from polyp/cpulimit.h
rename to src/daemon/cpulimit.h
index a171da9b4209cfd577e6cce767a77ac8ac77eee3..cb9a123d92329edae5fb0e80dd950a1598073cf3 100644 (file)
@@ -1,34 +1,34 @@
 #ifndef foocpulimithfoo
 #define foocpulimithfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "mainloop-api.h"
+#include <pulse/mainloop-api.h>
 
-/* This kills the polypaudio process if it eats more than 70% of the
+/* This kills the pulseaudio process if it eats more than 70% of the
  * CPU time. This is build around setrlimit() and SIGXCPU. It is handy
  * in case of using SCHED_FIFO which may freeze the whole machine  */
 
-int pa_cpu_limit_init(struct pa_mainloop_api *m);
+int pa_cpu_limit_init(pa_mainloop_api *m);
 void pa_cpu_limit_done(void);
 
 #endif
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
new file mode 100644 (file)
index 0000000..9ac4090
--- /dev/null
@@ -0,0 +1,699 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sched.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/conf-parser.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+
+#include "daemon-conf.h"
+
+#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa"
+#define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa"
+#define DEFAULT_SYSTEM_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "system.pa"
+
+#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf"
+#define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf"
+
+#define ENV_SCRIPT_FILE "PULSE_SCRIPT"
+#define ENV_CONFIG_FILE "PULSE_CONFIG"
+#define ENV_DL_SEARCH_PATH "PULSE_DLPATH"
+
+static const pa_daemon_conf default_conf = {
+    .cmd = PA_CMD_DAEMON,
+    .daemonize = FALSE,
+    .fail = TRUE,
+    .high_priority = TRUE,
+    .nice_level = -11,
+    .realtime_scheduling = FALSE,
+    .realtime_priority = 5,  /* Half of JACK's default rtprio */
+    .disallow_module_loading = FALSE,
+    .exit_idle_time = -1,
+    .module_idle_time = 20,
+    .scache_idle_time = 20,
+    .auto_log_target = 1,
+    .script_commands = NULL,
+    .dl_search_path = NULL,
+    .load_default_script_file = TRUE,
+    .default_script_file = NULL,
+    .log_target = PA_LOG_SYSLOG,
+    .log_level = PA_LOG_NOTICE,
+    .resample_method = PA_RESAMPLER_AUTO,
+    .disable_remixing = FALSE,
+    .config_file = NULL,
+    .use_pid_file = TRUE,
+    .system_instance = FALSE,
+    .no_cpu_limit = FALSE,
+    .disable_shm = FALSE,
+    .default_n_fragments = 4,
+    .default_fragment_size_msec = 25,
+    .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 }
+#ifdef HAVE_SYS_RESOURCE_H
+   ,.rlimit_fsize = { .value = 0, .is_set = FALSE },
+    .rlimit_data = { .value = 0, .is_set = FALSE },
+    .rlimit_stack = { .value = 0, .is_set = FALSE },
+    .rlimit_core = { .value = 0, .is_set = FALSE },
+    .rlimit_rss = { .value = 0, .is_set = FALSE }
+#ifdef RLIMIT_NPROC
+   ,.rlimit_nproc = { .value = 0, .is_set = FALSE }
+#endif
+   ,.rlimit_nofile = { .value = 256, .is_set = TRUE }
+#ifdef RLIMIT_MEMLOCK
+   ,.rlimit_memlock = { .value = 0, .is_set = FALSE }
+#endif
+   ,.rlimit_as = { .value = 0, .is_set = FALSE }
+#ifdef RLIMIT_LOCKS
+   ,.rlimit_locks = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_SIGPENDING
+   ,.rlimit_sigpending = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_MSGQUEUE
+   ,.rlimit_msgqueue = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_NICE
+   ,.rlimit_nice = { .value = 31, .is_set = TRUE }     /* nice level of -11 */
+#endif
+#ifdef RLIMIT_RTPRIO
+   ,.rlimit_rtprio = { .value = 9, .is_set = TRUE }    /* One below JACK's default for the server */
+#endif
+#ifdef RLIMIT_RTTIME
+   ,.rlimit_rttime = { .value = PA_USEC_PER_SEC, .is_set = TRUE }
+#endif
+#endif
+};
+
+pa_daemon_conf* pa_daemon_conf_new(void) {
+    pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
+
+    c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
+    return c;
+}
+
+void pa_daemon_conf_free(pa_daemon_conf *c) {
+    pa_assert(c);
+    pa_xfree(c->script_commands);
+    pa_xfree(c->dl_search_path);
+    pa_xfree(c->default_script_file);
+    pa_xfree(c->config_file);
+    pa_xfree(c);
+}
+
+int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) {
+    pa_assert(c);
+    pa_assert(string);
+
+    if (!strcmp(string, "auto"))
+        c->auto_log_target = 1;
+    else if (!strcmp(string, "syslog")) {
+        c->auto_log_target = 0;
+        c->log_target = PA_LOG_SYSLOG;
+    } else if (!strcmp(string, "stderr")) {
+        c->auto_log_target = 0;
+        c->log_target = PA_LOG_STDERR;
+    } else
+        return -1;
+
+    return 0;
+}
+
+int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) {
+    uint32_t u;
+    pa_assert(c);
+    pa_assert(string);
+
+    if (pa_atou(string, &u) >= 0) {
+        if (u >= PA_LOG_LEVEL_MAX)
+            return -1;
+
+        c->log_level = (pa_log_level_t) u;
+    } else if (pa_startswith(string, "debug"))
+        c->log_level = PA_LOG_DEBUG;
+    else if (pa_startswith(string, "info"))
+        c->log_level = PA_LOG_INFO;
+    else if (pa_startswith(string, "notice"))
+        c->log_level = PA_LOG_NOTICE;
+    else if (pa_startswith(string, "warn"))
+        c->log_level = PA_LOG_WARN;
+    else if (pa_startswith(string, "err"))
+        c->log_level = PA_LOG_ERROR;
+    else
+        return -1;
+
+    return 0;
+}
+
+int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {
+    int m;
+    pa_assert(c);
+    pa_assert(string);
+
+    if ((m = pa_parse_resample_method(string)) < 0)
+        return -1;
+
+    c->resample_method = m;
+    return 0;
+}
+
+static int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    pa_daemon_conf *c = data;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if (pa_daemon_conf_set_log_target(c, rvalue) < 0) {
+        pa_log("[%s:%u] Invalid log target '%s'.", filename, line, rvalue);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int parse_log_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    pa_daemon_conf *c = data;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if (pa_daemon_conf_set_log_level(c, rvalue) < 0) {
+        pa_log("[%s:%u] Invalid log level '%s'.", filename, line, rvalue);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    pa_daemon_conf *c = data;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if (pa_daemon_conf_set_resample_method(c, rvalue) < 0) {
+        pa_log("[%s:%u] Invalid resample method '%s'.", filename, line, rvalue);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int parse_rlimit(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+#ifdef HAVE_SYS_RESOURCE_H
+    struct pa_rlimit *r = data;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(r);
+
+    if (rvalue[strspn(rvalue, "\t ")] == 0) {
+        /* Empty string */
+        r->is_set = 0;
+        r->value = 0;
+    } else {
+        int32_t k;
+        if (pa_atoi(rvalue, &k) < 0) {
+            pa_log("[%s:%u] Invalid rlimit '%s'.", filename, line, rvalue);
+            return -1;
+        }
+        r->is_set = k >= 0;
+        r->value = k >= 0 ? (rlim_t) k : 0;
+    }
+#else
+    pa_log_warn("[%s:%u] rlimit not supported on this platform.", filename, line);
+#endif
+
+    return 0;
+}
+
+static int parse_sample_format(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    pa_daemon_conf *c = data;
+    pa_sample_format_t f;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if ((f = pa_parse_sample_format(rvalue)) < 0) {
+        pa_log("[%s:%u] Invalid sample format '%s'.", filename, line, rvalue);
+        return -1;
+    }
+
+    c->default_sample_spec.format = f;
+    return 0;
+}
+
+static int parse_sample_rate(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    pa_daemon_conf *c = data;
+    int32_t r;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if (pa_atoi(rvalue, &r) < 0 || r > (int32_t) PA_RATE_MAX || r <= 0) {
+        pa_log("[%s:%u] Invalid sample rate '%s'.", filename, line, rvalue);
+        return -1;
+    }
+
+    c->default_sample_spec.rate = r;
+    return 0;
+}
+
+static int parse_sample_channels(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    pa_daemon_conf *c = data;
+    int32_t n;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if (pa_atoi(rvalue, &n) < 0 || n > (int32_t) PA_CHANNELS_MAX || n <= 0) {
+        pa_log("[%s:%u] Invalid sample channels '%s'.", filename, line, rvalue);
+        return -1;
+    }
+
+    c->default_sample_spec.channels = (uint8_t) n;
+    return 0;
+}
+
+static int parse_fragments(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    pa_daemon_conf *c = data;
+    int32_t n;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if (pa_atoi(rvalue, &n) < 0 || n < 2) {
+        pa_log("[%s:%u] Invalid number of fragments '%s'.", filename, line, rvalue);
+        return -1;
+    }
+
+    c->default_n_fragments = (unsigned) n;
+    return 0;
+}
+
+static int parse_fragment_size_msec(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    pa_daemon_conf *c = data;
+    int32_t n;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if (pa_atoi(rvalue, &n) < 0 || n < 1) {
+        pa_log("[%s:%u] Invalid fragment size '%s'.", filename, line, rvalue);
+        return -1;
+    }
+
+    c->default_fragment_size_msec = (unsigned) n;
+    return 0;
+}
+
+static int parse_nice_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    pa_daemon_conf *c = data;
+    int32_t level;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if (pa_atoi(rvalue, &level) < 0 || level < -20 || level > 19) {
+        pa_log("[%s:%u] Invalid nice level '%s'.", filename, line, rvalue);
+        return -1;
+    }
+
+    c->nice_level = (int) level;
+    return 0;
+}
+
+static int parse_rtprio(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    pa_daemon_conf *c = data;
+    int32_t rtprio;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if (pa_atoi(rvalue, &rtprio) < 0 || rtprio < sched_get_priority_min(SCHED_FIFO) || rtprio > sched_get_priority_max(SCHED_FIFO)) {
+        pa_log("[%s:%u] Invalid realtime priority '%s'.", filename, line, rvalue);
+        return -1;
+    }
+
+    c->realtime_priority = (int) rtprio;
+    return 0;
+}
+
+int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
+    int r = -1;
+    FILE *f = NULL;
+
+    pa_config_item table[] = {
+        { "daemonize",                  pa_config_parse_bool,     NULL },
+        { "fail",                       pa_config_parse_bool,     NULL },
+        { "high-priority",              pa_config_parse_bool,     NULL },
+        { "realtime-scheduling",        pa_config_parse_bool,     NULL },
+        { "disallow-module-loading",    pa_config_parse_bool,     NULL },
+        { "use-pid-file",               pa_config_parse_bool,     NULL },
+        { "system-instance",            pa_config_parse_bool,     NULL },
+        { "no-cpu-limit",               pa_config_parse_bool,     NULL },
+        { "disable-shm",                pa_config_parse_bool,     NULL },
+        { "exit-idle-time",             pa_config_parse_int,      NULL },
+        { "module-idle-time",           pa_config_parse_int,      NULL },
+        { "scache-idle-time",           pa_config_parse_int,      NULL },
+        { "realtime-priority",          parse_rtprio,             NULL },
+        { "dl-search-path",             pa_config_parse_string,   NULL },
+        { "default-script-file",        pa_config_parse_string,   NULL },
+        { "log-target",                 parse_log_target,         NULL },
+        { "log-level",                  parse_log_level,          NULL },
+        { "verbose",                    parse_log_level,          NULL },
+        { "resample-method",            parse_resample_method,    NULL },
+        { "default-sample-format",      parse_sample_format,      NULL },
+        { "default-sample-rate",        parse_sample_rate,        NULL },
+        { "default-sample-channels",    parse_sample_channels,    NULL },
+        { "default-fragments",          parse_fragments,          NULL },
+        { "default-fragment-size-msec", parse_fragment_size_msec, NULL },
+        { "nice-level",                 parse_nice_level,         NULL },
+        { "disable-remixing",           pa_config_parse_bool,     NULL },
+        { "load-default-script-file",   pa_config_parse_bool,     NULL },
+#ifdef HAVE_SYS_RESOURCE_H
+        { "rlimit-fsize",               parse_rlimit,             NULL },
+        { "rlimit-data",                parse_rlimit,             NULL },
+        { "rlimit-stack",               parse_rlimit,             NULL },
+        { "rlimit-core",                parse_rlimit,             NULL },
+        { "rlimit-rss",                 parse_rlimit,             NULL },
+        { "rlimit-nofile",              parse_rlimit,             NULL },
+        { "rlimit-as",                  parse_rlimit,             NULL },
+#ifdef RLIMIT_NPROC
+        { "rlimit-nproc",               parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_MEMLOCK
+        { "rlimit-memlock",             parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_LOCKS
+        { "rlimit-locks",               parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_SIGPENDING
+        { "rlimit-sigpending",          parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_MSGQUEUE
+        { "rlimit-msgqueue",            parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_NICE
+        { "rlimit-nice",                parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_RTPRIO
+        { "rlimit-rtprio",              parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_RTTIME
+        { "rlimit-rttime",              parse_rlimit,             NULL },
+#endif
+#endif
+        { NULL,                         NULL,                     NULL },
+    };
+
+    table[0].data = &c->daemonize;
+    table[1].data = &c->fail;
+    table[2].data = &c->high_priority;
+    table[3].data = &c->realtime_scheduling;
+    table[4].data = &c->disallow_module_loading;
+    table[5].data = &c->use_pid_file;
+    table[6].data = &c->system_instance;
+    table[7].data = &c->no_cpu_limit;
+    table[8].data = &c->disable_shm;
+    table[9].data = &c->exit_idle_time;
+    table[10].data = &c->module_idle_time;
+    table[11].data = &c->scache_idle_time;
+    table[12].data = c;
+    table[13].data = &c->dl_search_path;
+    table[14].data = &c->default_script_file;
+    table[15].data = c;
+    table[16].data = c;
+    table[17].data = c;
+    table[18].data = c;
+    table[19].data = c;
+    table[20].data = c;
+    table[21].data = c;
+    table[22].data = c;
+    table[23].data = c;
+    table[24].data = c;
+    table[25].data = &c->disable_remixing;
+    table[26].data = &c->load_default_script_file;
+#ifdef HAVE_SYS_RESOURCE_H
+    table[27].data = &c->rlimit_fsize;
+    table[28].data = &c->rlimit_data;
+    table[29].data = &c->rlimit_stack;
+    table[30].data = &c->rlimit_as;
+    table[31].data = &c->rlimit_core;
+    table[32].data = &c->rlimit_nofile;
+    table[33].data = &c->rlimit_as;
+#ifdef RLIMIT_NPROC
+    table[34].data = &c->rlimit_nproc;
+#endif
+
+#ifdef RLIMIT_MEMLOCK
+#ifndef RLIMIT_NPROC
+#error "Houston, we have a numbering problem!"
+#endif
+    table[35].data = &c->rlimit_memlock;
+#endif
+
+#ifdef RLIMIT_LOCKS
+#ifndef RLIMIT_MEMLOCK
+#error "Houston, we have a numbering problem!"
+#endif
+    table[36].data = &c->rlimit_locks;
+#endif
+
+#ifdef RLIMIT_SIGPENDING
+#ifndef RLIMIT_LOCKS
+#error "Houston, we have a numbering problem!"
+#endif
+    table[37].data = &c->rlimit_sigpending;
+#endif
+
+#ifdef RLIMIT_MSGQUEUE
+#ifndef RLIMIT_SIGPENDING
+#error "Houston, we have a numbering problem!"
+#endif
+    table[38].data = &c->rlimit_msgqueue;
+#endif
+
+#ifdef RLIMIT_NICE
+#ifndef RLIMIT_MSGQUEUE
+#error "Houston, we have a numbering problem!"
+#endif
+    table[39].data = &c->rlimit_nice;
+#endif
+
+#ifdef RLIMIT_RTPRIO
+#ifndef RLIMIT_NICE
+#error "Houston, we have a numbering problem!"
+#endif
+    table[40].data = &c->rlimit_rtprio;
+#endif
+
+#ifdef RLIMIT_RTTIME
+#ifndef RLIMIT_RTTIME
+#error "Houston, we have a numbering problem!"
+#endif
+    table[41].data = &c->rlimit_rttime;
+#endif
+#endif
+
+    pa_xfree(c->config_file);
+    c->config_file = NULL;
+
+    f = filename ?
+        fopen(c->config_file = pa_xstrdup(filename), "r") :
+        pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
+
+    if (!f && errno != ENOENT) {
+        pa_log_warn("Failed to open configuration file: %s", pa_cstrerror(errno));
+        goto finish;
+    }
+
+    r = f ? pa_config_parse(c->config_file, f, table, NULL) : 0;
+
+finish:
+    if (f)
+        fclose(f);
+
+    return r;
+}
+
+int pa_daemon_conf_env(pa_daemon_conf *c) {
+    char *e;
+    pa_assert(c);
+
+    if ((e = getenv(ENV_DL_SEARCH_PATH))) {
+        pa_xfree(c->dl_search_path);
+        c->dl_search_path = pa_xstrdup(e);
+    }
+    if ((e = getenv(ENV_SCRIPT_FILE))) {
+        pa_xfree(c->default_script_file);
+        c->default_script_file = pa_xstrdup(e);
+    }
+
+    return 0;
+}
+
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c) {
+    pa_assert(c);
+
+    if (!c->default_script_file) {
+        if (c->system_instance)
+            c->default_script_file = pa_find_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE);
+        else
+            c->default_script_file = pa_find_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE);
+    }
+
+    return c->default_script_file;
+}
+
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) {
+    FILE *f;
+    pa_assert(c);
+
+    if (!c->default_script_file) {
+        if (c->system_instance)
+            f = pa_open_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE, &c->default_script_file);
+        else
+            f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file);
+    } else
+        f = fopen(c->default_script_file, "r");
+
+    return f;
+}
+
+
+static const char* const log_level_to_string[] = {
+    [PA_LOG_DEBUG] = "debug",
+    [PA_LOG_INFO] = "info",
+    [PA_LOG_NOTICE] = "notice",
+    [PA_LOG_WARN] = "warning",
+    [PA_LOG_ERROR] = "error"
+};
+
+char *pa_daemon_conf_dump(pa_daemon_conf *c) {
+    pa_strbuf *s;
+
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    if (c->config_file)
+        pa_strbuf_printf(s, "### Read from configuration file: %s ###\n", c->config_file);
+
+    pa_assert(c->log_level <= PA_LOG_LEVEL_MAX);
+
+    pa_strbuf_printf(s, "daemonize = %s\n", pa_yes_no(c->daemonize));
+    pa_strbuf_printf(s, "fail = %s\n", pa_yes_no(c->fail));
+    pa_strbuf_printf(s, "high-priority = %s\n", pa_yes_no(c->high_priority));
+    pa_strbuf_printf(s, "nice-level = %i\n", c->nice_level);
+    pa_strbuf_printf(s, "realtime-scheduling = %s\n", pa_yes_no(c->realtime_scheduling));
+    pa_strbuf_printf(s, "realtime-priority = %i\n", c->realtime_priority);
+    pa_strbuf_printf(s, "disallow-module-loading = %s\n", pa_yes_no(c->disallow_module_loading));
+    pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file));
+    pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
+    pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit));
+    pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm));
+    pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
+    pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
+    pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
+    pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
+    pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c)));
+    pa_strbuf_printf(s, "load-default-script-file = %s\n", pa_yes_no(c->load_default_script_file));
+    pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
+    pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
+    pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
+    pa_strbuf_printf(s, "disable-remixing = %s\n", pa_yes_no(c->disable_remixing));
+    pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format));
+    pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate);
+    pa_strbuf_printf(s, "default-sample-channels = %u\n", c->default_sample_spec.channels);
+    pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments);
+    pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec);
+#ifdef HAVE_SYS_RESOURCE_H
+    pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);
+    pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
+    pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
+    pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
+    pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
+    pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
+#ifdef RLIMIT_NPROC
+    pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
+#endif
+    pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
+#ifdef RLIMIT_MEMLOCK
+    pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
+#endif
+#ifdef RLIMIT_LOCKS
+    pa_strbuf_printf(s, "rlimit-locks = %li\n", c->rlimit_locks.is_set ? (long int) c->rlimit_locks.value : -1);
+#endif
+#ifdef RLIMIT_SIGPENDING
+    pa_strbuf_printf(s, "rlimit-sigpending = %li\n", c->rlimit_sigpending.is_set ? (long int) c->rlimit_sigpending.value : -1);
+#endif
+#ifdef RLIMIT_MSGQUEUE
+    pa_strbuf_printf(s, "rlimit-msgqueue = %li\n", c->rlimit_msgqueue.is_set ? (long int) c->rlimit_msgqueue.value : -1);
+#endif
+#ifdef RLIMIT_NICE
+    pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_nice.is_set ? (long int) c->rlimit_nice.value : -1);
+#endif
+#ifdef RLIMIT_RTPRIO
+    pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_rtprio.is_set ? (long int) c->rlimit_rtprio.value : -1);
+#endif
+#ifdef RLIMIT_RTTIME
+    pa_strbuf_printf(s, "rlimit-rttime = %li\n", c->rlimit_rttime.is_set ? (long int) c->rlimit_rttime.value : -1);
+#endif
+#endif
+
+    return pa_strbuf_tostring_free(s);
+}
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
new file mode 100644 (file)
index 0000000..be2fe1a
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef foodaemonconfhfoo
+#define foodaemonconfhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/sample.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+/* The actual command to execute */
+typedef enum pa_daemon_conf_cmd {
+    PA_CMD_DAEMON,  /* the default */
+    PA_CMD_START,
+    PA_CMD_HELP,
+    PA_CMD_VERSION,
+    PA_CMD_DUMP_CONF,
+    PA_CMD_DUMP_MODULES,
+    PA_CMD_KILL,
+    PA_CMD_CHECK,
+    PA_CMD_DUMP_RESAMPLE_METHODS,
+    PA_CMD_CLEANUP_SHM
+} pa_daemon_conf_cmd_t;
+
+#ifdef HAVE_SYS_RESOURCE_H
+typedef struct pa_rlimit {
+    rlim_t value;
+    pa_bool_t is_set;
+} pa_rlimit;
+#endif
+
+/* A structure containing configuration data for the PulseAudio server . */
+typedef struct pa_daemon_conf {
+    pa_daemon_conf_cmd_t cmd;
+    pa_bool_t daemonize,
+        fail,
+        high_priority,
+        realtime_scheduling,
+        disallow_module_loading,
+        use_pid_file,
+        system_instance,
+        no_cpu_limit,
+        disable_shm,
+        disable_remixing,
+        load_default_script_file;
+    int exit_idle_time,
+        module_idle_time,
+        scache_idle_time,
+        auto_log_target,
+        realtime_priority,
+        nice_level,
+        resample_method;
+    char *script_commands, *dl_search_path, *default_script_file;
+    pa_log_target_t log_target;
+    pa_log_level_t log_level;
+    char *config_file;
+
+#ifdef HAVE_SYS_RESOURCE_H
+    pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss, rlimit_nofile, rlimit_as;
+#ifdef RLIMIT_NPROC
+    pa_rlimit rlimit_nproc;
+#endif
+#ifdef RLIMIT_MEMLOCK
+    pa_rlimit rlimit_memlock;
+#endif
+#ifdef RLIMIT_LOCKS
+    pa_rlimit rlimit_locks;
+#endif
+#ifdef RLIMIT_SIGPENDING
+    pa_rlimit rlimit_sigpending;
+#endif
+#ifdef RLIMIT_MSGQUEUE
+    pa_rlimit rlimit_msgqueue;
+#endif
+#ifdef RLIMIT_NICE
+    pa_rlimit rlimit_nice;
+#endif
+#ifdef RLIMIT_RTPRIO
+    pa_rlimit rlimit_rtprio;
+#endif
+#ifdef RLIMIT_RTTIME
+    pa_rlimit rlimit_rttime;
+#endif
+#endif
+
+    unsigned default_n_fragments, default_fragment_size_msec;
+    pa_sample_spec default_sample_spec;
+} pa_daemon_conf;
+
+/* Allocate a new structure and fill it with sane defaults */
+pa_daemon_conf* pa_daemon_conf_new(void);
+void pa_daemon_conf_free(pa_daemon_conf*c);
+
+/* Load configuration data from the specified file overwriting the
+ * current settings in *c. If filename is NULL load the default daemon
+ * configuration file */
+int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename);
+
+/* Pretty print the current configuration data of the daemon. The
+ * returned string has to be freed manually. The output of this
+ * function may be parsed with pa_daemon_conf_load(). */
+char *pa_daemon_conf_dump(pa_daemon_conf *c);
+
+/* Load the configuration data from the process' environment
+ * overwriting the current settings in *c. */
+int pa_daemon_conf_env(pa_daemon_conf *c);
+
+/* Set these configuration variables in the structure by passing a string */
+int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
+
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
+
+#endif
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
new file mode 100644 (file)
index 0000000..dfabcfb
--- /dev/null
@@ -0,0 +1,73 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for
+## more information. Default values a commented out.  Use either ; or # for
+## commenting.
+
+; daemonize = no
+; fail = yes
+; disallow-module-loading = no
+; use-pid-file = yes
+; system-instance = no
+; disable-shm = no
+
+; high-priority = yes
+; nice-level = -11
+
+; realtime-scheduling = no
+; realtime-priority = 5
+
+; exit-idle-time = -1
+; module-idle-time = 20
+; scache-idle-time = 20
+
+; dl-search-path = (depends on architecture)
+
+; load-defaul-script-file = yes
+; default-script-file = @PA_DEFAULT_CONFIG_FILE@
+
+; log-target = auto
+; log-level = notice
+
+; resample-method = speex-float-3
+; disable-remixing = no
+
+; no-cpu-limit = no
+
+; rlimit-fsize = -1
+; rlimit-data = -1
+; rlimit-stack = -1
+; rlimit-core = -1
+; rlimit-as = -1
+; rlimit-rss = -1
+; rlimit-nproc = -1
+; rlimit-nofile = 256
+; rlimit-memlock = -1
+; rlimit-locks = -1
+; rlimit-sigpending = -1
+; rlimit-msgqueue = -1
+; rlimit-nice = 31
+; rlimit-rtprio = 9
+; rlimit-rtttime = 1000000
+
+; default-sample-format = s16le
+; default-sample-rate = 44100
+; default-sample-channels = 2
+
+; default-fragments = 4
+; default-fragment-size-msec = 25
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
new file mode 100755 (executable)
index 0000000..aad9f5d
--- /dev/null
@@ -0,0 +1,116 @@
+#!@PA_BINARY@ -nF
+#
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+.nofail
+
+### Load something into the sample cache
+#load-sample-lazy x11-bell /usr/share/sounds/gtk-events/activate.wav
+load-sample-lazy pulse-hotplug /usr/share/sounds/startup3.wav
+#load-sample-lazy pulse-coldplug /usr/share/sounds/startup3.wav
+#load-sample-lazy pulse-access /usr/share/sounds/generic.wav
+
+.fail
+
+### Load audio drivers statically (it's probably better to not load
+### these drivers manually, but instead use module-hal-detect --
+### see below -- for doing this automatically)
+#load-module module-alsa-sink
+#load-module module-alsa-source device=hw:1,0
+#load-module module-oss device="/dev/dsp" sink_name=output source_name=input
+#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
+#load-module module-null-sink
+#load-module module-pipe-sink
+
+### Automatically load driver modules depending on the hardware available
+.ifexists module-hal-detect@PA_SOEXT@
+load-module module-hal-detect
+.else
+### Alternatively use the static hardware detection module (for systems that
+### lack HAL support)
+load-module module-detect
+.endif
+
+### Load several protocols
+.ifexists module-esound-protocol-unix@PA_SOEXT@
+load-module module-esound-protocol-unix
+.endif
+load-module module-native-protocol-unix
+
+### Network access (may be configured with paprefs, so leave this commented
+### here if you plan to use paprefs)
+#load-module module-esound-protocol-tcp
+#load-module module-native-protocol-tcp
+#load-module module-zeroconf-publish
+
+### Load the RTP reciever module (also configured via paprefs, see above)
+#load-module module-rtp-recv
+
+### Load the RTP sender module (also configured via paprefs, see above)
+#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 description="RTP Multicast Sink"
+#load-module module-rtp-send source=rtp.monitor
+
+### Automatically restore the volume of playback streams
+load-module module-volume-restore
+
+### Automatically restore the default sink/source when changed by the user during runtime
+load-module module-default-device-restore
+
+### Automatically move streams to the default sink if the sink they are
+### connected to dies, similar for sources
+load-module module-rescue-streams
+
+### Make sure we always have a sink around, even if it is a null sink.
+load-module module-always-sink
+
+### Automatically suspend sinks/sources that become idle for too long
+load-module module-suspend-on-idle
+
+### Load X11 bell module
+#load-module module-x11-bell sample=bell-windowing-system
+
+### Register ourselves in the X11 session manager
+# Deactivated by default, to avoid deadlock when PA is started as esd from gnome-session
+# Instead we load this via /etc/xdg/autostart/ and "pactl load-module" now
+#load-module module-x11-xsmp
+
+### If autoexit on idle is enabled we want to make sure we only quit
+### when no local session needs us anymore.
+load-module module-console-kit
+
+### Enable positioned event sounds
+load-module module-position-event-sounds
+
+### Load additional modules from GConf settings. This can be configured with the paprefs tool.
+### Please keep in mind that the modules configured by paprefs might conflict with manually
+### loaded modules.
+.ifexists module-gconf@PA_SOEXT@
+.nofail
+load-module module-gconf
+.fail
+.endif
+
+### Publish connection data in the X11 root window
+.ifexists module-x11-publish@PA_SOEXT@
+.nofail
+load-module module-x11-publish
+.fail
+.endif
+
+### Make some devices default
+#set-default-sink output
+#set-default-source input
diff --git a/src/daemon/default.pa.win32 b/src/daemon/default.pa.win32
new file mode 100644 (file)
index 0000000..d5a1e18
--- /dev/null
@@ -0,0 +1,43 @@
+#\r
+# This file is part of PulseAudio.\r
+#\r
+# PulseAudio is free software; you can redistribute it and/or modify it\r
+# under the terms of the GNU Lesser General Public License as published by\r
+# the Free Software Foundation; either version 2 of the License, or\r
+# (at your option) any later version.\r
+#\r
+# PulseAudio is distributed in the hope that it will be useful, but\r
+# WITHOUT ANY WARRANTY; without even the implied warranty of\r
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+# General Public License for more details.\r
+#\r
+# You should have received a copy of the GNU Lesser General Public License\r
+# along with PulseAudio; if not, write to the Free Software Foundation,\r
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.\r
+\r
+\r
+# Load audio drivers statically\r
+\r
+load-module module-waveout sink_name=output source_name=input\r
+load-module module-null-sink\r
+\r
+# Load audio drivers automatically on access\r
+\r
+#add-autoload-sink output module-waveout sink_name=output source_name=input\r
+#add-autoload-source input module-waveout sink_name=output source_name=input\r
+\r
+# Load several protocols\r
+#load-module module-esound-protocol-tcp\r
+#load-module module-native-protocol-tcp\r
+#load-module module-simple-protocol-tcp\r
+#load-module module-cli-protocol-tcp\r
+\r
+# Make some devices default\r
+set-default-sink output\r
+set-default-source input\r
+\r
+.nofail\r
+\r
+# Load something to the sample cache\r
+load-sample x11-bell %WINDIR%\Media\ding.wav\r
+load-sample-dir-lazy %WINDIR%\Media\*.wav\r
diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c
new file mode 100644 (file)
index 0000000..cd6866a
--- /dev/null
@@ -0,0 +1,156 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <ltdl.h>
+
+#include <pulse/util.h>
+
+#include <pulsecore/modinfo.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "dumpmodules.h"
+
+#define PREFIX "module-"
+
+static void short_info(const char *name, PA_GCC_UNUSED const char *path, pa_modinfo *i) {
+    pa_assert(name);
+    pa_assert(i);
+
+    printf("%-40s%s\n", name, i->description ? i->description : "n/a");
+}
+
+static void long_info(const char *name, const char *path, pa_modinfo *i) {
+    static int nl = 0;
+    pa_assert(name);
+    pa_assert(i);
+
+    if (nl)
+        printf("\n");
+
+    nl = 1;
+
+    printf("Name: %s\n", name);
+
+    if (!i->description && !i->version && !i->author && !i->usage)
+        printf("No module information available\n");
+    else {
+        if (i->version)
+            printf("Version: %s\n", i->version);
+        if (i->description)
+            printf("Description: %s\n", i->description);
+        if (i->author)
+            printf("Author: %s\n", i->author);
+        if (i->usage)
+            printf("Usage: %s\n", i->usage);
+        printf("Load Once: %s\n", pa_yes_no(i->load_once));
+    }
+
+    if (path)
+        printf("Path: %s\n", path);
+}
+
+static void show_info(const char *name, const char *path, void (*info)(const char *name, const char *path, pa_modinfo*i)) {
+    pa_modinfo *i;
+
+    pa_assert(name);
+
+    if ((i = pa_modinfo_get_by_name(path ? path : name))) {
+        info(name, path, i);
+        pa_modinfo_free(i);
+    }
+}
+
+extern const lt_dlsymlist lt_preloaded_symbols[];
+
+static int is_preloaded(const char *name) {
+    const lt_dlsymlist *l;
+
+    for (l = lt_preloaded_symbols; l->name; l++) {
+        char buf[64], *e;
+
+        if (l->address)
+            continue;
+
+        pa_snprintf(buf, sizeof(buf), "%s", l->name);
+        if ((e = strrchr(buf, '.')))
+            *e = 0;
+
+        if (!strcmp(name, buf))
+            return 1;
+    }
+
+    return 0;
+}
+
+static int callback(const char *path, lt_ptr data) {
+    const char *e;
+    pa_daemon_conf *c = (data);
+
+    e = pa_path_get_filename(path);
+
+    if (strlen(e) <= sizeof(PREFIX)-1 || strncmp(e, PREFIX, sizeof(PREFIX)-1))
+        return 0;
+
+    if (is_preloaded(e))
+        return 0;
+
+    show_info(e, path, c->log_level >= PA_LOG_INFO ? long_info : short_info);
+    return 0;
+}
+
+void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) {
+    pa_assert(c);
+
+    if (argc > 0) {
+        int i;
+        for (i = 0; i < argc; i++)
+            show_info(argv[i], NULL, long_info);
+    } else {
+        const lt_dlsymlist *l;
+
+        for (l = lt_preloaded_symbols; l->name; l++) {
+            char buf[64], *e;
+
+            if (l->address)
+                continue;
+
+            if (strlen(l->name) <= sizeof(PREFIX)-1 || strncmp(l->name, PREFIX, sizeof(PREFIX)-1))
+                continue;
+
+            pa_snprintf(buf, sizeof(buf), "%s", l->name);
+            if ((e = strrchr(buf, '.')))
+                *e = 0;
+
+            show_info(buf, NULL, c->log_level >= PA_LOG_INFO ? long_info : short_info);
+        }
+
+        lt_dlforeachfile(NULL, callback, c);
+    }
+}
similarity index 68%
rename from polyp/dumpmodules.h
rename to src/daemon/dumpmodules.h
index fadc571f3ee716133a4dcfab838484464b088ea9..c49a5eda71998286a6f2b97b95bd354f5b9771e2 100644 (file)
@@ -1,23 +1,23 @@
 #ifndef foodumpmoduleshfoo
 #define foodumpmoduleshfoo
 
-/* $Id*/
-
 /***
-  This file is part of polypaudio.
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
 
-  polypaudio is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
 
-  polypaudio is distributed in the hope that it will be useful, but
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
@@ -26,6 +26,6 @@
 
 /* Dump all available modules to STDOUT. If argc > 0 print information
  * about the modules specified in argv[] instead. */
-void pa_dump_modules(struct pa_daemon_conf *c, int argc, char * const argv[]);
+void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]);
 
 #endif
similarity index 88%
rename from polyp/esdcompat.sh.in
rename to src/daemon/esdcompat.in
index 76023f52a7b2012de674370d564c72dea67b778e..665018036165827fca77434f3e74c04dafbe9c06 100755 (executable)
@@ -1,21 +1,19 @@
 #!/bin/sh
 
-# $Id$
+# This file is part of PulseAudio.
 #
-# This file is part of polypaudio.
-#
-# polypaudio is free software; you can redistribute it and/or modify
+# PulseAudio is free software; you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation; either version 2 of the License, or
 # (at your option) any later version.
 #
-# polypaudio is distributed in the hope that it will be useful, but
+# PulseAudio is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software
+# along with PulseAudio; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 # USA.
 
@@ -28,7 +26,7 @@ fail() {
 
 ARGS=" --log-target=syslog"
 
-for N in $(seq $#) ; do
+while [ "$#" -gt "0" ]; do
 
     case "$1" in
         "")
@@ -95,4 +93,4 @@ EOF
     shift
 done
 
-eval "exec '@POLYPAUDIO_BINARY@'$ARGS"
+eval "exec '@PA_BINARY@'$ARGS"
diff --git a/src/daemon/ltdl-bind-now.c b/src/daemon/ltdl-bind-now.c
new file mode 100644 (file)
index 0000000..b177067
--- /dev/null
@@ -0,0 +1,195 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#if HAVE_SYS_DL_H
+#include <sys/dl.h>
+#endif
+
+#ifndef HAVE_STRUCT_LT_USER_DLLOADER
+/* Only used with ltdl 2.2 */
+#include <string.h>
+#endif
+
+#include <ltdl.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/log.h>
+
+#include "ltdl-bind-now.h"
+
+#ifdef RTLD_NOW
+#define PA_BIND_NOW RTLD_NOW
+#elif defined(DL_NOW)
+#define PA_BIND_NOW DL_NOW
+#else
+#undef PA_BIND_NOW
+#endif
+
+static pa_mutex *libtool_mutex = NULL;
+
+PA_STATIC_TLS_DECLARE_NO_FREE(libtool_tls);
+
+static void libtool_lock(void) {
+    pa_mutex_lock(libtool_mutex);
+}
+
+static void libtool_unlock(void) {
+    pa_mutex_unlock(libtool_mutex);
+}
+
+static void libtool_set_error(const char *error) {
+    PA_STATIC_TLS_SET(libtool_tls, (char*) error);
+}
+
+static const char *libtool_get_error(void) {
+    return PA_STATIC_TLS_GET(libtool_tls);
+}
+
+#ifdef PA_BIND_NOW
+
+/*
+  To avoid lazy relocations during runtime in our RT threads we add
+  our own shared object loader with uses RTLD_NOW if it is
+  available. The standard ltdl loader prefers RTLD_LAZY.
+
+  Please note that this loader doesn't have any influence on
+  relocations on any libraries that are already loaded into our
+  process, i.e. because the pulseaudio binary links directly to
+  them. To disable lazy relocations for those libraries it is possible
+  to set $LT_BIND_NOW before starting the pulsaudio binary.
+*/
+
+#ifndef HAVE_LT_DLADVISE
+static lt_module bind_now_open(lt_user_data d, const char *fname) {
+#else
+  static lt_module bind_now_open(lt_user_data d, const char *fname, lt_dladvise advise) {
+#endif
+    lt_module m;
+
+    pa_assert(fname);
+
+    if (!(m = dlopen(fname, PA_BIND_NOW))) {
+        libtool_set_error(dlerror());
+        return NULL;
+    }
+
+    return m;
+}
+
+static int bind_now_close(lt_user_data d, lt_module m) {
+
+    pa_assert(m);
+
+    if (dlclose(m) != 0){
+        libtool_set_error(dlerror());
+        return 1;
+    }
+
+    return 0;
+}
+
+static lt_ptr bind_now_find_sym(lt_user_data d, lt_module m, const char *symbol) {
+    lt_ptr ptr;
+
+    pa_assert(m);
+    pa_assert(symbol);
+
+    if (!(ptr = dlsym(m, symbol))) {
+        libtool_set_error(dlerror());
+        return NULL;
+    }
+
+    return ptr;
+}
+
+#endif
+
+void pa_ltdl_init(void) {
+
+#ifdef PA_BIND_NOW
+# ifdef HAVE_STRUCT_LT_USER_DLLOADER
+    lt_dlloader *place;
+    static const struct lt_user_dlloader loader = {
+        .module_open = bind_now_open,
+        .module_close = bind_now_close,
+        .find_sym = bind_now_find_sym
+    };
+# else
+    static const lt_dlvtable *dlopen_loader;
+    static lt_dlvtable bindnow_loader;
+# endif
+#endif
+
+    pa_assert_se(lt_dlinit() == 0);
+    pa_assert_se(libtool_mutex = pa_mutex_new(TRUE, FALSE));
+#ifdef HAVE_LT_DLMUTEX_REGISTER
+    pa_assert_se(lt_dlmutex_register(libtool_lock, libtool_unlock, libtool_set_error, libtool_get_error) == 0);
+#endif
+
+#ifdef PA_BIND_NOW
+# ifdef HAVE_STRUCT_LT_USER_DLLOADER
+
+    if (!(place = lt_dlloader_find("dlopen")))
+        place = lt_dlloader_next(NULL);
+
+    /* Add our BIND_NOW loader as the default module loader. */
+    if (lt_dlloader_add(place, &loader, "bind-now-loader") != 0)
+        pa_log_warn("Failed to add bind-now-loader.");
+# else
+    /* Already initialised */
+    if ( dlopen_loader != NULL ) return;
+
+    if (!(dlopen_loader = lt_dlloader_find("dlopen"))) {
+      pa_log_warn("Failed to find original dlopen loader.");
+      return;
+    }
+
+    memcpy(&bindnow_loader, dlopen_loader, sizeof(bindnow_loader));
+    bindnow_loader.name = "bind-now-loader";
+    bindnow_loader.module_open = bind_now_open;
+    bindnow_loader.module_close = bind_now_close;
+    bindnow_loader.find_sym = bind_now_find_sym;
+    bindnow_loader.priority = LT_DLLOADER_PREPEND;
+
+    /* Add our BIND_NOW loader as the default module loader. */
+    if (lt_dlloader_add(&bindnow_loader) != 0)
+        pa_log_warn("Failed to add bind-now-loader.");
+# endif
+#endif
+}
+
+void pa_ltdl_done(void) {
+    pa_assert_se(lt_dlexit() == 0);
+    pa_mutex_free(libtool_mutex);
+    libtool_mutex = NULL;
+}
+
similarity index 54%
rename from polyp/sound-file.h
rename to src/daemon/ltdl-bind-now.h
index e53598535f3ab48f8c4a309ccddd45f7d62d325b..f95d13b4205f2af8a9fd2ce104c7dce3c24ee0dc 100644 (file)
@@ -1,30 +1,30 @@
-#ifndef soundfilehfoo
-#define soundfilehfoo
-
-/* $Id$ */
+#ifndef foopulsecoreltdlbindnowhfoo
+#define foopulsecoreltdlbindnowhfoo
 
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "memchunk.h"
-#include "sample.h"
 
-int pa_sound_file_load(const char *fname, struct pa_sample_spec *ss, struct pa_memchunk *chunk, struct pa_memblock_stat *s);
+void pa_ltdl_init(void);
+void pa_ltdl_done(void);
 
 #endif
+
diff --git a/src/daemon/main.c b/src/daemon/main.c
new file mode 100644 (file)
index 0000000..1459441
--- /dev/null
@@ -0,0 +1,936 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <ltdl.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <locale.h>
+#include <sys/types.h>
+
+#include <liboil/liboil.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_LIBWRAP
+#include <syslog.h>
+#include <tcpd.h>
+#endif
+
+#ifdef HAVE_DBUS
+#include <dbus/dbus.h>
+#endif
+
+#include <pulse/mainloop.h>
+#include <pulse/mainloop-signal.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/winsock.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/module.h>
+#include <pulsecore/cli-command.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sioman.h>
+#include <pulsecore/cli-text.h>
+#include <pulsecore/pid.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/random.h>
+#include <pulsecore/rtsig.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/once.h>
+#include <pulsecore/shm.h>
+
+#include "cmdline.h"
+#include "cpulimit.h"
+#include "daemon-conf.h"
+#include "dumpmodules.h"
+#include "caps.h"
+#include "ltdl-bind-now.h"
+#include "polkit.h"
+
+#define AUTOSPAWN_LOCK "autospawn.lock"
+
+#ifdef HAVE_LIBWRAP
+/* Only one instance of these variables */
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif
+
+#ifdef HAVE_OSS
+/* padsp looks for this symbol in the running process and disables
+ * itself if it finds it and it is set to 7 (which is actually a bit
+ * mask). For details see padsp. */
+int __padsp_disabled__ = 7;
+#endif
+
+#ifdef OS_IS_WIN32
+
+static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+    MSG msg;
+    struct timeval tvnext;
+
+    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+        if (msg.message == WM_QUIT)
+            raise(SIGTERM);
+        else {
+            TranslateMessage(&msg);
+            DispatchMessage(&msg);
+        }
+    }
+
+    pa_timeval_add(pa_gettimeofday(&tvnext), 100000);
+    a->time_restart(e, &tvnext);
+}
+
+#endif
+
+static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, int sig, void *userdata) {
+    pa_log_info("Got signal %s.", pa_sig2str(sig));
+
+    switch (sig) {
+#ifdef SIGUSR1
+        case SIGUSR1:
+            pa_module_load(userdata, "module-cli", NULL);
+            break;
+#endif
+
+#ifdef SIGUSR2
+        case SIGUSR2:
+            pa_module_load(userdata, "module-cli-protocol-unix", NULL);
+            break;
+#endif
+
+#ifdef SIGHUP
+        case SIGHUP: {
+            char *c = pa_full_status_string(userdata);
+            pa_log_notice("%s", c);
+            pa_xfree(c);
+            return;
+        }
+#endif
+
+        case SIGINT:
+        case SIGTERM:
+        default:
+            pa_log_info("Exiting.");
+            m->quit(m, 1);
+            break;
+    }
+}
+
+#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
+
+static int change_user(void) {
+    struct passwd *pw;
+    struct group * gr;
+    int r;
+
+    /* This function is called only in system-wide mode. It creates a
+     * runtime dir in /var/run/ with proper UID/GID and drops privs
+     * afterwards. */
+
+    if (!(pw = getpwnam(PA_SYSTEM_USER))) {
+        pa_log("Failed to find user '%s'.", PA_SYSTEM_USER);
+        return -1;
+    }
+
+    if (!(gr = getgrnam(PA_SYSTEM_GROUP))) {
+        pa_log("Failed to find group '%s'.", PA_SYSTEM_GROUP);
+        return -1;
+    }
+
+    pa_log_info("Found user '%s' (UID %lu) and group '%s' (GID %lu).",
+                PA_SYSTEM_USER, (unsigned long) pw->pw_uid,
+                PA_SYSTEM_GROUP, (unsigned long) gr->gr_gid);
+
+    if (pw->pw_gid != gr->gr_gid) {
+        pa_log("GID of user '%s' and of group '%s' don't match.", PA_SYSTEM_USER, PA_SYSTEM_GROUP);
+        return -1;
+    }
+
+    if (strcmp(pw->pw_dir, PA_SYSTEM_RUNTIME_PATH) != 0)
+        pa_log_warn("Warning: home directory of user '%s' is not '%s', ignoring.", PA_SYSTEM_USER, PA_SYSTEM_RUNTIME_PATH);
+
+    if (pa_make_secure_dir(PA_SYSTEM_RUNTIME_PATH, 0755, pw->pw_uid, gr->gr_gid) < 0) {
+        pa_log("Failed to create '%s': %s", PA_SYSTEM_RUNTIME_PATH, pa_cstrerror(errno));
+        return -1;
+    }
+
+    if (pa_make_secure_dir(PA_SYSTEM_STATE_PATH, 0700, pw->pw_uid, gr->gr_gid) < 0) {
+        pa_log("Failed to create '%s': %s", PA_SYSTEM_STATE_PATH, pa_cstrerror(errno));
+        return -1;
+    }
+
+    /* We don't create the config dir here, because we don't need to write to it */
+
+    if (initgroups(PA_SYSTEM_USER, gr->gr_gid) != 0) {
+        pa_log("Failed to change group list: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+#if defined(HAVE_SETRESGID)
+    r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid);
+#elif defined(HAVE_SETEGID)
+    if ((r = setgid(gr->gr_gid)) >= 0)
+        r = setegid(gr->gr_gid);
+#elif defined(HAVE_SETREGID)
+    r = setregid(gr->gr_gid, gr->gr_gid);
+#else
+#error "No API to drop priviliges"
+#endif
+
+    if (r < 0) {
+        pa_log("Failed to change GID: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+#if defined(HAVE_SETRESUID)
+    r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid);
+#elif defined(HAVE_SETEUID)
+    if ((r = setuid(pw->pw_uid)) >= 0)
+        r = seteuid(pw->pw_uid);
+#elif defined(HAVE_SETREUID)
+    r = setreuid(pw->pw_uid, pw->pw_uid);
+#else
+#error "No API to drop priviliges"
+#endif
+
+    if (r < 0) {
+        pa_log("Failed to change UID: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_set_env("USER", PA_SYSTEM_USER);
+    pa_set_env("USERNAME", PA_SYSTEM_USER);
+    pa_set_env("LOGNAME", PA_SYSTEM_USER);
+    pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
+
+    /* Relevant for pa_runtime_path() */
+    pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
+    pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_CONFIG_PATH);
+    pa_set_env("PULSE_STATE_PATH", PA_SYSTEM_STATE_PATH);
+
+    pa_log_info("Successfully dropped root privileges.");
+
+    return 0;
+}
+
+#else /* HAVE_PWD_H && HAVE_GRP_H */
+
+static int change_user(void) {
+    pa_log("System wide mode unsupported on this platform.");
+    return -1;
+}
+
+#endif /* HAVE_PWD_H && HAVE_GRP_H */
+
+#ifdef HAVE_SYS_RESOURCE_H
+
+static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
+    struct rlimit rl;
+    pa_assert(r);
+
+    if (!r->is_set)
+        return 0;
+
+    rl.rlim_cur = rl.rlim_max = r->value;
+
+    if (setrlimit(resource, &rl) < 0) {
+        pa_log_info("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+static void set_all_rlimits(const pa_daemon_conf *conf) {
+    set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE");
+    set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
+    set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK");
+    set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
+    set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS");
+#ifdef RLIMIT_NPROC
+    set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
+#endif
+    set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
+#ifdef RLIMIT_MEMLOCK
+    set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
+#endif
+    set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
+#ifdef RLIMIT_LOCKS
+    set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS");
+#endif
+#ifdef RLIMIT_SIGPENDING
+    set_one_rlimit(&conf->rlimit_sigpending, RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING");
+#endif
+#ifdef RLIMIT_MSGQUEUE
+    set_one_rlimit(&conf->rlimit_msgqueue, RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE");
+#endif
+#ifdef RLIMIT_NICE
+    set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE");
+#endif
+#ifdef RLIMIT_RTPRIO
+    set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO");
+#endif
+#ifdef RLIMIT_RTTIME
+    set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME");
+#endif
+}
+#endif
+
+int main(int argc, char *argv[]) {
+    pa_core *c = NULL;
+    pa_strbuf *buf = NULL;
+    pa_daemon_conf *conf = NULL;
+    pa_mainloop *mainloop = NULL;
+    char *s;
+    int r = 0, retval = 1, d = 0;
+    pa_bool_t suid_root, real_root;
+    pa_bool_t valid_pid_file = FALSE;
+    gid_t gid = (gid_t) -1;
+    pa_bool_t ltdl_init = FALSE;
+    int passed_fd = -1;
+    const char *e;
+#ifdef HAVE_FORK
+    int daemon_pipe[2] = { -1, -1 };
+#endif
+#ifdef OS_IS_WIN32
+    pa_time_event *win32_timer;
+    struct timeval win32_tv;
+#endif
+    char *lf = NULL;
+    int autospawn_lock_fd = -1;
+
+#if defined(__linux__) && defined(__OPTIMIZE__)
+    /*
+       Disable lazy relocations to make usage of external libraries
+       more deterministic for our RT threads. We abuse __OPTIMIZE__ as
+       a check whether we are a debug build or not.
+    */
+
+    if (!getenv("LD_BIND_NOW")) {
+        char *rp;
+
+        /* We have to execute ourselves, because the libc caches the
+         * value of $LD_BIND_NOW on initialization. */
+
+        pa_set_env("LD_BIND_NOW", "1");
+        pa_assert_se(rp = pa_readlink("/proc/self/exe"));
+        pa_assert_se(execv(rp, argv) == 0);
+    }
+#endif
+
+#ifdef HAVE_GETUID
+    real_root = getuid() == 0;
+    suid_root = !real_root && geteuid() == 0;
+#else
+    real_root = FALSE;
+    suid_root = FALSE;
+#endif
+
+    if (!real_root) {
+        /* Drop all capabilities except CAP_SYS_NICE  */
+        pa_limit_caps();
+
+        /* Drop priviliges, but keep CAP_SYS_NICE */
+        pa_drop_root();
+
+        /* After dropping root, the effective set is reset, hence,
+         * let's raise it again */
+        pa_limit_caps();
+
+        /* When capabilities are not supported we will not be able to
+         * aquire RT sched anymore. But yes, that's the way it is. It
+         * is just too risky tun let PA run as root all the time. */
+    }
+
+    if ((e = getenv("PULSE_PASSED_FD"))) {
+        passed_fd = atoi(e);
+
+        if (passed_fd <= 2)
+            passed_fd = -1;
+    }
+
+    pa_close_all(passed_fd, -1);
+
+    pa_reset_sigs(-1);
+    pa_unblock_sigs(-1);
+
+    /* At this point, we are a normal user, possibly with CAP_NICE if
+     * we were started SUID. If we are started as normal root, than we
+     * still are normal root. */
+
+    setlocale(LC_ALL, "");
+    pa_log_set_maximal_level(PA_LOG_INFO);
+    pa_log_set_ident("pulseaudio");
+
+    conf = pa_daemon_conf_new();
+
+    if (pa_daemon_conf_load(conf, NULL) < 0)
+        goto finish;
+
+    if (pa_daemon_conf_env(conf) < 0)
+        goto finish;
+
+    if (pa_cmdline_parse(conf, argc, argv, &d) < 0) {
+        pa_log("Failed to parse command line.");
+        goto finish;
+    }
+
+    pa_log_set_maximal_level(conf->log_level);
+    pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
+
+    pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root));
+
+    if (!real_root && pa_have_caps()) {
+        pa_bool_t allow_high_priority = FALSE, allow_realtime = FALSE;
+
+        /* Let's better not enable high prio or RT by default */
+
+        if (conf->high_priority && !allow_high_priority) {
+            if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
+                pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing high-priority scheduling.");
+                allow_high_priority = TRUE;
+            }
+        }
+
+        if (conf->realtime_scheduling && !allow_realtime) {
+            if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
+                pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time scheduling.");
+                allow_realtime = TRUE;
+            }
+        }
+
+#ifdef HAVE_POLKIT
+        if (conf->high_priority && !allow_high_priority) {
+            if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) {
+                pa_log_info("PolicyKit grants us acquire-high-priority privilege.");
+                allow_high_priority = TRUE;
+            } else
+                pa_log_info("PolicyKit refuses acquire-high-priority privilege.");
+        }
+
+        if (conf->realtime_scheduling && !allow_realtime) {
+            if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) {
+                pa_log_info("PolicyKit grants us acquire-real-time privilege.");
+                allow_realtime = TRUE;
+            } else
+                pa_log_info("PolicyKit refuses acquire-real-time privilege.");
+        }
+#endif
+
+        if (!allow_high_priority && !allow_realtime) {
+
+            /* OK, there's no further need to keep CAP_NICE. Hence
+             * let's give it up early */
+
+            pa_drop_caps();
+
+            if (conf->high_priority || conf->realtime_scheduling)
+                pa_log_notice("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n"
+                              "We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
+                              "For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user.");
+        }
+    }
+
+#ifdef HAVE_SYS_RESOURCE_H
+    /* Reset resource limits. If we are run as root (for system mode)
+     * this might end up increasing the limits, which is intended
+     * behaviour. For all other cases, i.e. started as normal user, or
+     * SUID root at this point we should have no CAP_SYS_RESOURCE and
+     * increasing the limits thus should fail. Which is, too, intended
+     * behaviour */
+
+    set_all_rlimits(conf);
+#endif
+
+    if (conf->high_priority && !pa_can_high_priority())
+        pa_log_warn("High-priority scheduling enabled in configuration but not allowed by policy.");
+
+    if (conf->high_priority && (conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START))
+        pa_raise_priority(conf->nice_level);
+
+    if (pa_have_caps()) {
+        pa_bool_t drop;
+
+        drop = (conf->cmd != PA_CMD_DAEMON && conf->cmd != PA_CMD_START) || !conf->realtime_scheduling;
+
+#ifdef RLIMIT_RTPRIO
+        if (!drop) {
+            struct rlimit rl;
+            /* At this point we still have CAP_NICE if we were loaded
+             * SUID root. If possible let's acquire RLIMIT_RTPRIO
+             * instead and give CAP_NICE up. */
+
+            if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
+
+                if (rl.rlim_cur >= 9)
+                    drop = TRUE;
+                else {
+                    rl.rlim_max = rl.rlim_cur = 9;
+
+                    if (setrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
+                        pa_log_info("Successfully increased RLIMIT_RTPRIO");
+                        drop = TRUE;
+                    } else
+                        pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
+                }
+            }
+        }
+#endif
+
+        if (drop)  {
+            pa_log_info("Giving up CAP_NICE");
+            pa_drop_caps();
+            suid_root = FALSE;
+        }
+    }
+
+    if (conf->realtime_scheduling && !pa_can_realtime())
+        pa_log_warn("Real-time scheduling enabled in configuration but not allowed by policy.");
+
+    pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
+
+    LTDL_SET_PRELOADED_SYMBOLS();
+    pa_ltdl_init();
+    ltdl_init = TRUE;
+
+    if (conf->dl_search_path)
+        lt_dlsetsearchpath(conf->dl_search_path);
+
+#ifdef OS_IS_WIN32
+    {
+        WSADATA data;
+        WSAStartup(MAKEWORD(2, 0), &data);
+    }
+#endif
+
+    pa_random_seed();
+
+    switch (conf->cmd) {
+        case PA_CMD_DUMP_MODULES:
+            pa_dump_modules(conf, argc-d, argv+d);
+            retval = 0;
+            goto finish;
+
+        case PA_CMD_DUMP_CONF: {
+            s = pa_daemon_conf_dump(conf);
+            fputs(s, stdout);
+            pa_xfree(s);
+            retval = 0;
+            goto finish;
+        }
+
+        case PA_CMD_DUMP_RESAMPLE_METHODS: {
+            int i;
+
+            for (i = 0; i < PA_RESAMPLER_MAX; i++)
+                if (pa_resample_method_supported(i))
+                    printf("%s\n", pa_resample_method_to_string(i));
+
+            goto finish;
+        }
+
+        case PA_CMD_HELP :
+            pa_cmdline_help(argv[0]);
+            retval = 0;
+            goto finish;
+
+        case PA_CMD_VERSION :
+            printf(PACKAGE_NAME" "PACKAGE_VERSION"\n");
+            retval = 0;
+            goto finish;
+
+        case PA_CMD_CHECK: {
+            pid_t pid;
+
+            if (pa_pid_file_check_running(&pid, "pulseaudio") < 0)
+                pa_log_info("Daemon not running");
+            else {
+                pa_log_info("Daemon running as PID %u", pid);
+                retval = 0;
+            }
+
+            goto finish;
+
+        }
+        case PA_CMD_KILL:
+
+            if (pa_pid_file_kill(SIGINT, NULL, "pulseaudio") < 0)
+                pa_log("Failed to kill daemon.");
+            else
+                retval = 0;
+
+            goto finish;
+
+        case PA_CMD_CLEANUP_SHM:
+
+            if (pa_shm_cleanup() >= 0)
+                retval = 0;
+
+            goto finish;
+
+        default:
+            pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START);
+    }
+
+    if (real_root && !conf->system_instance)
+        pa_log_warn("This program is not intended to be run as root (unless --system is specified).");
+    else if (!real_root && conf->system_instance) {
+        pa_log("Root priviliges required.");
+        goto finish;
+    }
+
+    if (conf->cmd == PA_CMD_START) {
+        /* If we shall start PA only when it is not running yet, we
+         * first take the autospawn lock to make things
+         * synchronous. */
+
+        lf = pa_runtime_path(AUTOSPAWN_LOCK);
+        autospawn_lock_fd = pa_lock_lockfile(lf);
+    }
+
+    if (conf->daemonize) {
+        pid_t child;
+        int tty_fd;
+
+        if (pa_stdio_acquire() < 0) {
+            pa_log("Failed to acquire stdio.");
+            goto finish;
+        }
+
+#ifdef HAVE_FORK
+        if (pipe(daemon_pipe) < 0) {
+            pa_log("pipe failed: %s", pa_cstrerror(errno));
+            goto finish;
+        }
+
+        if ((child = fork()) < 0) {
+            pa_log("fork() failed: %s", pa_cstrerror(errno));
+            goto finish;
+        }
+
+        if (child != 0) {
+            ssize_t n;
+            /* Father */
+
+            pa_assert_se(pa_close(daemon_pipe[1]) == 0);
+            daemon_pipe[1] = -1;
+
+            if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
+
+                if (n < 0)
+                    pa_log("read() failed: %s", pa_cstrerror(errno));
+
+                retval = 1;
+            }
+
+            if (retval)
+                pa_log("Daemon startup failed.");
+            else
+                pa_log_info("Daemon startup successful.");
+
+            goto finish;
+        }
+
+        if (autospawn_lock_fd >= 0) {
+            /* The lock file is unlocked from the parent, so we need
+             * to close it in the child */
+
+            pa_close(autospawn_lock_fd);
+            autospawn_lock_fd = -1;
+        }
+
+        pa_assert_se(pa_close(daemon_pipe[0]) == 0);
+        daemon_pipe[0] = -1;
+#endif
+
+        if (conf->auto_log_target)
+            pa_log_set_target(PA_LOG_SYSLOG, NULL);
+
+#ifdef HAVE_SETSID
+        setsid();
+#endif
+#ifdef HAVE_SETPGID
+        setpgid(0,0);
+#endif
+
+#ifndef OS_IS_WIN32
+        pa_close(0);
+        pa_close(1);
+        pa_close(2);
+
+        pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+        pa_assert_se(open("/dev/null", O_WRONLY) == 1);
+        pa_assert_se(open("/dev/null", O_WRONLY) == 2);
+#else
+        FreeConsole();
+#endif
+
+#ifdef SIGTTOU
+        signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+        signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+        signal(SIGTSTP, SIG_IGN);
+#endif
+
+#ifdef TIOCNOTTY
+        if ((tty_fd = open("/dev/tty", O_RDWR)) >= 0) {
+            ioctl(tty_fd, TIOCNOTTY, (char*) 0);
+            pa_assert_se(pa_close(tty_fd) == 0);
+        }
+#endif
+    }
+
+    pa_set_env("PULSE_INTERNAL", "1");
+    pa_assert_se(chdir("/") == 0);
+    umask(0022);
+
+    if (conf->system_instance)
+        if (change_user() < 0)
+            goto finish;
+
+    pa_set_env("PULSE_SYSTEM", conf->system_instance ? "1" : "0");
+
+    pa_log_info("This is PulseAudio " PACKAGE_VERSION);
+    pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE);
+    if (!(s = pa_get_runtime_dir()))
+        goto finish;
+    pa_log_info("Using runtime directory %s.", s);
+    pa_xfree(s);
+    if (!(s = pa_get_state_dir()))
+        pa_log_info("Using state directory %s.", s);
+    pa_xfree(s);
+
+    pa_log_info("Running in system mode: %s", pa_yes_no(pa_in_system_mode()));
+
+    if (conf->use_pid_file) {
+        int z;
+
+        if ((z = pa_pid_file_create("pulseaudio")) != 0) {
+
+            if (conf->cmd == PA_CMD_START && z > 0) {
+                /* If we are already running and with are run in
+                 * --start mode, then let's return this as success. */
+
+                pa_log_info("z=%i rock!", z);
+
+                retval = 0;
+                goto finish;
+            }
+
+            pa_log("pa_pid_file_create() failed.");
+            goto finish;
+        }
+
+        valid_pid_file = TRUE;
+    }
+
+#ifdef SIGPIPE
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    if (pa_rtclock_hrtimer())
+        pa_log_info("Fresh high-resolution timers available! Bon appetit!");
+    else
+        pa_log_info("Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!");
+
+#ifdef SIGRTMIN
+    /* Valgrind uses SIGRTMAX. To easy debugging we don't use it here */
+    pa_rtsig_configure(SIGRTMIN, SIGRTMAX-1);
+#endif
+
+    pa_assert_se(mainloop = pa_mainloop_new());
+
+    if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm))) {
+        pa_log("pa_core_new() failed.");
+        goto finish;
+    }
+
+    c->default_sample_spec = conf->default_sample_spec;
+    c->default_n_fragments = conf->default_n_fragments;
+    c->default_fragment_size_msec = conf->default_fragment_size_msec;
+    c->exit_idle_time = conf->exit_idle_time;
+    c->module_idle_time = conf->module_idle_time;
+    c->scache_idle_time = conf->scache_idle_time;
+    c->resample_method = conf->resample_method;
+    c->realtime_priority = conf->realtime_priority;
+    c->realtime_scheduling = !!conf->realtime_scheduling;
+    c->disable_remixing = !!conf->disable_remixing;
+    c->running_as_daemon = !!conf->daemonize;
+
+    pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
+    pa_signal_new(SIGINT, signal_callback, c);
+    pa_signal_new(SIGTERM, signal_callback, c);
+#ifdef SIGUSR1
+    pa_signal_new(SIGUSR1, signal_callback, c);
+#endif
+#ifdef SIGUSR2
+    pa_signal_new(SIGUSR2, signal_callback, c);
+#endif
+#ifdef SIGHUP
+    pa_signal_new(SIGHUP, signal_callback, c);
+#endif
+
+#ifdef OS_IS_WIN32
+    win32_timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL);
+#endif
+
+    oil_init();
+
+    if (!conf->no_cpu_limit)
+        pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
+
+    buf = pa_strbuf_new();
+    if (conf->load_default_script_file) {
+        FILE *f;
+
+        if ((f = pa_daemon_conf_open_default_script_file(conf))) {
+            r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
+            fclose(f);
+        }
+    }
+
+    if (r >= 0)
+        r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
+
+    pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
+    pa_xfree(s);
+
+    /* We completed the initial module loading, so let's disable it
+     * from now on, if requested */
+    c->disallow_module_loading = !!conf->disallow_module_loading;
+
+    if (r < 0 && conf->fail) {
+        pa_log("Failed to initialize daemon.");
+        goto finish;
+    }
+
+    if (!c->modules || pa_idxset_size(c->modules) == 0) {
+        pa_log("Daemon startup without any loaded modules, refusing to work.");
+        goto finish;
+    }
+
+    if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, TRUE) && conf->fail) {
+        pa_log_error("Default sink name (%s) does not exist in name register.", c->default_sink_name);
+        goto finish;
+    }
+
+#ifdef HAVE_FORK
+    if (daemon_pipe[1] >= 0) {
+        int ok = 0;
+        pa_loop_write(daemon_pipe[1], &ok, sizeof(ok), NULL);
+        pa_close(daemon_pipe[1]);
+        daemon_pipe[1] = -1;
+    }
+#endif
+
+    pa_log_info("Daemon startup complete.");
+
+    retval = 0;
+    if (pa_mainloop_run(mainloop, &retval) < 0)
+        goto finish;
+
+    pa_log_info("Daemon shutdown initiated.");
+
+finish:
+
+    if (autospawn_lock_fd >= 0)
+        pa_unlock_lockfile(lf, autospawn_lock_fd);
+
+    if (lf)
+        pa_xfree(lf);
+
+#ifdef OS_IS_WIN32
+    if (win32_timer)
+        pa_mainloop_get_api(mainloop)->time_free(win32_timer);
+#endif
+
+    if (c) {
+        pa_core_unref(c);
+        pa_log_info("Daemon terminated.");
+    }
+
+    if (!conf->no_cpu_limit)
+        pa_cpu_limit_done();
+
+    pa_signal_done();
+
+#ifdef HAVE_FORK
+    if (daemon_pipe[1] >= 0)
+        pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
+
+    pa_close_pipe(daemon_pipe);
+#endif
+
+    if (mainloop)
+        pa_mainloop_free(mainloop);
+
+    if (conf)
+        pa_daemon_conf_free(conf);
+
+    if (valid_pid_file)
+        pa_pid_file_remove();
+
+#ifdef OS_IS_WIN32
+    WSACleanup();
+#endif
+
+    if (ltdl_init)
+        pa_ltdl_done();
+
+#ifdef HAVE_DBUS
+    dbus_shutdown();
+#endif
+
+    return retval;
+}
diff --git a/src/daemon/org.pulseaudio.policy b/src/daemon/org.pulseaudio.policy
new file mode 100644 (file)
index 0000000..6cdeec6
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?><!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<!--
+This file is part of PulseAudio.
+
+PulseAudio is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+
+<policyconfig>
+  <vendor>The PulseAudio Project</vendor>
+  <vendor_url>http://pulseaudio.org/</vendor_url>
+  <icon_name>audio-card</icon_name>
+
+  <action id="org.pulseaudio.acquire-real-time">
+    <description>Real-time scheduling for the PulseAudio daemon</description>
+    <message>System policy prevents PulseAudio from acquiring real-time scheduling.</message>
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>no</allow_active>
+    </defaults>
+  </action>
+
+  <action id="org.pulseaudio.acquire-high-priority">
+    <description>High-priority scheduling (negative Unix nice level) for the PulseAudio daemon</description>
+    <message>System policy prevents PulseAudio from acquiring high-priority scheduling.</message>
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>no</allow_active>
+    </defaults>
+  </action>
+
+</policyconfig>
diff --git a/src/daemon/polkit.c b/src/daemon/polkit.c
new file mode 100644 (file)
index 0000000..256e319
--- /dev/null
@@ -0,0 +1,165 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <inttypes.h>
+
+#include <dbus/dbus.h>
+#include <polkit-dbus/polkit-dbus.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "polkit.h"
+
+int pa_polkit_check(const char *action_id) {
+    int ret = -1;
+    DBusError dbus_error;
+    DBusConnection *bus = NULL;
+    PolKitCaller *caller = NULL;
+    PolKitAction *action = NULL;
+    PolKitContext *context = NULL;
+    PolKitError *polkit_error = NULL;
+    PolKitSession *session = NULL;
+    PolKitResult polkit_result;
+
+    dbus_error_init(&dbus_error);
+
+    if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error))) {
+        pa_log_error("Cannot connect to system bus: %s", dbus_error.message);
+        goto finish;
+    }
+
+    if (!(caller = polkit_caller_new_from_pid(bus, getpid(), &dbus_error))) {
+        pa_log_error("Cannot get caller from PID: %s", dbus_error.message);
+        goto finish;
+    }
+
+    /* This function is called when PulseAudio is called SUID root. We
+     * want to authenticate the real user that called us and not the
+     * effective user we gained through being SUID root. Hence we
+     * overwrite the UID caller data here explicitly, just for
+     * paranoia. In fact PolicyKit should fill in the UID here anyway
+     * -- an not the EUID or any other user id. */
+
+    if (!(polkit_caller_set_uid(caller, getuid()))) {
+        pa_log_error("Cannot set UID on caller object.");
+        goto finish;
+    }
+
+    if (!(polkit_caller_get_ck_session(caller, &session))) {
+        pa_log_error("Failed to get CK session.");
+        goto finish;
+    }
+
+    /* We need to overwrite the UID in both the caller and the session
+     * object */
+
+    if (!(polkit_session_set_uid(session, getuid()))) {
+        pa_log_error("Cannot set UID on session object.");
+        goto finish;
+    }
+
+    if (!(action = polkit_action_new())) {
+        pa_log_error("Cannot allocate PolKitAction.");
+        goto finish;
+    }
+
+    if (!polkit_action_set_action_id(action, action_id)) {
+        pa_log_error("Cannot set action_id");
+        goto finish;
+    }
+
+    if (!(context = polkit_context_new())) {
+        pa_log_error("Cannot allocate PolKitContext.");
+        goto finish;
+    }
+
+    if (!polkit_context_init(context, &polkit_error)) {
+        pa_log_error("Cannot initialize PolKitContext: %s", polkit_error_get_error_message(polkit_error));
+        goto finish;
+    }
+
+    for (;;) {
+
+        polkit_result = polkit_context_is_caller_authorized(context, action, caller, TRUE, &polkit_error);
+
+        if (polkit_error_is_set(polkit_error)) {
+            pa_log_error("Could not determine whether caller is authorized: %s", polkit_error_get_error_message(polkit_error));
+            goto finish;
+        }
+
+        if (polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH ||
+            polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION ||
+            polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS ||
+            polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_ONE_SHOT ||
+            polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH ||
+            polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_SESSION ||
+            polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_ALWAYS ||
+            polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_ONE_SHOT
+        ) {
+
+            if (polkit_auth_obtain(action_id, 0, getpid(), &dbus_error)) {
+                polkit_result = POLKIT_RESULT_YES;
+                break;
+            }
+
+            if (dbus_error_is_set(&dbus_error)) {
+                pa_log_error("Cannot obtain auth: %s", dbus_error.message);
+                goto finish;
+            }
+        }
+
+        break;
+    }
+
+    if (polkit_result != POLKIT_RESULT_YES && polkit_result != POLKIT_RESULT_NO)
+        pa_log_warn("PolicyKit responded with '%s'", polkit_result_to_string_representation(polkit_result));
+
+    ret = polkit_result == POLKIT_RESULT_YES;
+
+finish:
+
+    if (caller)
+        polkit_caller_unref(caller);
+
+    if (action)
+        polkit_action_unref(action);
+
+    if (context)
+        polkit_context_unref(context);
+
+    if (bus)
+        dbus_connection_unref(bus);
+
+    dbus_error_free(&dbus_error);
+
+    if (polkit_error)
+        polkit_error_free(polkit_error);
+
+    return ret;
+}
diff --git a/src/daemon/polkit.h b/src/daemon/polkit.h
new file mode 100644 (file)
index 0000000..0d65ec5
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef foopolkithfoo
+#define foopolkithfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+int pa_polkit_check(const char *action);
+
+#endif
diff --git a/src/daemon/pulseaudio-module-xsmp.desktop b/src/daemon/pulseaudio-module-xsmp.desktop
new file mode 100644 (file)
index 0000000..fa719a7
--- /dev/null
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Version=1.0
+Encoding=UTF-8
+Name=PulseAudio Session Management
+Comment=Load module-x11-xsmp into PulseAudio
+Exec=pactl load-module module-x11-xsmp
+Terminal=false
+Type=Application
+Categories=
+GenericName=
similarity index 89%
rename from polyp/depmod.py
rename to src/depmod.py
index 7bb223b198eceffacc2d71c0039036f0f64f8e6a..6cb3cb2169562b38bc5507715f7f8a7bc5849826 100755 (executable)
@@ -1,20 +1,19 @@
 #!/usr/bin/python
-# $Id$
-#
-# This file is part of polypaudio.
+
+# This file is part of PulseAudio.
 #
-# polypaudio is free software; you can redistribute it and/or modify
+# PulseAudio is free software; you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation; either version 2 of the License, or
 # (at your option) any later version.
 #
-# polypaudio is distributed in the hope that it will be useful, but
+# PulseAudio is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License
-# along with polypaudio; if not, write to the Free Software
+# along with PulseAudio; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 # USA.
 
diff --git a/src/map-file b/src/map-file
new file mode 100644 (file)
index 0000000..8d1c582
--- /dev/null
@@ -0,0 +1,256 @@
+PULSE_0 {
+global:
+pa_browser_new;
+pa_browser_new_full;
+pa_browser_ref;
+pa_browser_set_callback;
+pa_browser_set_error_callback;
+pa_browser_unref;
+pa_bytes_per_second;
+pa_bytes_snprint;
+pa_bytes_to_usec;
+pa_channel_map_equal;
+pa_channel_map_init;
+pa_channel_map_init_auto;
+pa_channel_map_init_extend;
+pa_channel_map_init_mono;
+pa_channel_map_init_stereo;
+pa_channel_map_parse;
+pa_channel_map_snprint;
+pa_channel_map_valid;
+pa_channel_position_to_pretty_string;
+pa_channel_position_to_string;
+pa_context_add_autoload;
+pa_context_connect;
+pa_context_disconnect;
+pa_context_drain;
+pa_context_errno;
+pa_context_exit_daemon;
+pa_context_get_autoload_info_by_index;
+pa_context_get_autoload_info_by_name;
+pa_context_get_autoload_info_list;
+pa_context_get_client_info;
+pa_context_get_client_info_list;
+pa_context_get_index;
+pa_context_get_module_info;
+pa_context_get_module_info_list;
+pa_context_get_protocol_version;
+pa_context_get_sample_info_by_index;
+pa_context_get_sample_info_by_name;
+pa_context_get_sample_info_list;
+pa_context_get_server;
+pa_context_get_server_info;
+pa_context_get_server_protocol_version;
+pa_context_get_sink_info_by_index;
+pa_context_get_sink_info_by_name;
+pa_context_get_sink_info_list;
+pa_context_get_sink_input_info;
+pa_context_get_sink_input_info_list;
+pa_context_get_source_info_by_index;
+pa_context_get_source_info_by_name;
+pa_context_get_source_info_list;
+pa_context_get_source_output_info;
+pa_context_get_source_output_info_list;
+pa_context_get_state;
+pa_context_is_local;
+pa_context_is_pending;
+pa_context_kill_client;
+pa_context_kill_sink_input;
+pa_context_kill_source_output;
+pa_context_load_module;
+pa_context_move_sink_input_by_index;
+pa_context_move_sink_input_by_name;
+pa_context_move_source_output_by_index;
+pa_context_move_source_output_by_name;
+pa_context_new;
+pa_context_new_with_proplist;
+pa_context_play_sample;
+pa_context_play_sample_with_proplist;
+pa_context_proplist_remove;
+pa_context_proplist_update;
+pa_context_ref;
+pa_context_remove_autoload_by_index;
+pa_context_remove_autoload_by_name;
+pa_context_remove_sample;
+pa_context_set_default_sink;
+pa_context_set_default_source;
+pa_context_set_name;
+pa_context_set_sink_input_mute;
+pa_context_set_sink_input_volume;
+pa_context_set_sink_mute_by_index;
+pa_context_set_sink_mute_by_name;
+pa_context_set_sink_volume_by_index;
+pa_context_set_sink_volume_by_name;
+pa_context_set_source_mute_by_index;
+pa_context_set_source_mute_by_name;
+pa_context_set_source_volume_by_index;
+pa_context_set_source_volume_by_name;
+pa_context_set_state_callback;
+pa_context_set_subscribe_callback;
+pa_context_stat;
+pa_context_subscribe;
+pa_context_suspend_sink_by_index;
+pa_context_suspend_sink_by_name;
+pa_context_suspend_source_by_index;
+pa_context_suspend_source_by_name;
+pa_context_unload_module;
+pa_context_unref;
+pa_cvolume_avg;
+pa_cvolume_channels_equal_to;
+pa_cvolume_equal;
+pa_cvolume_set;
+pa_cvolume_snprint;
+pa_cvolume_valid;
+pa_frame_size;
+pa_get_binary_name;
+pa_get_fqdn;
+pa_get_home_dir;
+pa_get_host_name;
+pa_get_library_version;
+pa_gettimeofday;
+pa_get_user_name;
+pa_glib_mainloop_free;
+pa_glib_mainloop_get_api;
+pa_glib_mainloop_new;
+pa_locale_to_utf8;
+pa_mainloop_api_once;
+pa_mainloop_dispatch;
+pa_mainloop_free;
+pa_mainloop_get_api;
+pa_mainloop_get_retval;
+pa_mainloop_iterate;
+pa_mainloop_new;
+pa_mainloop_poll;
+pa_mainloop_prepare;
+pa_mainloop_quit;
+pa_mainloop_run;
+pa_mainloop_set_poll_func;
+pa_mainloop_wakeup;
+pa_msleep;
+pa_operation_cancel;
+pa_operation_get_state;
+pa_operation_ref;
+pa_operation_unref;
+pa_parse_sample_format;
+pa_path_get_filename;
+pa_proplist_free;
+pa_proplist_contains;
+pa_proplist_clear;
+pa_proplist_copy;
+pa_proplist_get;
+pa_proplist_gets;
+pa_proplist_iterate;
+pa_proplist_update;
+pa_proplist_new;
+pa_proplist_set;
+pa_proplist_sets;
+pa_proplist_setf;
+pa_proplist_unset;
+pa_proplist_unset_many;
+pa_proplist_to_string;
+pa_sample_format_to_string;
+pa_sample_size;
+pa_sample_spec_equal;
+pa_sample_spec_snprint;
+pa_sample_spec_valid;
+pa_signal_done;
+pa_signal_free;
+pa_signal_init;
+pa_signal_new;
+pa_signal_set_destroy;
+pa_simple_drain;
+pa_simple_flush;
+pa_simple_free;
+pa_simple_get_latency;
+pa_simple_new;
+pa_simple_read;
+pa_simple_write;
+pa_stream_connect_playback;
+pa_stream_connect_record;
+pa_stream_connect_upload;
+pa_stream_cork;
+pa_stream_disconnect;
+pa_stream_drain;
+pa_stream_drop;
+pa_stream_finish_upload;
+pa_stream_flush;
+pa_stream_get_buffer_attr;
+pa_stream_get_channel_map;
+pa_stream_get_context;
+pa_stream_get_device_index;
+pa_stream_get_device_name;
+pa_stream_get_index;
+pa_stream_get_latency;
+pa_stream_get_monitor_stream;
+pa_stream_get_sample_spec;
+pa_stream_get_state;
+pa_stream_get_time;
+pa_stream_get_timing_info;
+pa_stream_is_corked;
+pa_stream_is_suspended;
+pa_stream_new;
+pa_stream_new_with_proplist;
+pa_stream_peek;
+pa_stream_prebuf;
+pa_stream_proplist_remove;
+pa_stream_proplist_update;
+pa_stream_readable_size;
+pa_stream_ref;
+pa_stream_set_buffer_attr;
+pa_stream_set_latency_update_callback;
+pa_stream_set_moved_callback;
+pa_stream_set_monitor_stream;
+pa_stream_set_name;
+pa_stream_set_overflow_callback;
+pa_stream_set_read_callback;
+pa_stream_set_started_callback;
+pa_stream_set_state_callback;
+pa_stream_set_suspended_callback;
+pa_stream_set_underflow_callback;
+pa_stream_set_write_callback;
+pa_stream_trigger;
+pa_stream_unref;
+pa_stream_update_sample_rate;
+pa_stream_update_timing_info;
+pa_stream_writable_size;
+pa_stream_write;
+pa_strerror;
+pa_sw_cvolume_multiply;
+pa_sw_volume_from_dB;
+pa_sw_volume_from_linear;
+pa_sw_volume_multiply;
+pa_sw_volume_to_dB;
+pa_sw_volume_to_linear;
+pa_threaded_mainloop_accept;
+pa_threaded_mainloop_free;
+pa_threaded_mainloop_get_api;
+pa_threaded_mainloop_get_retval;
+pa_threaded_mainloop_in_thread;
+pa_threaded_mainloop_lock;
+pa_threaded_mainloop_new;
+pa_threaded_mainloop_signal;
+pa_threaded_mainloop_start;
+pa_threaded_mainloop_stop;
+pa_threaded_mainloop_unlock;
+pa_threaded_mainloop_wait;
+pa_timeval_add;
+pa_timeval_age;
+pa_timeval_cmp;
+pa_timeval_diff;
+pa_timeval_load;
+pa_timeval_store;
+pa_timeval_sub;
+pa_usec_to_bytes;
+pa_utf8_filter;
+pa_utf8_to_locale;
+pa_utf8_valid;
+pa_xfree;
+pa_xmalloc;
+pa_xmalloc0;
+pa_xmemdup;
+pa_xrealloc;
+pa_xstrdup;
+pa_xstrndup;
+local:
+*;
+};
diff --git a/src/modules/.gitignore b/src/modules/.gitignore
new file mode 100644 (file)
index 0000000..2d2d942
--- /dev/null
@@ -0,0 +1 @@
+module-*-symdef.h
diff --git a/src/modules/Makefile b/src/modules/Makefile
new file mode 120000 (symlink)
index 0000000..c110232
--- /dev/null
@@ -0,0 +1 @@
+../pulse/Makefile
\ No newline at end of file
diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c
new file mode 100644 (file)
index 0000000..5d52cbc
--- /dev/null
@@ -0,0 +1,1118 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <limits.h>
+#include <asoundlib.h>
+
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/atomic.h>
+
+#include "alsa-util.h"
+
+struct pa_alsa_fdlist {
+    int num_fds;
+    struct pollfd *fds;
+    /* This is a temporary buffer used to avoid lots of mallocs */
+    struct pollfd *work_fds;
+
+    snd_mixer_t *mixer;
+
+    pa_mainloop_api *m;
+    pa_defer_event *defer;
+    pa_io_event **ios;
+
+    int polled;
+
+    void (*cb)(void *userdata);
+    void *userdata;
+};
+
+static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) {
+
+    struct pa_alsa_fdlist *fdl = userdata;
+    int err, i;
+    unsigned short revents;
+
+    pa_assert(a);
+    pa_assert(fdl);
+    pa_assert(fdl->mixer);
+    pa_assert(fdl->fds);
+    pa_assert(fdl->work_fds);
+
+    if (fdl->polled)
+        return;
+
+    fdl->polled = 1;
+
+    memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
+
+    for (i = 0;i < fdl->num_fds; i++) {
+        if (e == fdl->ios[i]) {
+            if (events & PA_IO_EVENT_INPUT)
+                fdl->work_fds[i].revents |= POLLIN;
+            if (events & PA_IO_EVENT_OUTPUT)
+                fdl->work_fds[i].revents |= POLLOUT;
+            if (events & PA_IO_EVENT_ERROR)
+                fdl->work_fds[i].revents |= POLLERR;
+            if (events & PA_IO_EVENT_HANGUP)
+                fdl->work_fds[i].revents |= POLLHUP;
+            break;
+        }
+    }
+
+    pa_assert(i != fdl->num_fds);
+
+    if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) {
+        pa_log_error("Unable to get poll revent: %s", snd_strerror(err));
+        return;
+    }
+
+    a->defer_enable(fdl->defer, 1);
+
+    if (revents)
+        snd_mixer_handle_events(fdl->mixer);
+}
+
+static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *userdata) {
+    struct pa_alsa_fdlist *fdl = userdata;
+    int num_fds, i, err;
+    struct pollfd *temp;
+
+    pa_assert(a);
+    pa_assert(fdl);
+    pa_assert(fdl->mixer);
+
+    a->defer_enable(fdl->defer, 0);
+
+    num_fds = snd_mixer_poll_descriptors_count(fdl->mixer);
+    pa_assert(num_fds > 0);
+
+    if (num_fds != fdl->num_fds) {
+        if (fdl->fds)
+            pa_xfree(fdl->fds);
+        if (fdl->work_fds)
+            pa_xfree(fdl->work_fds);
+        fdl->fds = pa_xnew0(struct pollfd, num_fds);
+        fdl->work_fds = pa_xnew(struct pollfd, num_fds);
+    }
+
+    memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
+
+    if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) {
+        pa_log_error("Unable to get poll descriptors: %s", snd_strerror(err));
+        return;
+    }
+
+    fdl->polled = 0;
+
+    if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
+        return;
+
+    if (fdl->ios) {
+        for (i = 0; i < fdl->num_fds; i++)
+            a->io_free(fdl->ios[i]);
+
+        if (num_fds != fdl->num_fds) {
+            pa_xfree(fdl->ios);
+            fdl->ios = NULL;
+        }
+    }
+
+    if (!fdl->ios)
+        fdl->ios = pa_xnew(pa_io_event*, num_fds);
+
+    /* Swap pointers */
+    temp = fdl->work_fds;
+    fdl->work_fds = fdl->fds;
+    fdl->fds = temp;
+
+    fdl->num_fds = num_fds;
+
+    for (i = 0;i < num_fds;i++)
+        fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
+            ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
+            ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
+            io_cb, fdl);
+}
+
+struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
+    struct pa_alsa_fdlist *fdl;
+
+    fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
+
+    fdl->num_fds = 0;
+    fdl->fds = NULL;
+    fdl->work_fds = NULL;
+    fdl->mixer = NULL;
+    fdl->m = NULL;
+    fdl->defer = NULL;
+    fdl->ios = NULL;
+    fdl->polled = 0;
+
+    return fdl;
+}
+
+void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
+    pa_assert(fdl);
+
+    if (fdl->defer) {
+        pa_assert(fdl->m);
+        fdl->m->defer_free(fdl->defer);
+    }
+
+    if (fdl->ios) {
+        int i;
+        pa_assert(fdl->m);
+        for (i = 0;i < fdl->num_fds;i++)
+            fdl->m->io_free(fdl->ios[i]);
+        pa_xfree(fdl->ios);
+    }
+
+    if (fdl->fds)
+        pa_xfree(fdl->fds);
+    if (fdl->work_fds)
+        pa_xfree(fdl->work_fds);
+
+    pa_xfree(fdl);
+}
+
+int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) {
+    pa_assert(fdl);
+    pa_assert(mixer_handle);
+    pa_assert(m);
+    pa_assert(!fdl->m);
+
+    fdl->mixer = mixer_handle;
+    fdl->m = m;
+    fdl->defer = m->defer_new(m, defer_cb, fdl);
+
+    return 0;
+}
+
+static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_sample_format_t *f) {
+
+    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,
+        [PA_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
+        [PA_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
+    };
+
+    static const pa_sample_format_t try_order[] = {
+        PA_SAMPLE_FLOAT32NE,
+        PA_SAMPLE_FLOAT32RE,
+        PA_SAMPLE_S32NE,
+        PA_SAMPLE_S32RE,
+        PA_SAMPLE_S16NE,
+        PA_SAMPLE_S16RE,
+        PA_SAMPLE_ALAW,
+        PA_SAMPLE_ULAW,
+        PA_SAMPLE_U8,
+        PA_SAMPLE_INVALID
+    };
+
+    int i, ret;
+
+    pa_assert(pcm_handle);
+    pa_assert(f);
+
+    if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
+        return ret;
+
+    if (*f == PA_SAMPLE_FLOAT32BE)
+        *f = PA_SAMPLE_FLOAT32LE;
+    else if (*f == PA_SAMPLE_FLOAT32LE)
+        *f = PA_SAMPLE_FLOAT32BE;
+    else if (*f == PA_SAMPLE_S16BE)
+        *f = PA_SAMPLE_S16LE;
+    else if (*f == PA_SAMPLE_S16LE)
+        *f = PA_SAMPLE_S16BE;
+    else if (*f == PA_SAMPLE_S32BE)
+        *f = PA_SAMPLE_S32LE;
+    else if (*f == PA_SAMPLE_S32LE)
+        *f = PA_SAMPLE_S32BE;
+    else
+        goto try_auto;
+
+    if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
+        return ret;
+
+try_auto:
+
+    for (i = 0; try_order[i] != PA_SAMPLE_INVALID; i++) {
+        *f = try_order[i];
+
+        if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
+            return ret;
+    }
+
+    return -1;
+}
+
+/* Set the hardware parameters of the given ALSA device. Returns the
+ * selected fragment settings in *period and *period_size */
+int pa_alsa_set_hw_params(
+        snd_pcm_t *pcm_handle,
+        pa_sample_spec *ss,
+        uint32_t *periods,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t tsched_size,
+        pa_bool_t *use_mmap,
+        pa_bool_t *use_tsched,
+        pa_bool_t require_exact_channel_number) {
+
+    int ret = -1;
+    snd_pcm_uframes_t _period_size = *period_size;
+    unsigned int _periods = *periods;
+    snd_pcm_uframes_t buffer_size;
+    unsigned int r = ss->rate;
+    unsigned int c = ss->channels;
+    pa_sample_format_t f = ss->format;
+    snd_pcm_hw_params_t *hwparams;
+    pa_bool_t _use_mmap = use_mmap && *use_mmap;
+    pa_bool_t _use_tsched = use_tsched && *use_tsched;
+    int dir;
+
+    pa_assert(pcm_handle);
+    pa_assert(ss);
+    pa_assert(periods);
+    pa_assert(period_size);
+
+    snd_pcm_hw_params_alloca(&hwparams);
+
+    if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0)
+        goto finish;
+
+    if ((ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0)
+        goto finish;
+
+    if (_use_mmap) {
+        if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
+
+            /* mmap() didn't work, fall back to interleaved */
+
+            if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+                goto finish;
+
+            _use_mmap = FALSE;
+        }
+
+    } else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+        goto finish;
+
+    if (!_use_mmap)
+        _use_tsched = FALSE;
+
+    if ((ret = set_format(pcm_handle, hwparams, &f)) < 0)
+        goto finish;
+
+    if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0)
+        goto finish;
+
+    /* Adjust the buffer sizes, if we didn't get the rate we were asking for */
+    _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate);
+    tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate);
+
+    if (require_exact_channel_number) {
+        if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0)
+            goto finish;
+    } else {
+        if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0)
+            goto finish;
+    }
+
+    if (_use_tsched) {
+        _period_size = tsched_size;
+        _periods = 1;
+
+        pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0);
+        pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r);
+    }
+
+    buffer_size = _periods * _period_size;
+
+    if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0)
+        goto finish;
+
+    if (_periods > 0) {
+        dir = 1;
+        if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
+            dir = -1;
+            if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
+                goto finish;
+        }
+    }
+
+    if (_period_size > 0)
+        if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
+            goto finish;
+
+    if  ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
+        goto finish;
+
+    if (ss->rate != r)
+        pa_log_warn("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r);
+
+    if (ss->channels != c)
+        pa_log_warn("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c);
+
+    if (ss->format != f)
+        pa_log_warn("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f));
+
+    if ((ret = snd_pcm_prepare(pcm_handle)) < 0)
+        goto finish;
+
+    if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
+        (ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0)
+        goto finish;
+
+    /* If the sample rate deviates too much, we need to resample */
+    if (r < ss->rate*.95 || r > ss->rate*1.05)
+        ss->rate = r;
+    ss->channels = c;
+    ss->format = f;
+
+    pa_assert(_periods > 0);
+    pa_assert(_period_size > 0);
+
+    *periods = _periods;
+    *period_size = _period_size;
+
+    if (use_mmap)
+        *use_mmap = _use_mmap;
+
+    if (use_tsched)
+        *use_tsched = _use_tsched;
+
+    ret = 0;
+
+finish:
+
+    return ret;
+}
+
+int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {
+    snd_pcm_sw_params_t *swparams;
+    int err;
+
+    pa_assert(pcm);
+
+    snd_pcm_sw_params_alloca(&swparams);
+
+    if ((err = snd_pcm_sw_params_current(pcm, swparams) < 0)) {
+        pa_log_warn("Unable to determine current swparams: %s\n", snd_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) {
+        pa_log_warn("Unable to set stop threshold: %s\n", snd_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) {
+        pa_log_warn("Unable to set start threshold: %s\n", snd_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
+        pa_log_error("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err));
+        return err;
+    }
+
+    if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
+        pa_log_warn("Unable to set sw params: %s\n", snd_strerror(err));
+        return err;
+    }
+
+    return 0;
+}
+
+struct device_info {
+    pa_channel_map map;
+    const char *name;
+};
+
+static const struct device_info device_table[] = {
+    {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } }, "front" },
+
+    {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, "surround40" },
+
+    {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+            PA_CHANNEL_POSITION_LFE }}, "surround41" },
+
+    {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+            PA_CHANNEL_POSITION_CENTER }}, "surround50" },
+
+    {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+            PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }}, "surround51" },
+
+    {{ 8, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+            PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }} , "surround71" },
+
+    {{ 0, { 0 }}, NULL }
+};
+
+static pa_bool_t channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
+    pa_bool_t in_a[PA_CHANNEL_POSITION_MAX];
+    unsigned i;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    memset(in_a, 0, sizeof(in_a));
+
+    for (i = 0; i < a->channels; i++)
+        in_a[a->map[i]] = TRUE;
+
+    for (i = 0; i < b->channels; i++)
+        if (!in_a[b->map[i]])
+            return FALSE;
+
+    return TRUE;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_id(
+        const char *dev_id,
+        char **dev,
+        pa_sample_spec *ss,
+        pa_channel_map* map,
+        int mode,
+        uint32_t *nfrags,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t tsched_size,
+        pa_bool_t *use_mmap,
+        pa_bool_t *use_tsched) {
+
+    int i;
+    int direction = 1;
+    int err;
+    char *d;
+    snd_pcm_t *pcm_handle;
+
+    pa_assert(dev_id);
+    pa_assert(dev);
+    pa_assert(ss);
+    pa_assert(map);
+    pa_assert(nfrags);
+    pa_assert(period_size);
+
+    /* First we try to find a device string with a superset of the
+     * requested channel map and open it without the plug: prefix. We
+     * iterate through our device table from top to bottom and take
+     * the first that matches. If we didn't find a working device that
+     * way, we iterate backwards, and check all devices that do not
+     * provide a superset of the requested channel map.*/
+
+    for (i = 0;; i += direction) {
+        pa_sample_spec try_ss;
+
+        if (i < 0) {
+            pa_assert(direction == -1);
+
+            /* OK, so we iterated backwards, and now are at the
+             * beginning of our list. */
+
+            break;
+
+        } else if (!device_table[i].name) {
+            pa_assert(direction == 1);
+
+            /* OK, so we are at the end of our list. at iterated
+             * forwards. */
+
+            i--;
+            direction = -1;
+        }
+
+        if ((direction > 0) == !channel_map_superset(&device_table[i].map, map))
+            continue;
+
+        d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id);
+        pa_log_debug("Trying %s...", d);
+
+        if ((err = snd_pcm_open(&pcm_handle, d, mode,
+                                SND_PCM_NONBLOCK|
+                                SND_PCM_NO_AUTO_RESAMPLE|
+                                SND_PCM_NO_AUTO_CHANNELS|
+                                SND_PCM_NO_AUTO_FORMAT)) < 0) {
+            pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err));
+            pa_xfree(d);
+            continue;
+        }
+
+        try_ss.channels = device_table[i].map.channels;
+        try_ss.rate = ss->rate;
+        try_ss.format = ss->format;
+
+        if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
+            pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err));
+            pa_xfree(d);
+            snd_pcm_close(pcm_handle);
+            continue;
+        }
+
+        *ss = try_ss;
+        *map = device_table[i].map;
+        pa_assert(map->channels == ss->channels);
+        *dev = d;
+        return pcm_handle;
+    }
+
+    /* OK, we didn't find any good device, so let's try the raw plughw: stuff */
+
+    d = pa_sprintf_malloc("plughw:%s", dev_id);
+    pa_log_debug("Trying %s as last resort...", d);
+    pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched);
+    pa_xfree(d);
+
+    return pcm_handle;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_string(
+        const char *device,
+        char **dev,
+        pa_sample_spec *ss,
+        pa_channel_map* map,
+        int mode,
+        uint32_t *nfrags,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t tsched_size,
+        pa_bool_t *use_mmap,
+        pa_bool_t *use_tsched) {
+
+    int err;
+    char *d;
+    snd_pcm_t *pcm_handle;
+
+    pa_assert(device);
+    pa_assert(dev);
+    pa_assert(ss);
+    pa_assert(map);
+    pa_assert(nfrags);
+    pa_assert(period_size);
+
+    d = pa_xstrdup(device);
+
+    for (;;) {
+
+        if ((err = snd_pcm_open(&pcm_handle, d, mode, SND_PCM_NONBLOCK|
+                                SND_PCM_NO_AUTO_RESAMPLE|
+                                SND_PCM_NO_AUTO_CHANNELS|
+                                SND_PCM_NO_AUTO_FORMAT)) < 0) {
+            pa_log("Error opening PCM device %s: %s", d, snd_strerror(err));
+            pa_xfree(d);
+            return NULL;
+        }
+
+        if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) {
+
+            if (err == -EPERM) {
+                /* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */
+
+                if (pa_startswith(d, "hw:")) {
+                    char *t = pa_sprintf_malloc("plughw:%s", d+3);
+                    pa_log_debug("Opening the device as '%s' didn't work, retrying with '%s'.", d, t);
+                    pa_xfree(d);
+                    d = t;
+
+                    snd_pcm_close(pcm_handle);
+                    continue;
+                }
+
+                pa_log("Failed to set hardware parameters on %s: %s", d, snd_strerror(err));
+                pa_xfree(d);
+                snd_pcm_close(pcm_handle);
+                return NULL;
+            }
+        }
+
+        *dev = d;
+
+        if (ss->channels != map->channels)
+            pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_ALSA);
+
+        return pcm_handle;
+    }
+}
+
+int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
+    int err;
+
+    pa_assert(mixer);
+    pa_assert(dev);
+
+    if ((err = snd_mixer_attach(mixer, dev)) < 0) {
+        pa_log_info("Unable to attach to mixer %s: %s", dev, snd_strerror(err));
+        return -1;
+    }
+
+    if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
+        pa_log_warn("Unable to register mixer: %s", snd_strerror(err));
+        return -1;
+    }
+
+    if ((err = snd_mixer_load(mixer)) < 0) {
+        pa_log_warn("Unable to load mixer: %s", snd_strerror(err));
+        return -1;
+    }
+
+    pa_log_info("Successfully attached to mixer '%s'", dev);
+
+    return 0;
+}
+
+snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback) {
+    snd_mixer_elem_t *elem;
+    snd_mixer_selem_id_t *sid = NULL;
+
+    snd_mixer_selem_id_alloca(&sid);
+
+    pa_assert(mixer);
+    pa_assert(name);
+
+    snd_mixer_selem_id_set_name(sid, name);
+
+    if (!(elem = snd_mixer_find_selem(mixer, sid))) {
+        pa_log_info("Cannot find mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
+
+        if (fallback) {
+            snd_mixer_selem_id_set_name(sid, fallback);
+
+            if (!(elem = snd_mixer_find_selem(mixer, sid)))
+                pa_log_warn("Cannot find fallback mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
+        }
+    }
+
+    if (elem)
+        pa_log_info("Using mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
+
+    return elem;
+}
+
+static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
+    [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
+
+    [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
+    [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
+    [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
+
+    [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
+    [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
+    [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
+
+    [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
+
+    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+
+    [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
+    [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
+
+    [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX9] =  SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
+
+    [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+
+    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
+
+    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
+    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
+};
+
+
+int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback) {
+    unsigned i;
+    pa_bool_t alsa_channel_used[SND_MIXER_SCHN_LAST];
+    pa_bool_t mono_used = FALSE;
+
+    pa_assert(elem);
+    pa_assert(channel_map);
+    pa_assert(mixer_map);
+
+    memset(&alsa_channel_used, 0, sizeof(alsa_channel_used));
+
+    if (channel_map->channels > 1 &&
+        ((playback && snd_mixer_selem_has_playback_volume_joined(elem)) ||
+         (!playback && snd_mixer_selem_has_capture_volume_joined(elem)))) {
+        pa_log_info("ALSA device lacks independant volume controls for each channel, falling back to software volume control.");
+        return -1;
+    }
+
+    for (i = 0; i < channel_map->channels; i++) {
+        snd_mixer_selem_channel_id_t id;
+        pa_bool_t is_mono;
+
+        is_mono = channel_map->map[i] == PA_CHANNEL_POSITION_MONO;
+        id = alsa_channel_ids[channel_map->map[i]];
+
+        if (!is_mono && id == SND_MIXER_SCHN_UNKNOWN) {
+            pa_log_info("Configured channel map contains channel '%s' that is unknown to the ALSA mixer. Falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
+            return -1;
+        }
+
+        if ((is_mono && mono_used) || (!is_mono && alsa_channel_used[id])) {
+            pa_log_info("Channel map has duplicate channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
+            return -1;
+        }
+
+        if ((playback && (!snd_mixer_selem_has_playback_channel(elem, id) || (is_mono && !snd_mixer_selem_is_playback_mono(elem)))) ||
+            (!playback && (!snd_mixer_selem_has_capture_channel(elem, id) || (is_mono && !snd_mixer_selem_is_capture_mono(elem))))) {
+
+            pa_log_info("ALSA device lacks separate volumes control for channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i]));
+            return -1;
+        }
+
+        if (is_mono) {
+            mixer_map[i] = SND_MIXER_SCHN_MONO;
+            mono_used = TRUE;
+        } else {
+            mixer_map[i] = id;
+            alsa_channel_used[id] = TRUE;
+        }
+    }
+
+    pa_log_info("All %u channels can be mapped to mixer channels.", channel_map->channels);
+
+    return 0;
+}
+
+void pa_alsa_0dB_playback(snd_mixer_elem_t *elem) {
+    long min, max, v;
+
+    pa_assert(elem);
+
+    /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use
+     * raw volume levels and fix them to 75% */
+
+    if (snd_mixer_selem_set_playback_dB_all(elem, 0, -1) >= 0)
+        return;
+
+    if (snd_mixer_selem_set_playback_dB_all(elem, 0, 1) >= 0)
+        return;
+
+    if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) < 0)
+        return;
+
+    v = min + ((max - min) * 3) / 4; /* 75% */
+
+    if (v <= min)
+        v = max;
+
+    snd_mixer_selem_set_playback_volume_all(elem, v);
+}
+
+void pa_alsa_0dB_capture(snd_mixer_elem_t *elem) {
+    long min, max, v;
+
+    pa_assert(elem);
+
+    /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use
+     * raw volume levels and fix them to 75% */
+
+    if (snd_mixer_selem_set_capture_dB_all(elem, 0, -1) >= 0)
+        return;
+
+    if (snd_mixer_selem_set_capture_dB_all(elem, 0, 1) >= 0)
+        return;
+
+    if (snd_mixer_selem_get_capture_volume_range(elem, &min, &max) < 0)
+        return;
+
+    v = min + ((max - min) * 3) / 4; /* 75% */
+
+    if (v <= min)
+        v = max;
+
+    snd_mixer_selem_set_capture_volume_all(elem, v);
+}
+
+void pa_alsa_dump(snd_pcm_t *pcm) {
+    int err;
+    snd_output_t *out;
+
+    pa_assert(pcm);
+
+    pa_assert_se(snd_output_buffer_open(&out) == 0);
+
+    if ((err = snd_pcm_dump(pcm, out)) < 0)
+        pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err));
+    else {
+        char *s = NULL;
+        snd_output_buffer_string(out, &s);
+        pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s));
+    }
+
+    pa_assert_se(snd_output_close(out) == 0);
+}
+
+void pa_alsa_dump_status(snd_pcm_t *pcm) {
+    int err;
+    snd_output_t *out;
+    snd_pcm_status_t *status;
+
+    pa_assert(pcm);
+
+    snd_pcm_status_alloca(&status);
+
+    pa_assert_se(snd_output_buffer_open(&out) == 0);
+
+    pa_assert_se(snd_pcm_status(pcm, status) == 0);
+
+    if ((err = snd_pcm_status_dump(status, out)) < 0)
+        pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err));
+    else {
+        char *s = NULL;
+        snd_output_buffer_string(out, &s);
+        pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s));
+    }
+
+    pa_assert_se(snd_output_close(out) == 0);
+}
+
+static void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt,...) {
+    va_list ap;
+
+    va_start(ap, fmt);
+
+    pa_log_levelv_meta(PA_LOG_WARN, file, line, function, fmt, ap);
+
+    va_end(ap);
+}
+
+static pa_atomic_t n_error_handler_installed = PA_ATOMIC_INIT(0);
+
+void pa_alsa_redirect_errors_inc(void) {
+    /* This is not really thread safe, but we do our best */
+
+    if (pa_atomic_inc(&n_error_handler_installed) == 0)
+        snd_lib_error_set_handler(alsa_error_handler);
+}
+
+void pa_alsa_redirect_errors_dec(void) {
+    int r;
+
+    pa_assert_se((r = pa_atomic_dec(&n_error_handler_installed)) >= 1);
+
+    if (r == 1)
+        snd_lib_error_set_handler(NULL);
+}
+
+void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
+
+    static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = {
+        [SND_PCM_CLASS_GENERIC] = "generic",
+        [SND_PCM_CLASS_MULTI] = "multi",
+        [SND_PCM_CLASS_MODEM] = "modem",
+        [SND_PCM_CLASS_DIGITIZER] = "digitizer"
+    };
+    static const char * const class_table[SND_PCM_CLASS_LAST+1] = {
+        [SND_PCM_CLASS_GENERIC] = "sound",
+        [SND_PCM_CLASS_MULTI] = NULL,
+        [SND_PCM_CLASS_MODEM] = "modem",
+        [SND_PCM_CLASS_DIGITIZER] = NULL
+    };
+    static const char * const alsa_subclass_table[SND_PCM_SUBCLASS_LAST+1] = {
+        [SND_PCM_SUBCLASS_GENERIC_MIX] = "generic-mix",
+        [SND_PCM_SUBCLASS_MULTI_MIX] = "multi-mix"
+    };
+
+    snd_pcm_class_t class;
+    snd_pcm_subclass_t subclass;
+    const char *n, *id, *sdn;
+    char *cn = NULL, *lcn = NULL;
+    int card;
+
+    pa_assert(p);
+    pa_assert(pcm_info);
+
+    pa_proplist_sets(p, PA_PROP_DEVICE_API, "alsa");
+
+    class = snd_pcm_info_get_class(pcm_info);
+    if (class <= SND_PCM_CLASS_LAST) {
+        if (class_table[class])
+            pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, class_table[class]);
+        if (alsa_class_table[class])
+            pa_proplist_sets(p, "alsa.class", alsa_class_table[class]);
+    }
+    subclass = snd_pcm_info_get_subclass(pcm_info);
+    if (subclass <= SND_PCM_SUBCLASS_LAST)
+        if (alsa_subclass_table[subclass])
+            pa_proplist_sets(p, "alsa.subclass", alsa_subclass_table[subclass]);
+
+    if ((n = snd_pcm_info_get_name(pcm_info)))
+        pa_proplist_sets(p, "alsa.name", n);
+
+    if ((id = snd_pcm_info_get_id(pcm_info)))
+        pa_proplist_sets(p, "alsa.id", id);
+
+    pa_proplist_setf(p, "alsa.subdevice", "%u", snd_pcm_info_get_subdevice(pcm_info));
+    if ((sdn = snd_pcm_info_get_subdevice_name(pcm_info)))
+        pa_proplist_sets(p, "alsa.subdevice_name", sdn);
+
+    pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info));
+
+    if ((card = snd_pcm_info_get_card(pcm_info)) >= 0) {
+        pa_proplist_setf(p, "alsa.card", "%i", card);
+
+        if (snd_card_get_name(card, &cn) >= 0)
+            pa_proplist_sets(p, "alsa.card_name", cn);
+
+        if (snd_card_get_longname(card, &lcn) >= 0)
+            pa_proplist_sets(p, "alsa.long_card_name", lcn);
+    }
+
+    if (cn && n)
+        pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s - %s", cn, n);
+    else if (cn)
+        pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, cn);
+    else if (n)
+        pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n);
+
+    free(lcn);
+    free(cn);
+}
+
+int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
+    snd_pcm_state_t state;
+    int err;
+
+    pa_assert(pcm);
+
+    if (revents & POLLERR)
+        pa_log_warn("Got POLLERR from ALSA");
+    if (revents & POLLNVAL)
+        pa_log_warn("Got POLLNVAL from ALSA");
+    if (revents & POLLHUP)
+        pa_log_warn("Got POLLHUP from ALSA");
+
+    state = snd_pcm_state(pcm);
+    pa_log_warn("PCM state is %s", snd_pcm_state_name(state));
+
+    /* Try to recover from this error */
+
+    switch (state) {
+
+        case SND_PCM_STATE_XRUN:
+            if ((err = snd_pcm_recover(pcm, -EPIPE, 1)) != 0) {
+                pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err));
+                return -1;
+            }
+            break;
+
+        case SND_PCM_STATE_SUSPENDED:
+            if ((err = snd_pcm_recover(pcm, -ESTRPIPE, 1)) != 0) {
+                pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err));
+                return -1;
+            }
+            break;
+
+        default:
+
+            snd_pcm_drop(pcm);
+
+            if ((err = snd_pcm_prepare(pcm)) < 0) {
+                pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err));
+                return -1;
+            }
+            break;
+    }
+
+    return 0;
+}
+
+pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) {
+    int n, err;
+    struct pollfd *pollfd;
+    pa_rtpoll_item *item;
+
+    pa_assert(pcm);
+
+    if ((n = snd_pcm_poll_descriptors_count(pcm)) < 0) {
+        pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n));
+        return NULL;
+    }
+
+    item = pa_rtpoll_item_new(rtpoll, PA_RTPOLL_NEVER, n);
+    pollfd = pa_rtpoll_item_get_pollfd(item, NULL);
+
+    if ((err = snd_pcm_poll_descriptors(pcm, pollfd, n)) < 0) {
+        pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err));
+        pa_rtpoll_item_free(item);
+        return NULL;
+    }
+
+    return item;
+}
diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h
new file mode 100644 (file)
index 0000000..4de8bcd
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef fooalsautilhfoo
+#define fooalsautilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <asoundlib.h>
+
+#include <pulse/sample.h>
+#include <pulse/mainloop-api.h>
+#include <pulse/channelmap.h>
+#include <pulse/proplist.h>
+
+#include <pulsecore/rtpoll.h>
+
+typedef struct pa_alsa_fdlist pa_alsa_fdlist;
+
+struct pa_alsa_fdlist *pa_alsa_fdlist_new(void);
+void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl);
+int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m);
+
+int pa_alsa_set_hw_params(
+        snd_pcm_t *pcm_handle,
+        pa_sample_spec *ss,
+        uint32_t *periods,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t tsched_size,
+        pa_bool_t *use_mmap,
+        pa_bool_t *use_tsched,
+        pa_bool_t require_exact_channel_number);
+
+int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
+
+int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
+snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback);
+
+snd_pcm_t *pa_alsa_open_by_device_id(
+        const char *dev_id,
+        char **dev,
+        pa_sample_spec *ss,
+        pa_channel_map* map,
+        int mode,
+        uint32_t *nfrags,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t tsched_size,
+        pa_bool_t *use_mmap,
+        pa_bool_t *use_tsched);
+
+snd_pcm_t *pa_alsa_open_by_device_string(
+        const char *device,
+        char **dev,
+        pa_sample_spec *ss,
+        pa_channel_map* map,
+        int mode,
+        uint32_t *nfrags,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t tsched_size,
+        pa_bool_t *use_mmap,
+        pa_bool_t *use_tsched);
+
+int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback);
+
+void pa_alsa_0dB_playback(snd_mixer_elem_t *elem);
+void pa_alsa_0dB_capture(snd_mixer_elem_t *elem);
+
+void pa_alsa_dump(snd_pcm_t *pcm);
+void pa_alsa_dump_status(snd_pcm_t *pcm);
+
+void pa_alsa_redirect_errors_inc(void);
+void pa_alsa_redirect_errors_dec(void);
+
+void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info);
+
+int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
+
+pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll);
+
+#endif
diff --git a/src/modules/bt-proximity-helper.c b/src/modules/bt-proximity-helper.c
new file mode 100644 (file)
index 0000000..3767f01
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Small SUID helper that allows us to ping a BT device. Borrows
+ * heavily from bluez-utils' l2ping, which is licensed as GPL2+
+ * and comes with a copyright like this:
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2007  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#undef NDEBUG
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#define PING_STRING "PulseAudio"
+#define IDENT 200
+#define TIMEOUT 4
+#define INTERVAL 2
+
+static void update_status(int found) {
+    static int status = -1;
+
+    if (!found && status != 0)
+        printf("-");
+    if (found && status <= 0)
+        printf("+");
+
+    fflush(stdout);
+    status = !!found;
+}
+
+int main(int argc, char *argv[]) {
+    struct sockaddr_l2 addr;
+    union {
+        l2cap_cmd_hdr hdr;
+        uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
+    }  packet;
+    int fd = -1;
+    uint8_t id = IDENT;
+    int connected = 0;
+
+    assert(argc == 2);
+
+    for (;;) {
+        fd_set fds;
+        struct timeval end;
+        ssize_t r;
+
+        if (!connected) {
+
+            if (fd >= 0)
+                close(fd);
+
+            if ((fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
+                fprintf(stderr, "socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP) failed: %s", strerror(errno));
+                goto finish;
+            }
+
+            memset(&addr, 0, sizeof(addr));
+            addr.l2_family = AF_BLUETOOTH;
+            bacpy(&addr.l2_bdaddr, BDADDR_ANY);
+
+            if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+                fprintf(stderr, "bind() failed: %s", strerror(errno));
+                goto finish;
+            }
+
+            memset(&addr, 0, sizeof(addr));
+            addr.l2_family = AF_BLUETOOTH;
+            str2ba(argv[1], &addr.l2_bdaddr);
+
+            if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+
+                if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+                    update_status(0);
+                    sleep(INTERVAL);
+                    continue;
+                }
+
+                fprintf(stderr, "connect() failed: %s", strerror(errno));
+                goto finish;
+            }
+
+            connected = 1;
+        }
+
+        assert(connected);
+
+        memset(&packet, 0, sizeof(packet));
+        strcpy((char*) packet.buf + L2CAP_CMD_HDR_SIZE, PING_STRING);
+        packet.hdr.ident = id;
+        packet.hdr.len = htobs(sizeof(PING_STRING));
+        packet.hdr.code = L2CAP_ECHO_REQ;
+
+        if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
+
+            if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+                update_status(0);
+                connected = 0;
+                sleep(INTERVAL);
+                continue;
+            }
+
+            fprintf(stderr, "send() failed: %s", strerror(errno));
+            goto finish;
+        }
+
+        assert(r == sizeof(packet));
+
+        gettimeofday(&end, NULL);
+        end.tv_sec += TIMEOUT;
+
+        for (;;) {
+            struct timeval now, delta;
+
+            gettimeofday(&now, NULL);
+
+            if (timercmp(&end, &now, <=)) {
+                update_status(0);
+                connected = 0;
+                sleep(INTERVAL);
+                break;
+            }
+
+            timersub(&end, &now, &delta);
+
+            FD_ZERO(&fds);
+            FD_SET(fd, &fds);
+
+            if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
+                fprintf(stderr, "select() failed: %s", strerror(errno));
+                goto finish;
+            }
+
+            if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
+
+                if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
+                    update_status(0);
+                    connected = 0;
+                    sleep(INTERVAL);
+                    break;
+                }
+
+                fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
+                goto finish;
+            }
+
+            assert(r >= L2CAP_CMD_HDR_SIZE);
+
+            if (packet.hdr.ident != id)
+                continue;
+
+            if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {
+
+                if (++id >= 0xFF)
+                    id = IDENT;
+
+                update_status(1);
+                sleep(INTERVAL);
+                break;
+            }
+        }
+    }
+
+finish:
+
+    if (fd >= 0)
+        close(fd);
+
+    return 1;
+}
diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c
new file mode 100644 (file)
index 0000000..905be13
--- /dev/null
@@ -0,0 +1,327 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Shams E. King
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulsecore/log.h>
+#include <pulsecore/props.h>
+
+#include "dbus-util.h"
+
+struct pa_dbus_connection {
+    PA_REFCNT_DECLARE;
+
+    pa_core *core;
+    DBusConnection *connection;
+    const char *property_name;
+    pa_defer_event* dispatch_event;
+};
+
+static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
+    DBusConnection *conn = userdata;
+
+    if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
+        /* no more data to process, disable the deferred */
+        ea->defer_enable(ev, 0);
+    }
+}
+
+/* DBusDispatchStatusFunction callback for the pa mainloop */
+static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
+    pa_dbus_connection *c = userdata;
+
+    pa_assert(c);
+
+    switch(status) {
+
+        case DBUS_DISPATCH_COMPLETE:
+            c->core->mainloop->defer_enable(c->dispatch_event, 0);
+            break;
+
+        case DBUS_DISPATCH_DATA_REMAINS:
+        case DBUS_DISPATCH_NEED_MEMORY:
+        default:
+            c->core->mainloop->defer_enable(c->dispatch_event, 1);
+            break;
+    }
+}
+
+static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
+    unsigned int flags;
+    pa_io_event_flags_t events = 0;
+
+    pa_assert(watch);
+
+    flags = dbus_watch_get_flags(watch);
+
+    /* no watch flags for disabled watches */
+    if (!dbus_watch_get_enabled(watch))
+        return PA_IO_EVENT_NULL;
+
+    if (flags & DBUS_WATCH_READABLE)
+        events |= PA_IO_EVENT_INPUT;
+    if (flags & DBUS_WATCH_WRITABLE)
+        events |= PA_IO_EVENT_OUTPUT;
+
+    return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
+}
+
+/* pa_io_event_cb_t IO event handler */
+static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    unsigned int flags = 0;
+    DBusWatch *watch = userdata;
+
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+    pa_assert(fd == dbus_watch_get_unix_fd(watch));
+#else
+    pa_assert(fd == dbus_watch_get_fd(watch));
+#endif
+
+    if (!dbus_watch_get_enabled(watch)) {
+        pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
+        return;
+    }
+
+    if (events & PA_IO_EVENT_INPUT)
+        flags |= DBUS_WATCH_READABLE;
+    if (events & PA_IO_EVENT_OUTPUT)
+        flags |= DBUS_WATCH_WRITABLE;
+    if (events & PA_IO_EVENT_HANGUP)
+        flags |= DBUS_WATCH_HANGUP;
+    if (events & PA_IO_EVENT_ERROR)
+        flags |= DBUS_WATCH_ERROR;
+
+    dbus_watch_handle(watch, flags);
+}
+
+/* pa_time_event_cb_t timer event handler */
+static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) {
+    DBusTimeout *timeout = userdata;
+
+    if (dbus_timeout_get_enabled(timeout)) {
+        struct timeval next = *tv;
+        dbus_timeout_handle(timeout);
+
+        /* restart it for the next scheduled time */
+        pa_timeval_add(&next, dbus_timeout_get_interval(timeout) * 1000);
+        ea->time_restart(e, &next);
+    }
+}
+
+/* DBusAddWatchFunction callback for pa mainloop */
+static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
+    pa_core *c = PA_CORE(data);
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(c);
+
+    ev = c->mainloop->io_new(
+            c->mainloop,
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+            dbus_watch_get_unix_fd(watch),
+#else
+            dbus_watch_get_fd(watch),
+#endif
+            get_watch_flags(watch), handle_io_event, watch);
+
+    dbus_watch_set_data(watch, ev, NULL);
+
+    return TRUE;
+}
+
+/* DBusRemoveWatchFunction callback for pa mainloop */
+static void remove_watch(DBusWatch *watch, void *data) {
+    pa_core *c = PA_CORE(data);
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(c);
+
+    if ((ev = dbus_watch_get_data(watch)))
+        c->mainloop->io_free(ev);
+}
+
+/* DBusWatchToggledFunction callback for pa mainloop */
+static void toggle_watch(DBusWatch *watch, void *data) {
+    pa_core *c = PA_CORE(data);
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_core_assert_ref(c);
+
+    pa_assert_se(ev = dbus_watch_get_data(watch));
+
+    /* get_watch_flags() checks if the watch is enabled */
+    c->mainloop->io_enable(ev, get_watch_flags(watch));
+}
+
+/* DBusAddTimeoutFunction callback for pa mainloop */
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
+    pa_core *c = PA_CORE(data);
+    pa_time_event *ev;
+    struct timeval tv;
+
+    pa_assert(timeout);
+    pa_assert(c);
+
+    if (!dbus_timeout_get_enabled(timeout))
+        return FALSE;
+
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000);
+
+    ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout);
+
+    dbus_timeout_set_data(timeout, ev, NULL);
+
+    return TRUE;
+}
+
+/* DBusRemoveTimeoutFunction callback for pa mainloop */
+static void remove_timeout(DBusTimeout *timeout, void *data) {
+    pa_core *c = PA_CORE(data);
+    pa_time_event *ev;
+
+    pa_assert(timeout);
+    pa_assert(c);
+
+    if ((ev = dbus_timeout_get_data(timeout)))
+        c->mainloop->time_free(ev);
+}
+
+/* DBusTimeoutToggledFunction callback for pa mainloop */
+static void toggle_timeout(DBusTimeout *timeout, void *data) {
+    pa_core *c = PA_CORE(data);
+    pa_time_event *ev;
+
+    pa_assert(timeout);
+    pa_assert(c);
+
+    pa_assert_se(ev = dbus_timeout_get_data(timeout));
+
+    if (dbus_timeout_get_enabled(timeout)) {
+        struct timeval tv;
+
+        pa_gettimeofday(&tv);
+        pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000);
+
+        c->mainloop->time_restart(ev, &tv);
+    } else
+        c->mainloop->time_restart(ev, NULL);
+}
+
+static void wakeup_main(void *userdata) {
+    pa_dbus_connection *c = userdata;
+
+    pa_assert(c);
+
+    /* this will wakeup the mainloop and dispatch events, although
+     * it may not be the cleanest way of accomplishing it */
+    c->core->mainloop->defer_enable(c->dispatch_event, 1);
+}
+
+static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name) {
+    pa_dbus_connection *pconn;
+
+    pconn = pa_xnew(pa_dbus_connection, 1);
+    PA_REFCNT_INIT(pconn);
+    pconn->core = c;
+    pconn->property_name = name;
+    pconn->connection = conn;
+    pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb, conn);
+
+    pa_property_set(c, name, pconn);
+
+    return pconn;
+}
+
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) > 0);
+    pa_assert(c->connection);
+
+    return c->connection;
+}
+
+void pa_dbus_connection_unref(pa_dbus_connection *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) > 0);
+
+    if (PA_REFCNT_DEC(c) > 0)
+        return;
+
+    if (dbus_connection_get_is_connected(c->connection)) {
+        dbus_connection_close(c->connection);
+         /* must process remaining messages, bit of a kludge to handle
+         * both unload and shutdown */
+        while (dbus_connection_read_write_dispatch(c->connection, -1));
+    }
+
+    /* already disconnected, just free */
+    pa_property_remove(c->core, c->property_name);
+    c->core->mainloop->defer_free(c->dispatch_event);
+    dbus_connection_unref(c->connection);
+    pa_xfree(c);
+}
+
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) > 0);
+
+    PA_REFCNT_INC(c);
+
+    return c;
+}
+
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) {
+
+    static const char *const prop_name[] = {
+        [DBUS_BUS_SESSION] = "dbus-connection-session",
+        [DBUS_BUS_SYSTEM] = "dbus-connection-system",
+        [DBUS_BUS_STARTER] = "dbus-connection-starter"
+    };
+    DBusConnection *conn;
+    pa_dbus_connection *pconn;
+
+    pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
+
+    if ((pconn = pa_property_get(c, prop_name[type])))
+        return pa_dbus_connection_ref(pconn);
+
+    if (!(conn = dbus_bus_get_private(type, error)))
+        return NULL;
+
+    pconn = pa_dbus_connection_new(c, conn, prop_name[type]);
+
+    dbus_connection_set_exit_on_disconnect(conn, FALSE);
+    dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
+    dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, c, NULL);
+    dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, c, NULL);
+    dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
+
+    return pconn;
+}
diff --git a/src/modules/dbus-util.h b/src/modules/dbus-util.h
new file mode 100644 (file)
index 0000000..2b24ac6
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef foodbusutilhfoo
+#define foodbusutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Shams E. King
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <dbus/dbus.h>
+
+typedef struct pa_dbus_connection pa_dbus_connection;
+
+/* return the DBusConnection of the specified type for the given core,
+ * like dbus_bus_get(), but integrates the connection with the pa_core */
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error);
+
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *conn);
+
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *conn);
+void pa_dbus_connection_unref(pa_dbus_connection *conn);
+
+#endif
diff --git a/src/modules/gconf/Makefile b/src/modules/gconf/Makefile
new file mode 100644 (file)
index 0000000..316beb7
--- /dev/null
@@ -0,0 +1,13 @@
+# This is a dirty trick just to ease compilation with emacs
+#
+# This file is not intended to be distributed or anything
+#
+# So: don't touch it, even better ignore it!
+
+all:
+       $(MAKE) -C ../..
+
+clean:
+       $(MAKE) -C ../.. clean
+
+.PHONY: all clean
diff --git a/src/modules/gconf/gconf-helper.c b/src/modules/gconf/gconf-helper.c
new file mode 100644 (file)
index 0000000..f5016fa
--- /dev/null
@@ -0,0 +1,133 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <stdio.h>
+
+#include <gconf/gconf-client.h>
+#include <glib.h>
+
+#include <pulsecore/core-util.h>
+
+#define PA_GCONF_ROOT "/system/pulseaudio"
+#define PA_GCONF_PATH_MODULES PA_GCONF_ROOT"/modules"
+
+static void handle_module(GConfClient *client, const char *name) {
+    gchar p[1024];
+    gboolean enabled, locked;
+    int i;
+
+    pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/locked", name);
+    locked = gconf_client_get_bool(client, p, FALSE);
+
+    if (locked)
+        return;
+
+    pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/enabled", name);
+    enabled = gconf_client_get_bool(client, p, FALSE);
+
+    printf("%c%s%c", enabled ? '+' : '-', name, 0);
+
+    if (enabled) {
+
+        for (i = 0; i < 10; i++) {
+            gchar *n, *a;
+
+            pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/name%i", name, i);
+            if (!(n = gconf_client_get_string(client, p, NULL)) || !*n)
+                break;
+
+            pa_snprintf(p, sizeof(p), PA_GCONF_PATH_MODULES"/%s/args%i", name, i);
+            a = gconf_client_get_string(client, p, NULL);
+
+            printf("%s%c%s%c", n, 0, a ? a : "", 0);
+
+            g_free(n);
+            g_free(a);
+        }
+
+        printf("%c", 0);
+    }
+
+    fflush(stdout);
+}
+
+static void modules_callback(
+        GConfClient* client,
+        guint cnxn_id,
+        GConfEntry *entry,
+        gpointer user_data) {
+
+    const char *n;
+    char buf[128];
+
+    g_assert(strncmp(entry->key, PA_GCONF_PATH_MODULES"/", sizeof(PA_GCONF_PATH_MODULES)) == 0);
+
+    n = entry->key + sizeof(PA_GCONF_PATH_MODULES);
+
+    g_strlcpy(buf, n, sizeof(buf));
+    buf[strcspn(buf, "/")] = 0;
+
+    handle_module(client, buf);
+}
+
+int main(int argc, char *argv[]) {
+    GMainLoop *g;
+    GConfClient *client;
+    GSList *modules, *m;
+
+    g_type_init();
+
+    if (!(client = gconf_client_get_default()))
+        goto fail;
+
+    gconf_client_add_dir(client, PA_GCONF_ROOT, GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
+    gconf_client_notify_add(client, PA_GCONF_PATH_MODULES, modules_callback, NULL, NULL, NULL);
+
+    modules = gconf_client_all_dirs(client, PA_GCONF_PATH_MODULES, NULL);
+
+    for (m = modules; m; m = m->next) {
+        char *e = strrchr(m->data, '/');
+        handle_module(client, e ? e+1 : m->data);
+    }
+
+    g_slist_free(modules);
+
+    /* Signal the parent that we are now initialized */
+    printf("!");
+    fflush(stdout);
+
+    g = g_main_loop_new(NULL, FALSE);
+    g_main_loop_run(g);
+    g_main_loop_unref(g);
+
+    g_object_unref(G_OBJECT(client));
+
+    return 0;
+
+fail:
+    return 1;
+}
diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c
new file mode 100644 (file)
index 0000000..a2a4327
--- /dev/null
@@ -0,0 +1,395 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulse/mainloop-api.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/start-child.h>
+
+#include "module-gconf-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("GConf Adapter");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define MAX_MODULES 10
+#define BUF_MAX 2048
+
+/* #undef PA_GCONF_HELPER */
+/* #define PA_GCONF_HELPER "/home/lennart/projects/pulseaudio/src/gconf-helper" */
+
+struct module_item {
+    char *name;
+    char *args;
+    uint32_t index;
+};
+
+struct module_info {
+    char *name;
+
+    struct module_item items[MAX_MODULES];
+    unsigned n_items;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_hashmap *module_infos;
+
+    pid_t pid;
+
+    int fd;
+    int fd_type;
+    pa_io_event *io_event;
+
+    char buf[BUF_MAX];
+    size_t buf_fill;
+};
+
+static int fill_buf(struct userdata *u) {
+    ssize_t r;
+    pa_assert(u);
+
+    if (u->buf_fill >= BUF_MAX) {
+        pa_log("read buffer overflow");
+        return -1;
+    }
+
+    if ((r = pa_read(u->fd, u->buf + u->buf_fill, BUF_MAX - u->buf_fill, &u->fd_type)) <= 0)
+        return -1;
+
+    u->buf_fill += r;
+    return 0;
+}
+
+static int read_byte(struct userdata *u) {
+    int ret;
+    pa_assert(u);
+
+    if (u->buf_fill < 1)
+        if (fill_buf(u) < 0)
+            return -1;
+
+    ret = u->buf[0];
+    pa_assert(u->buf_fill > 0);
+    u->buf_fill--;
+    memmove(u->buf, u->buf+1, u->buf_fill);
+    return ret;
+}
+
+static char *read_string(struct userdata *u) {
+    pa_assert(u);
+
+    for (;;) {
+        char *e;
+
+        if ((e = memchr(u->buf, 0, u->buf_fill))) {
+            char *ret = pa_xstrdup(u->buf);
+            u->buf_fill -= e - u->buf +1;
+            memmove(u->buf, e+1, u->buf_fill);
+            return ret;
+        }
+
+        if (fill_buf(u) < 0)
+            return NULL;
+    }
+}
+
+static void unload_one_module(struct userdata *u, struct module_info*m, unsigned i) {
+    pa_assert(u);
+    pa_assert(m);
+    pa_assert(i < m->n_items);
+
+    if (m->items[i].index == PA_INVALID_INDEX)
+        return;
+
+    pa_log_debug("Unloading module #%i", m->items[i].index);
+    pa_module_unload_by_index(u->core, m->items[i].index);
+    m->items[i].index = PA_INVALID_INDEX;
+    pa_xfree(m->items[i].name);
+    pa_xfree(m->items[i].args);
+    m->items[i].name = m->items[i].args = NULL;
+}
+
+static void unload_all_modules(struct userdata *u, struct module_info*m) {
+    unsigned i;
+
+    pa_assert(u);
+    pa_assert(m);
+
+    for (i = 0; i < m->n_items; i++)
+        unload_one_module(u, m, i);
+
+    m->n_items = 0;
+}
+
+static void load_module(
+        struct userdata *u,
+        struct module_info *m,
+        int i,
+        const char *name,
+        const char *args,
+        int is_new) {
+
+    pa_module *mod;
+
+    pa_assert(u);
+    pa_assert(m);
+    pa_assert(name);
+    pa_assert(args);
+
+    if (!is_new) {
+        if (m->items[i].index != PA_INVALID_INDEX &&
+            strcmp(m->items[i].name, name) == 0 &&
+            strcmp(m->items[i].args, args) == 0)
+            return;
+
+        unload_one_module(u, m, i);
+    }
+
+    pa_log_debug("Loading module '%s' with args '%s' due to GConf configuration.", name, args);
+
+    m->items[i].name = pa_xstrdup(name);
+    m->items[i].args = pa_xstrdup(args);
+    m->items[i].index = PA_INVALID_INDEX;
+
+    if (!(mod = pa_module_load(u->core, name, args))) {
+        pa_log("pa_module_load() failed");
+        return;
+    }
+
+    m->items[i].index = mod->index;
+}
+
+static void module_info_free(void *p, void *userdata) {
+    struct module_info *m = p;
+    struct userdata *u = userdata;
+
+    pa_assert(m);
+    pa_assert(u);
+
+    unload_all_modules(u, m);
+    pa_xfree(m->name);
+    pa_xfree(m);
+}
+
+static int handle_event(struct userdata *u) {
+    int opcode;
+    int ret = 0;
+
+    do {
+        if ((opcode = read_byte(u)) < 0){
+            if (errno == EINTR || errno == EAGAIN)
+                break;
+            goto fail;
+        }
+
+        switch (opcode) {
+            case '!':
+                /* The helper tool is now initialized */
+                ret = 1;
+                break;
+
+            case '+': {
+                char *name;
+                struct module_info *m;
+                unsigned i, j;
+
+                if (!(name = read_string(u)))
+                    goto fail;
+
+                if (!(m = pa_hashmap_get(u->module_infos, name))) {
+                    m = pa_xnew(struct module_info, 1);
+                    m->name = name;
+                    m->n_items = 0;
+                    pa_hashmap_put(u->module_infos, m->name, m);
+                } else
+                    pa_xfree(name);
+
+                i = 0;
+                while (i < MAX_MODULES) {
+                    char *module, *args;
+
+                    if (!(module = read_string(u))) {
+                        if (i > m->n_items) m->n_items = i;
+                        goto fail;
+                    }
+
+                    if (!*module) {
+                        pa_xfree(module);
+                        break;
+                    }
+
+                    if (!(args = read_string(u))) {
+                        pa_xfree(module);
+
+                        if (i > m->n_items) m->n_items = i;
+                        goto fail;
+                    }
+
+                    load_module(u, m, i, module, args, i >= m->n_items);
+
+                    i++;
+
+                    pa_xfree(module);
+                    pa_xfree(args);
+                }
+
+                /* Unload all removed modules */
+                for (j = i; j < m->n_items; j++)
+                    unload_one_module(u, m, j);
+
+                m->n_items = i;
+
+                break;
+            }
+
+            case '-': {
+                char *name;
+                struct module_info *m;
+
+                if (!(name = read_string(u)))
+                    goto fail;
+
+                if ((m = pa_hashmap_get(u->module_infos, name))) {
+                    pa_hashmap_remove(u->module_infos, name);
+                    module_info_free(m, u);
+                }
+
+                pa_xfree(name);
+
+                break;
+            }
+        }
+    } while (u->buf_fill > 0 && ret == 0);
+
+    return ret;
+
+fail:
+    pa_log("Unable to read or parse data from client.");
+    return -1;
+}
+
+static void io_event_cb(
+        pa_mainloop_api*a,
+        pa_io_event* e,
+        int fd,
+        pa_io_event_flags_t events,
+        void *userdata) {
+
+    struct userdata *u = userdata;
+
+    if (handle_event(u) < 0) {
+
+        if (u->io_event) {
+            u->core->mainloop->io_free(u->io_event);
+            u->io_event = NULL;
+        }
+
+        pa_module_unload_request(u->module);
+    }
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    int r;
+
+    u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->module_infos = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    u->pid = (pid_t) -1;
+    u->fd = -1;
+    u->fd_type = 0;
+    u->io_event = NULL;
+    u->buf_fill = 0;
+
+    if ((u->fd = pa_start_child_for_read(PA_GCONF_HELPER, NULL, &u->pid)) < 0)
+        goto fail;
+
+    u->io_event = m->core->mainloop->io_new(
+            m->core->mainloop,
+            u->fd,
+            PA_IO_EVENT_INPUT,
+            io_event_cb,
+            u);
+
+    do {
+        if ((r = handle_event(u)) < 0)
+            goto fail;
+
+        /* Read until the client signalled us that it is ready with
+         * initialization */
+    } while (r != 1);
+
+    return 0;
+
+fail:
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->pid != (pid_t) -1) {
+        kill(u->pid, SIGTERM);
+        waitpid(u->pid, NULL, 0);
+    }
+
+    if (u->io_event)
+        m->core->mainloop->io_free(u->io_event);
+
+    if (u->fd >= 0)
+        pa_close(u->fd);
+
+
+    if (u->module_infos)
+        pa_hashmap_free(u->module_infos, module_info_free, u);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/ladspa.h b/src/modules/ladspa.h
new file mode 100644 (file)
index 0000000..b1a9c4e
--- /dev/null
@@ -0,0 +1,603 @@
+/* ladspa.h
+
+   Linux Audio Developer's Simple Plugin API Version 1.1[LGPL].
+   Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis,
+   Stefan Westerfeld.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+   USA. */
+
+#ifndef LADSPA_INCLUDED
+#define LADSPA_INCLUDED
+
+#define LADSPA_VERSION "1.1"
+#define LADSPA_VERSION_MAJOR 1
+#define LADSPA_VERSION_MINOR 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+
+/* Overview:
+
+   There is a large number of synthesis packages in use or development
+   on the Linux platform at this time. This API (`The Linux Audio
+   Developer's Simple Plugin API') attempts to give programmers the
+   ability to write simple `plugin' audio processors in C/C++ and link
+   them dynamically (`plug') into a range of these packages (`hosts').
+   It should be possible for any host and any plugin to communicate
+   completely through this interface.
+
+   This API is deliberately short and simple. To achieve compatibility
+   with a range of promising Linux sound synthesis packages it
+   attempts to find the `greatest common divisor' in their logical
+   behaviour. Having said this, certain limiting decisions are
+   implicit, notably the use of a fixed type (LADSPA_Data) for all
+   data transfer and absence of a parameterised `initialisation'
+   phase. See below for the LADSPA_Data typedef.
+
+   Plugins are expected to distinguish between control and audio
+   data. Plugins have `ports' that are inputs or outputs for audio or
+   control data and each plugin is `run' for a `block' corresponding
+   to a short time interval measured in samples. Audio data is
+   communicated using arrays of LADSPA_Data, allowing a block of audio
+   to be processed by the plugin in a single pass. Control data is
+   communicated using single LADSPA_Data values. Control data has a
+   single value at the start of a call to the `run()' or `run_adding()'
+   function, and may be considered to remain this value for its
+   duration. The plugin may assume that all its input and output ports
+   have been connected to the relevant data location (see the
+   `connect_port()' function below) before it is asked to run.
+
+   Plugins will reside in shared object files suitable for dynamic
+   linking by dlopen() and family. The file will provide a number of
+   `plugin types' that can be used to instantiate actual plugins
+   (sometimes known as `plugin instances') that can be connected
+   together to perform tasks.
+
+   This API contains very limited error-handling. */
+
+/*****************************************************************************/
+
+/* Fundamental data type passed in and out of plugin. This data type
+   is used to communicate audio samples and control values. It is
+   assumed that the plugin will work sensibly given any numeric input
+   value although it may have a preferred range (see hints below).
+
+   For audio it is generally assumed that 1.0f is the `0dB' reference
+   amplitude and is a `normal' signal level. */
+
+typedef float LADSPA_Data;
+
+/*****************************************************************************/
+
+/* Special Plugin Properties:
+
+   Optional features of the plugin type are encapsulated in the
+   LADSPA_Properties type. This is assembled by ORing individual
+   properties together. */
+
+typedef int LADSPA_Properties;
+
+/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a
+   real-time dependency (e.g. listens to a MIDI device) and so its
+   output must not be cached or subject to significant latency. */
+#define LADSPA_PROPERTY_REALTIME        0x1
+
+/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin
+   may cease to work correctly if the host elects to use the same data
+   location for both input and output (see connect_port()). This
+   should be avoided as enabling this flag makes it impossible for
+   hosts to use the plugin to process audio `in-place.' */
+#define LADSPA_PROPERTY_INPLACE_BROKEN  0x2
+
+/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin
+   is capable of running not only in a conventional host but also in a
+   `hard real-time' environment. To qualify for this the plugin must
+   satisfy all of the following:
+
+   (1) The plugin must not use malloc(), free() or other heap memory
+   management within its run() or run_adding() functions. All new
+   memory used in run() must be managed via the stack. These
+   restrictions only apply to the run() function.
+
+   (2) The plugin will not attempt to make use of any library
+   functions with the exceptions of functions in the ANSI standard C
+   and C maths libraries, which the host is expected to provide.
+
+   (3) The plugin will not access files, devices, pipes, sockets, IPC
+   or any other mechanism that might result in process or thread
+   blocking.
+
+   (4) The plugin will take an amount of time to execute a run() or
+   run_adding() call approximately of form (A+B*SampleCount) where A
+   and B depend on the machine and host in use. This amount of time
+   may not depend on input signals or plugin state. The host is left
+   the responsibility to perform timings to estimate upper bounds for
+   A and B. */
+#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4
+
+#define LADSPA_IS_REALTIME(x)        ((x) & LADSPA_PROPERTY_REALTIME)
+#define LADSPA_IS_INPLACE_BROKEN(x)  ((x) & LADSPA_PROPERTY_INPLACE_BROKEN)
+#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE)
+
+/*****************************************************************************/
+
+/* Plugin Ports:
+
+   Plugins have `ports' that are inputs or outputs for audio or
+   data. Ports can communicate arrays of LADSPA_Data (for audio
+   inputs/outputs) or single LADSPA_Data values (for control
+   input/outputs). This information is encapsulated in the
+   LADSPA_PortDescriptor type which is assembled by ORing individual
+   properties together.
+
+   Note that a port must be an input or an output port but not both
+   and that a port must be a control or audio port but not both. */
+
+typedef int LADSPA_PortDescriptor;
+
+/* Property LADSPA_PORT_INPUT indicates that the port is an input. */
+#define LADSPA_PORT_INPUT   0x1
+
+/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */
+#define LADSPA_PORT_OUTPUT  0x2
+
+/* Property LADSPA_PORT_CONTROL indicates that the port is a control
+   port. */
+#define LADSPA_PORT_CONTROL 0x4
+
+/* Property LADSPA_PORT_AUDIO indicates that the port is a audio
+   port. */
+#define LADSPA_PORT_AUDIO   0x8
+
+#define LADSPA_IS_PORT_INPUT(x)   ((x) & LADSPA_PORT_INPUT)
+#define LADSPA_IS_PORT_OUTPUT(x)  ((x) & LADSPA_PORT_OUTPUT)
+#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL)
+#define LADSPA_IS_PORT_AUDIO(x)   ((x) & LADSPA_PORT_AUDIO)
+
+/*****************************************************************************/
+
+/* Plugin Port Range Hints:
+
+   The host may wish to provide a representation of data entering or
+   leaving a plugin (e.g. to generate a GUI automatically). To make
+   this more meaningful, the plugin should provide `hints' to the host
+   describing the usual values taken by the data.
+
+   Note that these are only hints. The host may ignore them and the
+   plugin must not assume that data supplied to it is meaningful. If
+   the plugin receives invalid input data it is expected to continue
+   to run without failure and, where possible, produce a sensible
+   output (e.g. a high-pass filter given a negative cutoff frequency
+   might switch to an all-pass mode).
+
+   Hints are meaningful for all input and output ports but hints for
+   input control ports are expected to be particularly useful.
+
+   More hint information is encapsulated in the
+   LADSPA_PortRangeHintDescriptor type which is assembled by ORing
+   individual hint types together. Hints may require further
+   LowerBound and UpperBound information.
+
+   All the hint information for a particular port is aggregated in the
+   LADSPA_PortRangeHint structure. */
+
+typedef int LADSPA_PortRangeHintDescriptor;
+
+/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field
+   of the LADSPA_PortRangeHint should be considered meaningful. The
+   value in this field should be considered the (inclusive) lower
+   bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+   specified then the value of LowerBound should be multiplied by the
+   sample rate. */
+#define LADSPA_HINT_BOUNDED_BELOW   0x1
+
+/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field
+   of the LADSPA_PortRangeHint should be considered meaningful. The
+   value in this field should be considered the (inclusive) upper
+   bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+   specified then the value of UpperBound should be multiplied by the
+   sample rate. */
+#define LADSPA_HINT_BOUNDED_ABOVE   0x2
+
+/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be
+   considered a Boolean toggle. Data less than or equal to zero should
+   be considered `off' or `false,' and data above zero should be
+   considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in
+   conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or
+   LADSPA_HINT_DEFAULT_1. */
+#define LADSPA_HINT_TOGGLED         0x4
+
+/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified
+   should be interpreted as multiples of the sample rate. For
+   instance, a frequency range from 0Hz to the Nyquist frequency (half
+   the sample rate) could be requested by this hint in conjunction
+   with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
+   at all must support this hint to retain meaning. */
+#define LADSPA_HINT_SAMPLE_RATE     0x8
+
+/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the
+   user will find it more intuitive to view values using a logarithmic
+   scale. This is particularly useful for frequencies and gains. */
+#define LADSPA_HINT_LOGARITHMIC     0x10
+
+/* Hint LADSPA_HINT_INTEGER indicates that a user interface would
+   probably wish to provide a stepped control taking only integer
+   values. Any bounds set should be slightly wider than the actual
+   integer range required to avoid floating point rounding errors. For
+   instance, the integer set {0,1,2,3} might be described as [-0.1,
+   3.1]. */
+#define LADSPA_HINT_INTEGER         0x20
+
+/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal'
+   value for the port that is sensible as a default. For instance,
+   this value is suitable for use as an initial value in a user
+   interface or as a value the host might assign to a control port
+   when the user has not provided one. Defaults are encoded using a
+   mask so only one default may be specified for a port. Some of the
+   hints make use of lower and upper bounds, in which case the
+   relevant bound or bounds must be available and
+   LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting
+   default must be rounded if LADSPA_HINT_INTEGER is present. Default
+   values were introduced in LADSPA v1.1. */
+#define LADSPA_HINT_DEFAULT_MASK    0x3C0
+
+/* This default values indicates that no default is provided. */
+#define LADSPA_HINT_DEFAULT_NONE    0x0
+
+/* This default hint indicates that the suggested lower bound for the
+   port should be used. */
+#define LADSPA_HINT_DEFAULT_MINIMUM 0x40
+
+/* This default hint indicates that a low value between the suggested
+   lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 +
+   log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper
+   * 0.25). */
+#define LADSPA_HINT_DEFAULT_LOW     0x80
+
+/* This default hint indicates that a middle value between the
+   suggested lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 +
+   log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper *
+   0.5). */
+#define LADSPA_HINT_DEFAULT_MIDDLE  0xC0
+
+/* This default hint indicates that a high value between the suggested
+   lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 +
+   log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper
+   * 0.75). */
+#define LADSPA_HINT_DEFAULT_HIGH    0x100
+
+/* This default hint indicates that the suggested upper bound for the
+   port should be used. */
+#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140
+
+/* This default hint indicates that the number 0 should be used. Note
+   that this default may be used in conjunction with
+   LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_0       0x200
+
+/* This default hint indicates that the number 1 should be used. Note
+   that this default may be used in conjunction with
+   LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_1       0x240
+
+/* This default hint indicates that the number 100 should be used. */
+#define LADSPA_HINT_DEFAULT_100     0x280
+
+/* This default hint indicates that the Hz frequency of `concert A'
+   should be used. This will be 440 unless the host uses an unusual
+   tuning convention, in which case it may be within a few Hz. */
+#define LADSPA_HINT_DEFAULT_440     0x2C0
+
+#define LADSPA_IS_HINT_BOUNDED_BELOW(x)   ((x) & LADSPA_HINT_BOUNDED_BELOW)
+#define LADSPA_IS_HINT_BOUNDED_ABOVE(x)   ((x) & LADSPA_HINT_BOUNDED_ABOVE)
+#define LADSPA_IS_HINT_TOGGLED(x)         ((x) & LADSPA_HINT_TOGGLED)
+#define LADSPA_IS_HINT_SAMPLE_RATE(x)     ((x) & LADSPA_HINT_SAMPLE_RATE)
+#define LADSPA_IS_HINT_LOGARITHMIC(x)     ((x) & LADSPA_HINT_LOGARITHMIC)
+#define LADSPA_IS_HINT_INTEGER(x)         ((x) & LADSPA_HINT_INTEGER)
+
+#define LADSPA_IS_HINT_HAS_DEFAULT(x)     ((x) & LADSPA_HINT_DEFAULT_MASK)
+#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MINIMUM)
+#define LADSPA_IS_HINT_DEFAULT_LOW(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_LOW)
+#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x)  (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MIDDLE)
+#define LADSPA_IS_HINT_DEFAULT_HIGH(x)    (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_HIGH)
+#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MAXIMUM)
+#define LADSPA_IS_HINT_DEFAULT_0(x)       (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_0)
+#define LADSPA_IS_HINT_DEFAULT_1(x)       (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_1)
+#define LADSPA_IS_HINT_DEFAULT_100(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_100)
+#define LADSPA_IS_HINT_DEFAULT_440(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                            == LADSPA_HINT_DEFAULT_440)
+
+typedef struct _LADSPA_PortRangeHint {
+
+  /* Hints about the port. */
+  LADSPA_PortRangeHintDescriptor HintDescriptor;
+
+  /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When
+     LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+     multiplied by the relevant sample rate. */
+  LADSPA_Data LowerBound;
+
+  /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When
+     LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+     multiplied by the relevant sample rate. */
+  LADSPA_Data UpperBound;
+
+} LADSPA_PortRangeHint;
+
+/*****************************************************************************/
+
+/* Plugin Handles:
+
+   This plugin handle indicates a particular instance of the plugin
+   concerned. It is valid to compare this to NULL (0 for C++) but
+   otherwise the host should not attempt to interpret it. The plugin
+   may use it to reference internal instance data. */
+
+typedef void * LADSPA_Handle;
+
+/*****************************************************************************/
+
+/* Descriptor for a Type of Plugin:
+
+   This structure is used to describe a plugin type. It provides a
+   number of functions to examine the type, instantiate it, link it to
+   buffers and workspaces and to run it. */
+
+typedef struct _LADSPA_Descriptor {
+
+  /* This numeric identifier indicates the plugin type
+     uniquely. Plugin programmers may reserve ranges of IDs from a
+     central body to avoid clashes. Hosts may assume that IDs are
+     below 0x1000000. */
+  unsigned long UniqueID;
+
+  /* This identifier can be used as a unique, case-sensitive
+     identifier for the plugin type within the plugin file. Plugin
+     types should be identified by file and label rather than by index
+     or plugin name, which may be changed in new plugin
+     versions. Labels must not contain white-space characters. */
+  const char * Label;
+
+  /* This indicates a number of properties of the plugin. */
+  LADSPA_Properties Properties;
+
+  /* This member points to the null-terminated name of the plugin
+     (e.g. "Sine Oscillator"). */
+  const char * Name;
+
+  /* This member points to the null-terminated string indicating the
+     maker of the plugin. This can be an empty string but not NULL. */
+  const char * Maker;
+
+  /* This member points to the null-terminated string indicating any
+     copyright applying to the plugin. If no Copyright applies the
+     string "None" should be used. */
+  const char * Copyright;
+
+  /* This indicates the number of ports (input AND output) present on
+     the plugin. */
+  unsigned long PortCount;
+
+  /* This member indicates an array of port descriptors. Valid indices
+     vary from 0 to PortCount-1. */
+  const LADSPA_PortDescriptor * PortDescriptors;
+
+  /* This member indicates an array of null-terminated strings
+     describing ports (e.g. "Frequency (Hz)"). Valid indices vary from
+     0 to PortCount-1. */
+  const char * const * PortNames;
+
+  /* This member indicates an array of range hints for each port (see
+     above). Valid indices vary from 0 to PortCount-1. */
+  const LADSPA_PortRangeHint * PortRangeHints;
+
+  /* This may be used by the plugin developer to pass any custom
+     implementation data into an instantiate call. It must not be used
+     or interpreted by the host. It is expected that most plugin
+     writers will not use this facility as LADSPA_Handle should be
+     used to hold instance data. */
+  void * ImplementationData;
+
+  /* This member is a function pointer that instantiates a plugin. A
+     handle is returned indicating the new plugin instance. The
+     instantiation function accepts a sample rate as a parameter. The
+     plugin descriptor from which this instantiate function was found
+     must also be passed. This function must return NULL if
+     instantiation fails.
+
+     Note that instance initialisation should generally occur in
+     activate() rather than here. */
+  LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor,
+                               unsigned long                     SampleRate);
+
+  /* This member is a function pointer that connects a port on an
+     instantiated plugin to a memory location at which a block of data
+     for the port will be read/written. The data location is expected
+     to be an array of LADSPA_Data for audio ports or a single
+     LADSPA_Data value for control ports. Memory issues will be
+     managed by the host. The plugin must read/write the data at these
+     locations every time run() or run_adding() is called and the data
+     present at the time of this connection call should not be
+     considered meaningful.
+
+     connect_port() may be called more than once for a plugin instance
+     to allow the host to change the buffers that the plugin is
+     reading or writing. These calls may be made before or after
+     activate() or deactivate() calls.
+
+     connect_port() must be called at least once for each port before
+     run() or run_adding() is called. When working with blocks of
+     LADSPA_Data the plugin should pay careful attention to the block
+     size passed to the run function as the block allocated may only
+     just be large enough to contain the block of samples.
+
+     Plugin writers should be aware that the host may elect to use the
+     same buffer for more than one port and even use the same buffer
+     for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN).
+     However, overlapped buffers or use of a single buffer for both
+     audio and control data may result in unexpected behaviour. */
+   void (*connect_port)(LADSPA_Handle Instance,
+                        unsigned long Port,
+                        LADSPA_Data * DataLocation);
+
+  /* This member is a function pointer that initialises a plugin
+     instance and activates it for use. This is separated from
+     instantiate() to aid real-time support and so that hosts can
+     reinitialise a plugin instance by calling deactivate() and then
+     activate(). In this case the plugin instance must reset all state
+     information dependent on the history of the plugin instance
+     except for any data locations provided by connect_port() and any
+     gain set by set_run_adding_gain(). If there is nothing for
+     activate() to do then the plugin writer may provide a NULL rather
+     than an empty function.
+
+     When present, hosts must call this function once before run() (or
+     run_adding()) is called for the first time. This call should be
+     made as close to the run() call as possible and indicates to
+     real-time plugins that they are now live. Plugins should not rely
+     on a prompt call to run() after activate(). activate() may not be
+     called again unless deactivate() is called first. Note that
+     connect_port() may be called before or after a call to
+     activate(). */
+  void (*activate)(LADSPA_Handle Instance);
+
+  /* This method is a function pointer that runs an instance of a
+     plugin for a block. Two parameters are required: the first is a
+     handle to the particular instance to be run and the second
+     indicates the block size (in samples) for which the plugin
+     instance may run.
+
+     Note that if an activate() function exists then it must be called
+     before run() or run_adding(). If deactivate() is called for a
+     plugin instance then the plugin instance may not be reused until
+     activate() has been called again.
+
+     If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE
+     then there are various things that the plugin should not do
+     within the run() or run_adding() functions (see above). */
+  void (*run)(LADSPA_Handle Instance,
+              unsigned long SampleCount);
+
+  /* This method is a function pointer that runs an instance of a
+     plugin for a block. This has identical behaviour to run() except
+     in the way data is output from the plugin. When run() is used,
+     values are written directly to the memory areas associated with
+     the output ports. However when run_adding() is called, values
+     must be added to the values already present in the memory
+     areas. Furthermore, output values written must be scaled by the
+     current gain set by set_run_adding_gain() (see below) before
+     addition.
+
+     run_adding() is optional. When it is not provided by a plugin,
+     this function pointer must be set to NULL. When it is provided,
+     the function set_run_adding_gain() must be provided also. */
+  void (*run_adding)(LADSPA_Handle Instance,
+                     unsigned long SampleCount);
+
+  /* This method is a function pointer that sets the output gain for
+     use when run_adding() is called (see above). If this function is
+     never called the gain is assumed to default to 1. Gain
+     information should be retained when activate() or deactivate()
+     are called.
+
+     This function should be provided by the plugin if and only if the
+     run_adding() function is provided. When it is absent this
+     function pointer must be set to NULL. */
+  void (*set_run_adding_gain)(LADSPA_Handle Instance,
+                              LADSPA_Data   Gain);
+
+  /* This is the counterpart to activate() (see above). If there is
+     nothing for deactivate() to do then the plugin writer may provide
+     a NULL rather than an empty function.
+
+     Hosts must deactivate all activated units after they have been
+     run() (or run_adding()) for the last time. This call should be
+     made as close to the last run() call as possible and indicates to
+     real-time plugins that they are no longer live. Plugins should
+     not rely on prompt deactivation. Note that connect_port() may be
+     called before or after a call to deactivate().
+
+     Deactivation is not similar to pausing as the plugin instance
+     will be reinitialised when activate() is called to reuse it. */
+  void (*deactivate)(LADSPA_Handle Instance);
+
+  /* Once an instance of a plugin has been finished with it can be
+     deleted using the following function. The instance handle passed
+     ceases to be valid after this call.
+
+     If activate() was called for a plugin instance then a
+     corresponding call to deactivate() must be made before cleanup()
+     is called. */
+  void (*cleanup)(LADSPA_Handle Instance);
+
+} LADSPA_Descriptor;
+
+/**********************************************************************/
+
+/* Accessing a Plugin: */
+
+/* The exact mechanism by which plugins are loaded is host-dependent,
+   however all most hosts will need to know is the name of shared
+   object file containing the plugin types. To allow multiple hosts to
+   share plugin types, hosts may wish to check for environment
+   variable LADSPA_PATH. If present, this should contain a
+   colon-separated path indicating directories that should be searched
+   (in order) when loading plugin types.
+
+   A plugin programmer must include a function called
+   "ladspa_descriptor" with the following function prototype within
+   the shared object file. This function will have C-style linkage (if
+   you are using C++ this is taken care of by the `extern "C"' clause
+   at the top of the file).
+
+   A host will find the plugin shared object file by one means or
+   another, find the ladspa_descriptor() function, call it, and
+   proceed from there.
+
+   Plugin types are accessed by index (not ID) using values from 0
+   upwards. Out of range indexes must result in this function
+   returning NULL, so the plugin count can be determined by checking
+   for the least index that results in NULL being returned. */
+
+const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index);
+
+/* Datatype corresponding to the ladspa_descriptor() function. */
+typedef const LADSPA_Descriptor *
+(*LADSPA_Descriptor_Function)(unsigned long Index);
+
+/**********************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LADSPA_INCLUDED */
+
+/* EOF */
diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c
new file mode 100644 (file)
index 0000000..6765775
--- /dev/null
@@ -0,0 +1,1518 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <asoundlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/time-smoother.h>
+
+#include "alsa-util.h"
+#include "module-alsa-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "device=<ALSA device> "
+        "device_id=<ALSA card index> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map> "
+        "fragments=<number of fragments> "
+        "fragment_size=<fragment size> "
+        "mmap=<enable memory mapping?> "
+        "tsched=<enable system timer based scheduling mode?> "
+        "tsched_buffer_size=<buffer size when using timer based scheduling> "
+        "tsched_buffer_watermark=<lower fill watermark> "
+        "mixer_reset=<reset hw volume and mute settings to sane defaults when falling back to software?>");
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "device",
+    "device_id",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "fragments",
+    "fragment_size",
+    "mmap",
+    "tsched",
+    "tsched_buffer_size",
+    "tsched_buffer_watermark",
+    "mixer_reset",
+    NULL
+};
+
+#define DEFAULT_DEVICE "default"
+#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)            /* 2s */
+#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)       /* 20ms */
+#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC)                /* 3ms */
+#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC)               /* 3ms */
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    snd_pcm_t *pcm_handle;
+
+    pa_alsa_fdlist *mixer_fdl;
+    snd_mixer_t *mixer_handle;
+    snd_mixer_elem_t *mixer_elem;
+    long hw_volume_max, hw_volume_min;
+    long hw_dB_max, hw_dB_min;
+    pa_bool_t hw_dB_supported;
+
+    size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;
+    unsigned nfragments;
+    pa_memchunk memchunk;
+
+    char *device_name;
+
+    pa_bool_t use_mmap, use_tsched;
+
+    pa_bool_t first, after_rewind;
+
+    pa_rtpoll_item *alsa_rtpoll_item;
+
+    snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST];
+
+    pa_smoother *smoother;
+    int64_t frame_index;
+    uint64_t since_start;
+
+    snd_pcm_sframes_t hwbuf_unused_frames;
+};
+
+static void fix_tsched_watermark(struct userdata *u) {
+    size_t max_use;
+    size_t min_sleep, min_wakeup;
+    pa_assert(u);
+
+    max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size;
+
+    min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->sink->sample_spec);
+    min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->sink->sample_spec);
+
+    if (min_sleep > max_use/2)
+        min_sleep = pa_frame_align(max_use/2, &u->sink->sample_spec);
+    if (min_sleep < u->frame_size)
+        min_sleep = u->frame_size;
+
+    if (min_wakeup > max_use/2)
+        min_wakeup = pa_frame_align(max_use/2, &u->sink->sample_spec);
+    if (min_wakeup < u->frame_size)
+        min_wakeup = u->frame_size;
+
+    if (u->tsched_watermark > max_use-min_sleep)
+        u->tsched_watermark = max_use-min_sleep;
+
+    if (u->tsched_watermark < min_wakeup)
+        u->tsched_watermark = min_wakeup;
+}
+
+static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
+    pa_usec_t usec, wm;
+
+    pa_assert(sleep_usec);
+    pa_assert(process_usec);
+
+    pa_assert(u);
+
+    usec = pa_sink_get_requested_latency_within_thread(u->sink);
+
+    if (usec == (pa_usec_t) -1)
+        usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec);
+
+/*     pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
+
+    wm = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec);
+
+    if (usec >= wm) {
+        *sleep_usec = usec - wm;
+        *process_usec = wm;
+    } else
+        *process_usec = *sleep_usec = usec / 2;
+
+/*     pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
+}
+
+static int try_recover(struct userdata *u, const char *call, int err) {
+    pa_assert(u);
+    pa_assert(call);
+    pa_assert(err < 0);
+
+    pa_log_debug("%s: %s", call, snd_strerror(err));
+
+    pa_assert(err != -EAGAIN);
+
+    if (err == -EPIPE)
+        pa_log_debug("%s: Buffer underrun!", call);
+
+    if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
+        u->first = TRUE;
+        u->since_start = 0;
+        return 0;
+    }
+
+    pa_log("%s: %s", call, snd_strerror(err));
+    return -1;
+}
+
+static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) {
+    size_t left_to_play;
+
+    if (n*u->frame_size < u->hwbuf_size)
+        left_to_play = u->hwbuf_size - (n*u->frame_size);
+    else
+        left_to_play = 0;
+
+    if (left_to_play > 0) {
+/*         pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); */
+    } else if (!u->first && !u->after_rewind) {
+        pa_log_info("Underrun!");
+
+        if (u->use_tsched) {
+            size_t old_watermark = u->tsched_watermark;
+
+            u->tsched_watermark *= 2;
+            fix_tsched_watermark(u);
+
+            if (old_watermark != u->tsched_watermark)
+                pa_log_notice("Increasing wakeup watermark to %0.2f ms",
+                              (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
+        }
+    }
+
+    return left_to_play;
+}
+
+static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {
+    int work_done = 0;
+    pa_usec_t max_sleep_usec, process_usec;
+    size_t left_to_play;
+
+    pa_assert(u);
+    pa_sink_assert_ref(u->sink);
+
+    if (u->use_tsched)
+        hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+    for (;;) {
+        snd_pcm_sframes_t n;
+        int r;
+
+        snd_pcm_hwsync(u->pcm_handle);
+
+        /* First we determine how many samples are missing to fill the
+         * buffer up to 100% */
+
+        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+
+            if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
+                continue;
+
+            return r;
+        }
+
+        left_to_play = check_left_to_play(u, n);
+
+        if (u->use_tsched)
+
+            /* We won't fill up the playback buffer before at least
+            * half the sleep time is over because otherwise we might
+            * ask for more data from the clients then they expect. We
+            * need to guarantee that clients only have to keep around
+            * a single hw buffer length. */
+
+            if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
+                break;
+
+        if (PA_UNLIKELY(n <= u->hwbuf_unused_frames))
+            break;
+
+        n -= u->hwbuf_unused_frames;
+
+/*         pa_log_debug("Filling up"); */
+
+        for (;;) {
+            pa_memchunk chunk;
+            void *p;
+            int err;
+            const snd_pcm_channel_area_t *areas;
+            snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;
+
+/*             pa_log_debug("%lu frames to write", (unsigned long) frames); */
+
+            if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
+
+                if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            /* Make sure that if these memblocks need to be copied they will fit into one slot */
+            if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size)
+                frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size;
+
+            /* Check these are multiples of 8 bit */
+            pa_assert((areas[0].first & 7) == 0);
+            pa_assert((areas[0].step & 7)== 0);
+
+            /* We assume a single interleaved memory buffer */
+            pa_assert((areas[0].first >> 3) == 0);
+            pa_assert((areas[0].step >> 3) == u->frame_size);
+
+            p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+
+            chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
+            chunk.length = pa_memblock_get_length(chunk.memblock);
+            chunk.index = 0;
+
+            pa_sink_render_into_full(u->sink, &chunk);
+
+            /* FIXME: Maybe we can do something to keep this memory block
+             * a little bit longer around? */
+            pa_memblock_unref_fixed(chunk.memblock);
+
+            if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
+
+                if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            work_done = 1;
+
+            u->frame_index += frames;
+            u->since_start += frames * u->frame_size;
+
+/*             pa_log_debug("wrote %lu frames", (unsigned long) frames); */
+
+            if (frames >= (snd_pcm_uframes_t) n)
+                break;
+
+            n -= frames;
+        }
+    }
+
+    *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec;
+    return work_done;
+}
+
+static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {
+    int work_done = 0;
+    pa_usec_t max_sleep_usec, process_usec;
+    size_t left_to_play;
+
+    pa_assert(u);
+    pa_sink_assert_ref(u->sink);
+
+    if (u->use_tsched)
+        hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+    for (;;) {
+        snd_pcm_sframes_t n;
+        int r;
+
+        snd_pcm_hwsync(u->pcm_handle);
+
+        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+
+            if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
+                continue;
+
+            return r;
+        }
+
+        left_to_play = check_left_to_play(u, n);
+
+        if (u->use_tsched)
+
+            /* We won't fill up the playback buffer before at least
+            * half the sleep time is over because otherwise we might
+            * ask for more data from the clients then they expect. We
+            * need to guarantee that clients only have to keep around
+            * a single hw buffer length. */
+
+            if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
+                break;
+
+        if (PA_UNLIKELY(n <= u->hwbuf_unused_frames))
+            break;
+
+        n -= u->hwbuf_unused_frames;
+
+        for (;;) {
+            snd_pcm_sframes_t frames;
+            void *p;
+
+/*         pa_log_debug("%lu frames to write", (unsigned long) frames); */
+
+            if (u->memchunk.length <= 0)
+                pa_sink_render(u->sink, n * u->frame_size, &u->memchunk);
+
+            pa_assert(u->memchunk.length > 0);
+
+            frames = u->memchunk.length / u->frame_size;
+
+            if (frames > n)
+                frames = n;
+
+            p = pa_memblock_acquire(u->memchunk.memblock);
+            frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, frames);
+            pa_memblock_release(u->memchunk.memblock);
+
+            pa_assert(frames != 0);
+
+            if (PA_UNLIKELY(frames < 0)) {
+
+                if ((r = try_recover(u, "snd_pcm_writei", n)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            u->memchunk.index += frames * u->frame_size;
+            u->memchunk.length -= frames * u->frame_size;
+
+            if (u->memchunk.length <= 0) {
+                pa_memblock_unref(u->memchunk.memblock);
+                pa_memchunk_reset(&u->memchunk);
+            }
+
+            work_done = 1;
+
+            u->frame_index += frames;
+            u->since_start += frames * u->frame_size;
+
+/*         pa_log_debug("wrote %lu frames", (unsigned long) frames); */
+
+            if (frames >= n)
+                break;
+
+            n -= frames;
+        }
+    }
+
+    *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec;
+    return work_done;
+}
+
+static void update_smoother(struct userdata *u) {
+    snd_pcm_sframes_t delay  = 0;
+    int64_t frames;
+    int err;
+    pa_usec_t now1, now2;
+/*     struct timeval timestamp; */
+    snd_pcm_status_t *status;
+
+    snd_pcm_status_alloca(&status);
+
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    /* Let's update the time smoother */
+
+    snd_pcm_hwsync(u->pcm_handle);
+    snd_pcm_avail_update(u->pcm_handle);
+
+/*     if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { */
+/*         pa_log("Failed to query DSP status data: %s", snd_strerror(err)); */
+/*         return; */
+/*     } */
+
+/*     delay = snd_pcm_status_get_delay(status); */
+
+    if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
+        pa_log("Failed to query DSP status data: %s", snd_strerror(err));
+        return;
+    }
+
+    frames = u->frame_index - delay;
+
+/*     pa_log_debug("frame_index = %llu, delay = %llu, p = %llu", (unsigned long long) u->frame_index, (unsigned long long) delay, (unsigned long long) frames); */
+
+/*     snd_pcm_status_get_tstamp(status, &timestamp); */
+/*     pa_rtclock_from_wallclock(&timestamp); */
+/*     now1 = pa_timeval_load(&timestamp); */
+
+    now1 = pa_rtclock_usec();
+    now2 = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec);
+    pa_smoother_put(u->smoother, now1, now2);
+}
+
+static pa_usec_t sink_get_latency(struct userdata *u) {
+    pa_usec_t r = 0;
+    int64_t delay;
+    pa_usec_t now1, now2;
+
+    pa_assert(u);
+
+    now1 = pa_rtclock_usec();
+    now2 = pa_smoother_get(u->smoother, now1);
+
+    delay = (int64_t) pa_bytes_to_usec(u->frame_index * u->frame_size, &u->sink->sample_spec) - (int64_t) now2;
+
+    if (delay > 0)
+        r = (pa_usec_t) delay;
+
+    if (u->memchunk.memblock)
+        r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
+
+    return r;
+}
+
+static int build_pollfd(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    if (u->alsa_rtpoll_item)
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+    if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll)))
+        return -1;
+
+    return 0;
+}
+
+static int suspend(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    pa_smoother_pause(u->smoother, pa_rtclock_usec());
+
+    /* Let's suspend */
+    snd_pcm_drain(u->pcm_handle);
+    snd_pcm_close(u->pcm_handle);
+    u->pcm_handle = NULL;
+
+    if (u->alsa_rtpoll_item) {
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+        u->alsa_rtpoll_item = NULL;
+    }
+
+    pa_log_info("Device suspended...");
+
+    return 0;
+}
+
+static int update_sw_params(struct userdata *u) {
+    snd_pcm_uframes_t avail_min;
+    int err;
+
+    pa_assert(u);
+
+    /* Use the full buffer if noone asked us for anything specific */
+    u->hwbuf_unused_frames = 0;
+
+    if (u->use_tsched) {
+        pa_usec_t latency;
+
+        if ((latency = pa_sink_get_requested_latency_within_thread(u->sink)) != (pa_usec_t) -1) {
+            size_t b;
+
+            pa_log_debug("latency set to %0.2f", (double) latency / PA_USEC_PER_MSEC);
+
+            b = pa_usec_to_bytes(latency, &u->sink->sample_spec);
+
+            /* We need at least one sample in our buffer */
+
+            if (PA_UNLIKELY(b < u->frame_size))
+                b = u->frame_size;
+
+            u->hwbuf_unused_frames =
+                PA_LIKELY(b < u->hwbuf_size) ?
+                ((u->hwbuf_size - b) / u->frame_size) : 0;
+
+            fix_tsched_watermark(u);
+        }
+    }
+
+    pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);
+
+    /* We need at last one frame in the used part of the buffer */
+    avail_min = u->hwbuf_unused_frames + 1;
+
+    if (u->use_tsched) {
+        pa_usec_t sleep_usec, process_usec;
+
+        hw_sleep_time(u, &sleep_usec, &process_usec);
+        avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec);
+    }
+
+    pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
+
+    if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) {
+        pa_log("Failed to set software parameters: %s", snd_strerror(err));
+        return err;
+    }
+
+    pa_sink_set_max_request(u->sink, u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size);
+
+    return 0;
+}
+
+static int unsuspend(struct userdata *u) {
+    pa_sample_spec ss;
+    int err;
+    pa_bool_t b, d;
+    unsigned nfrags;
+    snd_pcm_uframes_t period_size;
+
+    pa_assert(u);
+    pa_assert(!u->pcm_handle);
+
+    pa_log_info("Trying resume...");
+
+    snd_config_update_free_global();
+    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
+        pa_log("Error opening PCM device %s: %s", u->device_name, snd_strerror(err));
+        goto fail;
+    }
+
+    ss = u->sink->sample_spec;
+    nfrags = u->nfragments;
+    period_size = u->fragment_size / u->frame_size;
+    b = u->use_mmap;
+    d = u->use_tsched;
+
+    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) {
+        pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
+        goto fail;
+    }
+
+    if (b != u->use_mmap || d != u->use_tsched) {
+        pa_log_warn("Resume failed, couldn't get original access mode.");
+        goto fail;
+    }
+
+    if (!pa_sample_spec_equal(&ss, &u->sink->sample_spec)) {
+        pa_log_warn("Resume failed, couldn't restore original sample settings.");
+        goto fail;
+    }
+
+    if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) {
+        pa_log_warn("Resume failed, couldn't restore original fragment settings.");
+        goto fail;
+    }
+
+    if (update_sw_params(u) < 0)
+        goto fail;
+
+    if (build_pollfd(u) < 0)
+        goto fail;
+
+    /* FIXME: We need to reload the volume somehow */
+
+    u->first = TRUE;
+    u->since_start = 0;
+
+    pa_log_info("Resumed successfully...");
+
+    return 0;
+
+fail:
+    if (u->pcm_handle) {
+        snd_pcm_close(u->pcm_handle);
+        u->pcm_handle = NULL;
+    }
+
+    return -1;
+}
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t r = 0;
+
+            if (u->pcm_handle)
+                r = sink_get_latency(u);
+
+            *((pa_usec_t*) data) = r;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SINK_SUSPENDED:
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+                    if (suspend(u) < 0)
+                        return -1;
+
+                    break;
+
+                case PA_SINK_IDLE:
+                case PA_SINK_RUNNING:
+
+                    if (u->sink->thread_info.state == PA_SINK_INIT) {
+                        if (build_pollfd(u) < 0)
+                            return -1;
+                    }
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+                        if (unsuspend(u) < 0)
+                            return -1;
+                    }
+
+                    break;
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                    ;
+            }
+
+            break;
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+
+    pa_assert(u);
+    pa_assert(u->mixer_handle);
+
+    if (mask == SND_CTL_EVENT_MASK_REMOVE)
+        return 0;
+
+    if (mask & SND_CTL_EVENT_MASK_VALUE) {
+        pa_sink_get_volume(u->sink);
+        pa_sink_get_mute(u->sink);
+    }
+
+    return 0;
+}
+
+static int sink_get_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    int err;
+    int i;
+
+    pa_assert(u);
+    pa_assert(u->mixer_elem);
+
+    for (i = 0; i < s->sample_spec.channels; i++) {
+        long alsa_vol;
+
+        pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i]));
+
+        if (u->hw_dB_supported) {
+
+            if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) {
+                s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+                continue;
+            }
+
+            u->hw_dB_supported = FALSE;
+        }
+
+        if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
+            goto fail;
+
+        s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
+    }
+
+    return 0;
+
+fail:
+    pa_log_error("Unable to read volume: %s", snd_strerror(err));
+
+    return -1;
+}
+
+static int sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    int err;
+    int i;
+
+    pa_assert(u);
+    pa_assert(u->mixer_elem);
+
+    for (i = 0; i < s->sample_spec.channels; i++) {
+        long alsa_vol;
+        pa_volume_t vol;
+
+        pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i]));
+
+        vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM);
+
+        if (u->hw_dB_supported) {
+            alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
+            alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
+
+            if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) {
+
+                if (snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+                    s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+
+                continue;
+            }
+
+            u->hw_dB_supported = FALSE;
+
+        }
+
+        alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+        alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
+
+        if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0)
+            goto fail;
+
+        if (snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+            s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
+    }
+
+    return 0;
+
+fail:
+    pa_log_error("Unable to set volume: %s", snd_strerror(err));
+
+    return -1;
+}
+
+static int sink_get_mute_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    int err, sw;
+
+    pa_assert(u);
+    pa_assert(u->mixer_elem);
+
+    if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) {
+        pa_log_error("Unable to get switch: %s", snd_strerror(err));
+        return -1;
+    }
+
+    s->muted = !sw;
+
+    return 0;
+}
+
+static int sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    int err;
+
+    pa_assert(u);
+    pa_assert(u->mixer_elem);
+
+    if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) {
+        pa_log_error("Unable to set switch: %s", snd_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    snd_pcm_sframes_t before;
+    pa_assert(u);
+
+    if (!u->pcm_handle)
+        return;
+
+    before = u->hwbuf_unused_frames;
+    update_sw_params(u);
+
+    /* Let's check whether we now use only a smaller part of the
+    buffer then before. If so, we need to make sure that subsequent
+    rewinds are relative to the new maxium fill level and not to the
+    current fill level. Thus, let's do a full rewind once, to clear
+    things up. */
+
+    if (u->hwbuf_unused_frames > before) {
+        pa_log_debug("Requesting rewind due to latency change.");
+        pa_sink_request_rewind(s, 0);
+    }
+}
+
+static int process_rewind(struct userdata *u) {
+    snd_pcm_sframes_t unused;
+    size_t rewind_nbytes, unused_nbytes, limit_nbytes;
+    pa_assert(u);
+
+    /* Figure out how much we shall rewind and reset the counter */
+    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+    u->sink->thread_info.rewind_nbytes = 0;
+
+    pa_assert(rewind_nbytes > 0);
+    pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+
+    snd_pcm_hwsync(u->pcm_handle);
+    if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) {
+        pa_log("snd_pcm_avail_update() failed: %s", snd_strerror(unused));
+        return -1;
+    }
+
+    unused_nbytes = u->tsched_watermark + (size_t) unused * u->frame_size;
+
+    if (u->hwbuf_size > unused_nbytes)
+        limit_nbytes = u->hwbuf_size - unused_nbytes;
+    else
+        limit_nbytes = 0;
+
+    if (rewind_nbytes > limit_nbytes)
+        rewind_nbytes = limit_nbytes;
+
+    if (rewind_nbytes > 0) {
+        snd_pcm_sframes_t in_frames, out_frames;
+
+        pa_log_debug("Limited to %lu bytes.", (unsigned long) rewind_nbytes);
+
+        in_frames = (snd_pcm_sframes_t) rewind_nbytes / u->frame_size;
+        pa_log_debug("before: %lu", (unsigned long) in_frames);
+        if ((out_frames = snd_pcm_rewind(u->pcm_handle, in_frames)) < 0) {
+            pa_log("snd_pcm_rewind() failed: %s", snd_strerror(out_frames));
+            return -1;
+        }
+        pa_log_debug("after: %lu", (unsigned long) out_frames);
+
+        rewind_nbytes = out_frames * u->frame_size;
+
+        if (rewind_nbytes <= 0)
+            pa_log_info("Tried rewind, but was apparently not possible.");
+        else {
+            u->frame_index -= out_frames;
+            pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+            pa_sink_process_rewind(u->sink, rewind_nbytes);
+
+            u->after_rewind = TRUE;
+        }
+    } else
+        pa_log_debug("Mhmm, actually there is nothing to rewind.");
+
+    return 0;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    for (;;) {
+        int ret;
+
+/*         pa_log_debug("loop"); */
+
+        /* Render some data and write it to the dsp */
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            int work_done;
+            pa_usec_t sleep_usec;
+
+            if (u->sink->thread_info.rewind_nbytes > 0)
+                if (process_rewind(u) < 0)
+                    goto fail;
+
+            if (u->use_mmap)
+                work_done = mmap_write(u, &sleep_usec);
+            else
+                work_done = unix_write(u, &sleep_usec);
+
+            if (work_done < 0)
+                goto fail;
+
+/*             pa_log_debug("work_done = %i", work_done); */
+
+            if (work_done) {
+
+                if (u->first) {
+                    pa_log_info("Starting playback.");
+                    snd_pcm_start(u->pcm_handle);
+
+                    pa_smoother_resume(u->smoother, pa_rtclock_usec());
+                }
+
+                update_smoother(u);
+            }
+
+            if (u->use_tsched) {
+                pa_usec_t cusec;
+
+                if (u->since_start <= u->hwbuf_size) {
+
+                    /* USB devices on ALSA seem to hit a buffer
+                     * underrun during the first iterations much
+                     * quicker then we calculate here, probably due to
+                     * the transport latency. To accomodate for that
+                     * we artificially decrease the sleep time until
+                     * we have filled the buffer at least once
+                     * completely.*/
+
+                    /*pa_log_debug("Cutting sleep time for the initial iterations by half.");*/
+                    sleep_usec /= 2;
+                }
+
+                /* OK, the playback buffer is now full, let's
+                 * calculate when to wake up next */
+/*                 pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
+
+                /* Convert from the sound card time domain to the
+                 * system time domain */
+                cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec);
+
+/*                 pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
+
+                /* We don't trust the conversion, so we wake up whatever comes first */
+                pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
+            }
+
+            u->first = FALSE;
+            u->after_rewind = FALSE;
+
+        } else if (u->use_tsched)
+
+            /* OK, we're in an invalid state, let's disable our timers */
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        /* Tell ALSA about this and process its response */
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            struct pollfd *pollfd;
+            unsigned short revents = 0;
+            int err;
+            unsigned n;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, &n);
+
+            if ((err = snd_pcm_poll_descriptors_revents(u->pcm_handle, pollfd, n, &revents)) < 0) {
+                pa_log("snd_pcm_poll_descriptors_revents() failed: %s", snd_strerror(err));
+                goto fail;
+            }
+
+            if (revents & (POLLERR|POLLNVAL|POLLHUP)) {
+                if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
+                    goto fail;
+
+                u->first = TRUE;
+                u->since_start = 0;
+            }
+
+            if (revents && u->use_tsched)
+                pa_log_debug("Wakeup from ALSA! (%i)", revents);
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+
+    pa_modargs *ma = NULL;
+    struct userdata *u = NULL;
+    const char *dev_id;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
+    snd_pcm_uframes_t period_frames, tsched_frames;
+    size_t frame_size;
+    snd_pcm_info_t *pcm_info = NULL;
+    int err;
+    const char *name;
+    char *name_buf = NULL;
+    pa_bool_t namereg_fail;
+    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE;
+    pa_usec_t usec;
+    pa_sink_new_data data;
+
+    snd_pcm_info_alloca(&pcm_info);
+
+    pa_assert(m);
+
+    pa_alsa_redirect_errors_inc();
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
+        pa_log("Failed to parse sample specification and channel map");
+        goto fail;
+    }
+
+    frame_size = pa_frame_size(&ss);
+
+    nfrags = m->core->default_n_fragments;
+    frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);
+    if (frag_size <= 0)
+        frag_size = frame_size;
+    tsched_size = pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss);
+    tsched_watermark = pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);
+
+    if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||
+        pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 ||
+        pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 ||
+        pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) {
+        pa_log("Failed to parse buffer metrics");
+        goto fail;
+    }
+
+    hwbuf_size = frag_size * nfrags;
+    period_frames = frag_size/frame_size;
+    tsched_frames = tsched_size/frame_size;
+
+    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+        pa_log("Failed to parse mmap argument.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+        pa_log("Failed to parse timer_scheduling argument.");
+        goto fail;
+    }
+
+    if (use_tsched && !pa_rtclock_hrtimer()) {
+        pa_log("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
+        use_tsched = FALSE;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) {
+        pa_log("Failed to parse mixer_reset argument.");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->use_mmap = use_mmap;
+    u->use_tsched = use_tsched;
+    u->first = TRUE;
+    u->since_start = 0;
+    u->after_rewind = FALSE;
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+    u->alsa_rtpoll_item = NULL;
+
+    u->smoother = pa_smoother_new(DEFAULT_TSCHED_BUFFER_USEC*2, DEFAULT_TSCHED_BUFFER_USEC*2, TRUE, 5);
+    usec = pa_rtclock_usec();
+    pa_smoother_set_time_offset(u->smoother, usec);
+    pa_smoother_pause(u->smoother, usec);
+
+    snd_config_update_free_global();
+
+    b = use_mmap;
+    d = use_tsched;
+
+    if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+                      dev_id,
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_PLAYBACK,
+                      &nfrags, &period_frames, tsched_frames,
+                      &b, &d)))
+
+            goto fail;
+
+    } else {
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_string(
+                      pa_modargs_get_value(ma, "device", DEFAULT_DEVICE),
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_PLAYBACK,
+                      &nfrags, &period_frames, tsched_frames,
+                      &b, &d)))
+            goto fail;
+
+    }
+
+    pa_assert(u->device_name);
+    pa_log_info("Successfully opened device %s.", u->device_name);
+
+    if (use_mmap && !b) {
+        pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
+        u->use_mmap = use_mmap = FALSE;
+    }
+
+    if (use_tsched && (!b || !d)) {
+        pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling.");
+        u->use_tsched = use_tsched = FALSE;
+    }
+
+    if (u->use_mmap)
+        pa_log_info("Successfully enabled mmap() mode.");
+
+    if (u->use_tsched)
+        pa_log_info("Successfully enabled timer-based scheduling mode.");
+
+    if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
+        pa_log("Error fetching PCM info: %s", snd_strerror(err));
+        goto fail;
+    }
+
+    /* ALSA might tweak the sample spec, so recalculate the frame size */
+    frame_size = pa_frame_size(&ss);
+
+    if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)
+        pa_log_warn("Error opening mixer: %s", snd_strerror(err));
+    else {
+        pa_bool_t found = FALSE;
+
+        if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
+            found = TRUE;
+        else {
+            snd_pcm_info_t *info;
+
+            snd_pcm_info_alloca(&info);
+
+            if (snd_pcm_info(u->pcm_handle, info) >= 0) {
+                char *md;
+                int card;
+
+                if ((card = snd_pcm_info_get_card(info)) >= 0) {
+
+                    md = pa_sprintf_malloc("hw:%i", card);
+
+                    if (strcmp(u->device_name, md))
+                        if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
+                            found = TRUE;
+                    pa_xfree(md);
+                }
+            }
+        }
+
+        if (found)
+            if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM")))
+                found = FALSE;
+
+        if (!found) {
+            snd_mixer_close(u->mixer_handle);
+            u->mixer_handle = NULL;
+        }
+    }
+
+    if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
+        namereg_fail = TRUE;
+    else {
+        name = name_buf = pa_sprintf_malloc("alsa_output.%s", u->device_name);
+        namereg_fail = FALSE;
+    }
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, name);
+    data.namereg_fail = namereg_fail;
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+
+    pa_alsa_init_proplist(data.proplist, pcm_info);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+    pa_sink_new_data_done(&data);
+    pa_xfree(name_buf);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink object");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    u->frame_size = frame_size;
+    u->fragment_size = frag_size = period_frames * frame_size;
+    u->nfragments = nfrags;
+    u->hwbuf_size = u->fragment_size * nfrags;
+    u->hwbuf_unused_frames = 0;
+    u->tsched_watermark = tsched_watermark;
+    u->frame_index = 0;
+    u->hw_dB_supported = FALSE;
+    u->hw_dB_min = u->hw_dB_max = 0;
+    u->hw_volume_min = u->hw_volume_max = 0;
+
+    if (use_tsched)
+        fix_tsched_watermark(u);
+
+    u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0;
+    u->sink->thread_info.max_request = u->hwbuf_size;
+
+    pa_sink_set_latency_range(u->sink,
+                              !use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1,
+                              pa_bytes_to_usec(u->hwbuf_size, &ss));
+
+    pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms",
+                nfrags, (long unsigned) u->fragment_size,
+                (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
+
+    if (use_tsched)
+        pa_log_info("Time scheduling watermark is %0.2fms",
+                    (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC);
+
+    if (update_sw_params(u) < 0)
+        goto fail;
+
+    pa_memchunk_reset(&u->memchunk);
+
+    if (u->mixer_handle) {
+        pa_assert(u->mixer_elem);
+
+        if (snd_mixer_selem_has_playback_volume(u->mixer_elem))
+
+            if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0 &&
+                snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) {
+
+                pa_bool_t suitable = TRUE;
+
+                pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
+
+                if (u->hw_volume_min > u->hw_volume_max) {
+
+                    pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max);
+                    suitable = FALSE;
+
+                } else if (u->hw_volume_max - u->hw_volume_min < 3) {
+
+                    pa_log_info("Device has less than 4 volume levels. Falling back to software volume control.");
+                    suitable = FALSE;
+
+                } else if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) {
+
+                    /* u->hw_dB_max = 0; u->hw_dB_min = -3000; Use this to make valgrind shut up */
+
+                    pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0);
+
+                    /* Let's see if this thing actually is useful for muting */
+                    if (u->hw_dB_min > -6000) {
+                        pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100);
+
+                        suitable = FALSE;
+                    } else if (u->hw_dB_max < 0) {
+
+                        pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100);
+                        suitable = FALSE;
+
+                    } else if (u->hw_dB_min >= u->hw_dB_max) {
+
+                        pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100);
+                        suitable = FALSE;
+
+                    } else {
+
+                        if (u->hw_dB_max > 0) {
+                            /* dB > 0 means overamplification, and clipping, we don't want that here */
+                            pa_log_info("Device can do overamplification for %0.2f dB. Limiting to 0 db", ((double) u->hw_dB_max) / 100);
+                            u->hw_dB_max = 0;
+                        }
+
+                        u->hw_dB_supported = TRUE;
+                    }
+                }
+
+                if (suitable) {
+                    u->sink->get_volume = sink_get_volume_cb;
+                    u->sink->set_volume = sink_set_volume_cb;
+                    u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0);
+                    pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+                } else if (mixer_reset) {
+                    pa_log_info("Using software volume control. Trying to reset sound card to 0 dB.");
+                    pa_alsa_0dB_playback(u->mixer_elem);
+                } else
+                    pa_log_info("Using software volume control. Leaving hw mixer controls untouched.");
+            }
+
+        if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) {
+            u->sink->get_mute = sink_get_mute_cb;
+            u->sink->set_mute = sink_set_mute_cb;
+            u->sink->flags |= PA_SINK_HW_MUTE_CTRL;
+        }
+
+        u->mixer_fdl = pa_alsa_fdlist_new();
+
+        if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
+            pa_log("Failed to initialize file descriptor monitoring");
+            goto fail;
+        }
+
+        snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
+        snd_mixer_elem_set_callback_private(u->mixer_elem, u);
+    } else
+        u->mixer_fdl = NULL;
+
+    pa_alsa_dump(u->pcm_handle);
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    /* Get initial mixer settings */
+    if (data.volume_is_set) {
+        if (u->sink->set_volume)
+            u->sink->set_volume(u->sink);
+    } else {
+        if (u->sink->get_volume)
+            u->sink->get_volume(u->sink);
+    }
+
+    if (data.muted_is_set) {
+        if (u->sink->set_mute)
+            u->sink->set_mute(u->sink);
+    } else {
+        if (u->sink->get_mute)
+            u->sink->get_mute(u->sink);
+    }
+
+    pa_sink_put(u->sink);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata)) {
+        pa_alsa_redirect_errors_dec();
+        return;
+    }
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->alsa_rtpoll_item)
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->mixer_fdl)
+        pa_alsa_fdlist_free(u->mixer_fdl);
+
+    if (u->mixer_handle)
+        snd_mixer_close(u->mixer_handle);
+
+    if (u->pcm_handle) {
+        snd_pcm_drop(u->pcm_handle);
+        snd_pcm_close(u->pcm_handle);
+    }
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
+    pa_xfree(u->device_name);
+    pa_xfree(u);
+
+    snd_config_update_free_global();
+
+    pa_alsa_redirect_errors_dec();
+}
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
new file mode 100644 (file)
index 0000000..1cc467d
--- /dev/null
@@ -0,0 +1,1332 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <asoundlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
+
+#include "alsa-util.h"
+#include "module-alsa-source-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "source_name=<name for the source> "
+        "device=<ALSA device> "
+        "device_id=<ALSA card index> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "channel_map=<channel map> "
+        "fragments=<number of fragments> "
+        "fragment_size=<fragment size> "
+        "mmap=<enable memory mapping?> "
+        "tsched=<enable system timer based scheduling mode?> "
+        "tsched_buffer_size=<buffer size when using timer based scheduling> "
+        "tsched_buffer_watermark=<upper fill watermark> "
+        "mixer_reset=<reset hw volume and mute settings to sane defaults when falling back to software?>");
+
+static const char* const valid_modargs[] = {
+    "source_name",
+    "device",
+    "device_id",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "fragments",
+    "fragment_size",
+    "mmap",
+    "tsched",
+    "tsched_buffer_size",
+    "tsched_buffer_watermark",
+    "mixer_reset",
+    NULL
+};
+
+#define DEFAULT_DEVICE "default"
+#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)       /* 2s */
+#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)  /* 20ms */
+#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC)           /* 3ms */
+#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC)          /* 3ms */
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    snd_pcm_t *pcm_handle;
+
+    pa_alsa_fdlist *mixer_fdl;
+    snd_mixer_t *mixer_handle;
+    snd_mixer_elem_t *mixer_elem;
+    long hw_volume_max, hw_volume_min;
+    long hw_dB_max, hw_dB_min;
+    pa_bool_t hw_dB_supported;
+
+    size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;
+    unsigned nfragments;
+
+    char *device_name;
+
+    pa_bool_t use_mmap, use_tsched;
+
+    pa_rtpoll_item *alsa_rtpoll_item;
+
+    snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST];
+
+    pa_smoother *smoother;
+    int64_t frame_index;
+
+    snd_pcm_sframes_t hwbuf_unused_frames;
+};
+
+static void fix_tsched_watermark(struct userdata *u) {
+    size_t max_use;
+    size_t min_sleep, min_wakeup;
+    pa_assert(u);
+
+    max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size;
+
+    min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->source->sample_spec);
+    min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->source->sample_spec);
+
+    if (min_sleep > max_use/2)
+        min_sleep = pa_frame_align(max_use/2, &u->source->sample_spec);
+    if (min_sleep < u->frame_size)
+        min_sleep = u->frame_size;
+
+    if (min_wakeup > max_use/2)
+        min_wakeup = pa_frame_align(max_use/2, &u->source->sample_spec);
+    if (min_wakeup < u->frame_size)
+        min_wakeup = u->frame_size;
+
+    if (u->tsched_watermark > max_use-min_sleep)
+        u->tsched_watermark = max_use-min_sleep;
+
+    if (u->tsched_watermark < min_wakeup)
+        u->tsched_watermark = min_wakeup;
+}
+
+static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
+    pa_usec_t wm, usec;
+
+    pa_assert(u);
+
+    usec = pa_source_get_requested_latency_within_thread(u->source);
+
+    if (usec == (pa_usec_t) -1)
+        usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec);
+
+/*     pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
+
+    wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec);
+
+    if (usec >= wm) {
+        *sleep_usec = usec - wm;
+        *process_usec = wm;
+    } else
+        *process_usec = *sleep_usec = usec /= 2;
+
+/*     pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
+
+    return usec;
+}
+
+static int try_recover(struct userdata *u, const char *call, int err) {
+    pa_assert(u);
+    pa_assert(call);
+    pa_assert(err < 0);
+
+    pa_log_debug("%s: %s", call, snd_strerror(err));
+
+    pa_assert(err != -EAGAIN);
+
+    if (err == -EPIPE)
+        pa_log_debug("%s: Buffer overrun!", call);
+
+    if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
+        snd_pcm_start(u->pcm_handle);
+        return 0;
+    }
+
+    pa_log("%s: %s", call, snd_strerror(err));
+    return -1;
+}
+
+static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) {
+    size_t left_to_record;
+
+    if (n*u->frame_size < u->hwbuf_size)
+        left_to_record = u->hwbuf_size - (n*u->frame_size);
+    else
+        left_to_record = 0;
+
+    if (left_to_record > 0) {
+/*         pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); */
+    } else {
+        pa_log_info("Overrun!");
+
+        if (u->use_tsched) {
+            size_t old_watermark = u->tsched_watermark;
+
+            u->tsched_watermark *= 2;
+            fix_tsched_watermark(u);
+
+            if (old_watermark != u->tsched_watermark)
+                pa_log_notice("Increasing wakeup watermark to %0.2f ms",
+                              (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
+        }
+    }
+
+    return left_to_record;
+}
+
+static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {
+    int work_done = 0;
+    pa_usec_t max_sleep_usec, process_usec;
+    size_t left_to_record;
+
+    pa_assert(u);
+    pa_source_assert_ref(u->source);
+
+    if (u->use_tsched)
+        hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+    for (;;) {
+        snd_pcm_sframes_t n;
+        int r;
+
+        snd_pcm_hwsync(u->pcm_handle);
+
+        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+
+            if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
+                continue;
+
+            return r;
+        }
+
+        left_to_record = check_left_to_record(u, n);
+
+        if (u->use_tsched)
+            if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
+                break;
+
+        if (PA_UNLIKELY(n <= 0))
+            break;
+
+        for (;;) {
+            int err;
+            const snd_pcm_channel_area_t *areas;
+            snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;
+            pa_memchunk chunk;
+            void *p;
+
+/*             pa_log_debug("%lu frames to read", (unsigned long) frames); */
+
+            if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
+
+                if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            /* Make sure that if these memblocks need to be copied they will fit into one slot */
+            if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size)
+                frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size;
+
+            /* Check these are multiples of 8 bit */
+            pa_assert((areas[0].first & 7) == 0);
+            pa_assert((areas[0].step & 7)== 0);
+
+            /* We assume a single interleaved memory buffer */
+            pa_assert((areas[0].first >> 3) == 0);
+            pa_assert((areas[0].step >> 3) == u->frame_size);
+
+            p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
+
+            chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
+            chunk.length = pa_memblock_get_length(chunk.memblock);
+            chunk.index = 0;
+
+            pa_source_post(u->source, &chunk);
+            pa_memblock_unref_fixed(chunk.memblock);
+
+            if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
+
+                if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            work_done = 1;
+
+            u->frame_index += frames;
+
+/*             pa_log_debug("read %lu frames", (unsigned long) frames); */
+
+            if (frames >= (snd_pcm_uframes_t) n)
+                break;
+
+            n -= frames;
+        }
+    }
+
+    *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec;
+    return work_done;
+}
+
+static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {
+    int work_done = 0;
+    pa_usec_t max_sleep_usec, process_usec;
+    size_t left_to_record;
+
+    pa_assert(u);
+    pa_source_assert_ref(u->source);
+
+    if (u->use_tsched)
+        hw_sleep_time(u, &max_sleep_usec, &process_usec);
+
+    for (;;) {
+        snd_pcm_sframes_t n;
+        int r;
+
+        snd_pcm_hwsync(u->pcm_handle);
+
+        if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+
+            if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0)
+                continue;
+
+            return r;
+        }
+
+        left_to_record = check_left_to_record(u, n);
+
+        if (u->use_tsched)
+            if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
+                break;
+
+        if (PA_UNLIKELY(n <= 0))
+            return work_done;
+
+        for (;;) {
+            void *p;
+            snd_pcm_sframes_t frames;
+            pa_memchunk chunk;
+
+            chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
+
+            frames = pa_memblock_get_length(chunk.memblock) / u->frame_size;
+
+            if (frames > n)
+                frames = n;
+
+/*             pa_log_debug("%lu frames to read", (unsigned long) n); */
+
+            p = pa_memblock_acquire(chunk.memblock);
+            frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, frames);
+            pa_memblock_release(chunk.memblock);
+
+            pa_assert(frames != 0);
+
+            if (PA_UNLIKELY(frames < 0)) {
+                pa_memblock_unref(chunk.memblock);
+
+                if ((r = try_recover(u, "snd_pcm_readi", n)) == 0)
+                    continue;
+
+                return r;
+            }
+
+            chunk.index = 0;
+            chunk.length = frames * u->frame_size;
+
+            pa_source_post(u->source, &chunk);
+            pa_memblock_unref(chunk.memblock);
+
+            work_done = 1;
+
+            u->frame_index += frames;
+
+/*             pa_log_debug("read %lu frames", (unsigned long) frames); */
+
+            if (frames >= n)
+                break;
+
+            n -= frames;
+        }
+    }
+
+    *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec;
+    return work_done;
+}
+
+static void update_smoother(struct userdata *u) {
+    snd_pcm_sframes_t delay = 0;
+    int64_t frames;
+    int err;
+    pa_usec_t now1, now2;
+
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    /* Let's update the time smoother */
+
+    snd_pcm_hwsync(u->pcm_handle);
+    snd_pcm_avail_update(u->pcm_handle);
+
+    if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
+        pa_log_warn("Failed to get delay: %s", snd_strerror(err));
+        return;
+    }
+
+    frames = u->frame_index + delay;
+
+    now1 = pa_rtclock_usec();
+    now2 = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec);
+
+    pa_smoother_put(u->smoother, now1, now2);
+}
+
+static pa_usec_t source_get_latency(struct userdata *u) {
+    pa_usec_t r = 0;
+    int64_t delay;
+    pa_usec_t now1, now2;
+
+    pa_assert(u);
+
+    now1 = pa_rtclock_usec();
+    now2 = pa_smoother_get(u->smoother, now1);
+
+    delay = (int64_t) now2 - pa_bytes_to_usec(u->frame_index * u->frame_size, &u->source->sample_spec);
+
+    if (delay > 0)
+        r = (pa_usec_t) delay;
+
+    return r;
+}
+
+static int build_pollfd(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    if (u->alsa_rtpoll_item)
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+    if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll)))
+        return -1;
+
+    return 0;
+}
+
+static int suspend(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->pcm_handle);
+
+    pa_smoother_pause(u->smoother, pa_rtclock_usec());
+
+    /* Let's suspend */
+    snd_pcm_close(u->pcm_handle);
+    u->pcm_handle = NULL;
+
+    if (u->alsa_rtpoll_item) {
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+        u->alsa_rtpoll_item = NULL;
+    }
+
+    pa_log_info("Device suspended...");
+
+    return 0;
+}
+
+static int update_sw_params(struct userdata *u) {
+    snd_pcm_uframes_t avail_min;
+    int err;
+
+    pa_assert(u);
+
+    /* Use the full buffer if noone asked us for anything specific */
+    u->hwbuf_unused_frames = 0;
+
+    if (u->use_tsched) {
+        pa_usec_t latency;
+
+        if ((latency = pa_source_get_requested_latency_within_thread(u->source)) != (pa_usec_t) -1) {
+            size_t b;
+
+            pa_log_debug("latency set to %0.2f", (double) latency / PA_USEC_PER_MSEC);
+
+            b = pa_usec_to_bytes(latency, &u->source->sample_spec);
+
+            /* We need at least one sample in our buffer */
+
+            if (PA_UNLIKELY(b < u->frame_size))
+                b = u->frame_size;
+
+            u->hwbuf_unused_frames =
+                PA_LIKELY(b < u->hwbuf_size) ?
+                ((u->hwbuf_size - b) / u->frame_size) : 0;
+
+            fix_tsched_watermark(u);
+        }
+    }
+
+    pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);
+
+    avail_min = 1;
+
+    if (u->use_tsched) {
+        pa_usec_t sleep_usec, process_usec;
+
+        hw_sleep_time(u, &sleep_usec, &process_usec);
+        avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec);
+    }
+
+    pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
+
+    if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) {
+        pa_log("Failed to set software parameters: %s", snd_strerror(err));
+        return err;
+    }
+
+    return 0;
+}
+
+static int unsuspend(struct userdata *u) {
+    pa_sample_spec ss;
+    int err;
+    pa_bool_t b, d;
+    unsigned nfrags;
+    snd_pcm_uframes_t period_size;
+
+    pa_assert(u);
+    pa_assert(!u->pcm_handle);
+
+    pa_log_info("Trying resume...");
+
+    snd_config_update_free_global();
+    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
+        pa_log("Error opening PCM device %s: %s", u->device_name, snd_strerror(err));
+        goto fail;
+    }
+
+    ss = u->source->sample_spec;
+    nfrags = u->nfragments;
+    period_size = u->fragment_size / u->frame_size;
+    b = u->use_mmap;
+    d = u->use_tsched;
+
+    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) {
+        pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
+        goto fail;
+    }
+
+    if (b != u->use_mmap || d != u->use_tsched) {
+        pa_log_warn("Resume failed, couldn't get original access mode.");
+        goto fail;
+    }
+
+    if (!pa_sample_spec_equal(&ss, &u->source->sample_spec)) {
+        pa_log_warn("Resume failed, couldn't restore original sample settings.");
+        goto fail;
+    }
+
+    if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) {
+        pa_log_warn("Resume failed, couldn't restore original fragment settings.");
+        goto fail;
+    }
+
+    if (update_sw_params(u) < 0)
+        goto fail;
+
+    if (build_pollfd(u) < 0)
+        goto fail;
+
+    /* FIXME: We need to reload the volume somehow */
+
+    snd_pcm_start(u->pcm_handle);
+    pa_smoother_resume(u->smoother, pa_rtclock_usec());
+
+    pa_log_info("Resumed successfully...");
+
+    return 0;
+
+fail:
+    if (u->pcm_handle) {
+        snd_pcm_close(u->pcm_handle);
+        u->pcm_handle = NULL;
+    }
+
+    return -1;
+}
+
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            pa_usec_t r = 0;
+
+            if (u->pcm_handle)
+                r = source_get_latency(u);
+
+            *((pa_usec_t*) data) = r;
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SET_STATE:
+
+            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SOURCE_SUSPENDED:
+                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
+
+                    if (suspend(u) < 0)
+                        return -1;
+
+                    break;
+
+                case PA_SOURCE_IDLE:
+                case PA_SOURCE_RUNNING:
+
+                    if (u->source->thread_info.state == PA_SOURCE_INIT) {
+                        if (build_pollfd(u) < 0)
+                            return -1;
+
+                        snd_pcm_start(u->pcm_handle);
+                    }
+
+                    if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
+                        if (unsuspend(u) < 0)
+                            return -1;
+                    }
+
+                    break;
+
+                case PA_SOURCE_UNLINKED:
+                case PA_SOURCE_INIT:
+                    ;
+            }
+
+            break;
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+
+    pa_assert(u);
+    pa_assert(u->mixer_handle);
+
+    if (mask == SND_CTL_EVENT_MASK_REMOVE)
+        return 0;
+
+    if (mask & SND_CTL_EVENT_MASK_VALUE) {
+        pa_source_get_volume(u->source);
+        pa_source_get_mute(u->source);
+    }
+
+    return 0;
+}
+
+static int source_get_volume_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    int err;
+    int i;
+
+    pa_assert(u);
+    pa_assert(u->mixer_elem);
+
+    for (i = 0; i < s->sample_spec.channels; i++) {
+        long alsa_vol;
+
+        pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i]));
+
+        if (u->hw_dB_supported) {
+
+            if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) {
+                s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+                continue;
+            }
+
+            u->hw_dB_supported = FALSE;
+        }
+
+        if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
+            goto fail;
+
+        s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
+    }
+
+    return 0;
+
+fail:
+    pa_log_error("Unable to read volume: %s", snd_strerror(err));
+
+    return -1;
+}
+
+static int source_set_volume_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    int err;
+    int i;
+
+    pa_assert(u);
+    pa_assert(u->mixer_elem);
+
+    for (i = 0; i < s->sample_spec.channels; i++) {
+        long alsa_vol;
+        pa_volume_t vol;
+
+        pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i]));
+
+        vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM);
+
+        if (u->hw_dB_supported) {
+            alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
+            alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
+
+
+            if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) {
+
+                if (snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+                    s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0);
+
+                continue;
+            }
+
+            u->hw_dB_supported = FALSE;
+        }
+
+        alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min;
+        alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
+
+        if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0)
+            goto fail;
+
+        if (snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0)
+            s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min));
+    }
+
+    return 0;
+
+fail:
+    pa_log_error("Unable to set volume: %s", snd_strerror(err));
+
+    return -1;
+}
+
+static int source_get_mute_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    int err, sw;
+
+    pa_assert(u);
+    pa_assert(u->mixer_elem);
+
+    if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) {
+        pa_log_error("Unable to get switch: %s", snd_strerror(err));
+        return -1;
+    }
+
+    s->muted = !sw;
+
+    return 0;
+}
+
+static int source_set_mute_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    int err;
+
+    pa_assert(u);
+    pa_assert(u->mixer_elem);
+
+    if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) {
+        pa_log_error("Unable to set switch: %s", snd_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static void source_update_requested_latency_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    pa_assert(u);
+
+    if (!u->pcm_handle)
+        return;
+
+    update_sw_params(u);
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    for (;;) {
+        int ret;
+
+/*         pa_log_debug("loop"); */
+
+        /* Read some data and pass it to the sources */
+        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+            int work_done = 0;
+            pa_usec_t sleep_usec;
+
+            if (u->use_mmap)
+                work_done = mmap_read(u, &sleep_usec);
+            else
+                work_done = unix_read(u, &sleep_usec);
+
+            if (work_done < 0)
+                goto fail;
+
+/*             pa_log_debug("work_done = %i", work_done); */
+
+            if (work_done)
+                update_smoother(u);
+
+            if (u->use_tsched) {
+                pa_usec_t cusec;
+
+                /* OK, the capture buffer is now empty, let's
+                 * calculate when to wake up next */
+
+/*                 pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
+
+                /* Convert from the sound card time domain to the
+                 * system time domain */
+                cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec);
+
+/*                 pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
+
+                /* We don't trust the conversion, so we wake up whatever comes first */
+                pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
+            }
+        } else if (u->use_tsched)
+
+            /* OK, we're in an invalid state, let's disable our timers */
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        /* Tell ALSA about this and process its response */
+        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+            struct pollfd *pollfd;
+            unsigned short revents = 0;
+            int err;
+            unsigned n;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, &n);
+
+            if ((err = snd_pcm_poll_descriptors_revents(u->pcm_handle, pollfd, n, &revents)) < 0) {
+                pa_log("snd_pcm_poll_descriptors_revents() failed: %s", snd_strerror(err));
+                goto fail;
+            }
+
+            if (revents & (POLLERR|POLLNVAL|POLLHUP)) {
+                if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
+                    goto fail;
+
+                snd_pcm_start(u->pcm_handle);
+            }
+
+            if (revents && u->use_tsched)
+                pa_log_debug("Wakeup from ALSA! (%i)", revents);
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+
+    pa_modargs *ma = NULL;
+    struct userdata *u = NULL;
+    const char *dev_id;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
+    snd_pcm_uframes_t period_frames, tsched_frames;
+    size_t frame_size;
+    snd_pcm_info_t *pcm_info = NULL;
+    int err;
+    const char *name;
+    char *name_buf = NULL;
+    pa_bool_t namereg_fail;
+    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE;
+    pa_source_new_data data;
+
+    snd_pcm_info_alloca(&pcm_info);
+
+    pa_assert(m);
+
+    pa_alsa_redirect_errors_inc();
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
+        pa_log("Failed to parse sample specification");
+        goto fail;
+    }
+
+    frame_size = pa_frame_size(&ss);
+
+    nfrags = m->core->default_n_fragments;
+    frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);
+    if (frag_size <= 0)
+        frag_size = frame_size;
+    tsched_size = pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss);
+    tsched_watermark = pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);
+
+    if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||
+        pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 ||
+        pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 ||
+        pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) {
+        pa_log("Failed to parse buffer metrics");
+        goto fail;
+    }
+
+    hwbuf_size = frag_size * nfrags;
+    period_frames = frag_size/frame_size;
+    tsched_frames = tsched_size/frame_size;
+
+    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+        pa_log("Failed to parse mmap argument.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+        pa_log("Failed to parse timer_scheduling argument.");
+        goto fail;
+    }
+
+    if (use_tsched && !pa_rtclock_hrtimer()) {
+        pa_log("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
+        use_tsched = FALSE;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) {
+        pa_log("Failed to parse mixer_reset argument.");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->use_mmap = use_mmap;
+    u->use_tsched = use_tsched;
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+    u->alsa_rtpoll_item = NULL;
+
+    u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE, 5);
+    pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
+    snd_config_update_free_global();
+
+    b = use_mmap;
+    d = use_tsched;
+
+    if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+                      dev_id,
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_CAPTURE,
+                      &nfrags, &period_frames, tsched_frames,
+                      &b, &d)))
+            goto fail;
+
+    } else {
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_string(
+                      pa_modargs_get_value(ma, "device", DEFAULT_DEVICE),
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_CAPTURE,
+                      &nfrags, &period_frames, tsched_frames,
+                      &b, &d)))
+            goto fail;
+    }
+
+    pa_assert(u->device_name);
+    pa_log_info("Successfully opened device %s.", u->device_name);
+
+    if (use_mmap && !b) {
+        pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
+        u->use_mmap = use_mmap = FALSE;
+    }
+
+    if (use_tsched && (!b || !d)) {
+        pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling.");
+        u->use_tsched = use_tsched = FALSE;
+    }
+
+    if (u->use_mmap)
+        pa_log_info("Successfully enabled mmap() mode.");
+
+    if (u->use_tsched)
+        pa_log_info("Successfully enabled timer-based scheduling mode.");
+
+    if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
+        pa_log("Error fetching PCM info: %s", snd_strerror(err));
+        goto fail;
+    }
+
+    /* ALSA might tweak the sample spec, so recalculate the frame size */
+    frame_size = pa_frame_size(&ss);
+
+    if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)
+        pa_log("Error opening mixer: %s", snd_strerror(err));
+    else {
+        pa_bool_t found = FALSE;
+
+        if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
+            found = TRUE;
+        else {
+            snd_pcm_info_t* info;
+
+            snd_pcm_info_alloca(&info);
+
+            if (snd_pcm_info(u->pcm_handle, info) >= 0) {
+                char *md;
+                int card;
+
+                if ((card = snd_pcm_info_get_card(info)) >= 0) {
+
+                    md = pa_sprintf_malloc("hw:%i", card);
+
+                    if (strcmp(u->device_name, md))
+                        if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
+                            found = TRUE;
+                    pa_xfree(md);
+                }
+            }
+        }
+
+        if (found)
+            if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic")))
+                found = FALSE;
+
+        if (!found) {
+            snd_mixer_close(u->mixer_handle);
+            u->mixer_handle = NULL;
+        }
+    }
+
+    if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
+        namereg_fail = TRUE;
+    else {
+        name = name_buf = pa_sprintf_malloc("alsa_input.%s", u->device_name);
+        namereg_fail = FALSE;
+    }
+
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_source_new_data_set_name(&data, name);
+    data.namereg_fail = namereg_fail;
+    pa_source_new_data_set_sample_spec(&data, &ss);
+    pa_source_new_data_set_channel_map(&data, &map);
+
+    pa_alsa_init_proplist(data.proplist, pcm_info);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+    pa_source_new_data_done(&data);
+    pa_xfree(name_buf);
+
+    if (!u->source) {
+        pa_log("Failed to create source object");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg;
+    u->source->update_requested_latency = source_update_requested_latency_cb;
+    u->source->userdata = u;
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+
+    u->frame_size = frame_size;
+    u->fragment_size = frag_size = period_frames * frame_size;
+    u->nfragments = nfrags;
+    u->hwbuf_size = u->fragment_size * nfrags;
+    u->hwbuf_unused_frames = 0;
+    u->tsched_watermark = tsched_watermark;
+    u->frame_index = 0;
+    u->hw_dB_supported = FALSE;
+    u->hw_dB_min = u->hw_dB_max = 0;
+    u->hw_volume_min = u->hw_volume_max = 0;
+
+    if (use_tsched)
+        fix_tsched_watermark(u);
+
+    pa_source_set_latency_range(u->source,
+                                !use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1,
+                                pa_bytes_to_usec(u->hwbuf_size, &ss));
+
+    pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms",
+                nfrags, (long unsigned) u->fragment_size,
+                (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
+
+    if (use_tsched)
+        pa_log_info("Time scheduling watermark is %0.2fms",
+                    (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC);
+
+    if (update_sw_params(u) < 0)
+        goto fail;
+
+    if (u->mixer_handle) {
+        pa_assert(u->mixer_elem);
+
+        if (snd_mixer_selem_has_capture_volume(u->mixer_elem))
+            if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, FALSE) >= 0 &&
+                snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) {
+
+                pa_bool_t suitable = TRUE;
+
+                pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
+
+                if (u->hw_volume_min > u->hw_volume_max) {
+
+                    pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max);
+                    suitable = FALSE;
+
+                } else if (u->hw_volume_max - u->hw_volume_min < 3) {
+
+                    pa_log_info("Device has less than 4 volume levels. Falling back to software volume control.");
+                    suitable = FALSE;
+
+                } else if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) {
+
+                    pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0);
+
+                    /* Let's see if this thing actually is useful for muting */
+                    if (u->hw_dB_min > -6000) {
+                        pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100);
+
+                        suitable = FALSE;
+                    } else if (u->hw_dB_max < 0) {
+
+                        pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100);
+                        suitable = FALSE;
+
+                    } else if (u->hw_dB_min >= u->hw_dB_max) {
+
+                        pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100);
+                        suitable = FALSE;
+
+                    } else
+                        u->hw_dB_supported = TRUE;
+                }
+
+                if (suitable) {
+                    u->source->get_volume = source_get_volume_cb;
+                    u->source->set_volume = source_set_volume_cb;
+                    u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0);
+                    pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+                } else if (mixer_reset) {
+                    pa_log_info("Using software volume control. Trying to reset sound card to 0 dB.");
+                    pa_alsa_0dB_capture(u->mixer_elem);
+                } else
+                    pa_log_info("Using software volume control. Leaving hw mixer controls untouched.");
+
+            }
+
+
+        if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) {
+            u->source->get_mute = source_get_mute_cb;
+            u->source->set_mute = source_set_mute_cb;
+            u->source->flags |= PA_SOURCE_HW_MUTE_CTRL;
+        }
+
+        u->mixer_fdl = pa_alsa_fdlist_new();
+
+        if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
+            pa_log("Failed to initialize file descriptor monitoring");
+            goto fail;
+        }
+
+        snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
+        snd_mixer_elem_set_callback_private(u->mixer_elem, u);
+    } else
+        u->mixer_fdl = NULL;
+
+    pa_alsa_dump(u->pcm_handle);
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+    /* Get initial mixer settings */
+    if (data.volume_is_set) {
+        if (u->source->set_volume)
+            u->source->set_volume(u->source);
+    } else {
+        if (u->source->get_volume)
+            u->source->get_volume(u->source);
+    }
+
+    if (data.muted_is_set) {
+        if (u->source->set_mute)
+            u->source->set_mute(u->source);
+    } else {
+        if (u->source->get_mute)
+            u->source->get_mute(u->source);
+    }
+
+    pa_source_put(u->source);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata)) {
+        pa_alsa_redirect_errors_dec();
+        return;
+    }
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->alsa_rtpoll_item)
+        pa_rtpoll_item_free(u->alsa_rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->mixer_fdl)
+        pa_alsa_fdlist_free(u->mixer_fdl);
+
+    if (u->mixer_handle)
+        snd_mixer_close(u->mixer_handle);
+
+    if (u->pcm_handle) {
+        snd_pcm_drop(u->pcm_handle);
+        snd_pcm_close(u->pcm_handle);
+    }
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
+    pa_xfree(u->device_name);
+    pa_xfree(u);
+
+    snd_config_update_free_global();
+    pa_alsa_redirect_errors_dec();
+}
diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c
new file mode 100644 (file)
index 0000000..8b67a36
--- /dev/null
@@ -0,0 +1,178 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2008 Colin Guthrie
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+
+#include "module-always-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("Always keeps at least one sink loaded even if it's a null one");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+        "sink_name=<name of sink>");
+
+#define DEFAULT_SINK_NAME "auto_null"
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    NULL,
+};
+
+struct userdata {
+    pa_hook_slot *put_slot, *unlink_slot;
+    pa_module* null_module;
+    pa_bool_t ignore;
+    char *sink_name;
+};
+
+static void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata* u) {
+    pa_sink *target;
+    uint32_t idx;
+    char *t;
+
+    pa_assert(c);
+    pa_assert(u);
+    pa_assert(!u->null_module);
+
+    /* Loop through all sinks and check to see if we have *any*
+     * sinks. Ignore the sink passed in (if it's not null) */
+    for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx))
+        if (!sink || target != sink)
+            break;
+
+    if (target)
+        return;
+
+    pa_log_debug("Autoloading null-sink as no other sinks detected.");
+
+    u->ignore = TRUE;
+
+    t = pa_sprintf_malloc("sink_name=%s", u->sink_name);
+    u->null_module = pa_module_load(c, "module-null-sink", t);
+    pa_xfree(t);
+
+    u->ignore = FALSE;
+
+    if (!u->null_module)
+        pa_log_warn("Unable to load module-null-sink");
+}
+
+static pa_hook_result_t put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+
+    /* This is us detecting ourselves on load... just ignore this. */
+    if (u->ignore)
+        return PA_HOOK_OK;
+
+    /* Auto-loaded null-sink not active, so ignoring newly detected sink. */
+    if (!u->null_module)
+        return PA_HOOK_OK;
+
+    /* This is us detecting ourselves on load in a different way... just ignore this too. */
+    if (sink->module == u->null_module)
+        return PA_HOOK_OK;
+
+    pa_log_info("A new sink has been discovered. Unloading null-sink.");
+
+    pa_module_unload_request(u->null_module);
+    u->null_module = NULL;
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+
+    /* First check to see if it's our own null-sink that's been removed... */
+    if (u->null_module && sink->module == u->null_module) {
+        pa_log_debug("Autoloaded null-sink removed");
+        u->null_module = NULL;
+        return PA_HOOK_OK;
+    }
+
+    load_null_sink_if_needed(c, sink, u);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        return -1;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    u->put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) put_hook_callback, u);
+    u->unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) unlink_hook_callback, u);
+    u->null_module = NULL;
+    u->ignore = FALSE;
+
+    pa_modargs_free(ma);
+
+    load_null_sink_if_needed(m->core, NULL, u);
+
+    return 0;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->put_slot)
+        pa_hook_slot_free(u->put_slot);
+    if (u->unlink_slot)
+        pa_hook_slot_free(u->unlink_slot);
+    if (u->null_module)
+        pa_module_unload_request(u->null_module);
+
+    pa_xfree(u->sink_name);
+    pa_xfree(u);
+}
diff --git a/src/modules/module-bt-proximity.c b/src/modules/module-bt-proximity.c
new file mode 100644 (file)
index 0000000..77b9586
--- /dev/null
@@ -0,0 +1,490 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/start-child.h>
+
+#include "dbus-util.h"
+#include "module-bt-proximity-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Bluetooth Proximity Volume Control");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+        "sink=<sink name> "
+        "hci=<hci device> "
+);
+
+#define DEFAULT_HCI "hci0"
+
+static const char* const valid_modargs[] = {
+    "sink",
+    "rssi",
+    "hci",
+    NULL,
+};
+
+struct bonding {
+    struct userdata *userdata;
+    char address[18];
+
+    pid_t pid;
+    int fd;
+
+    pa_io_event *io_event;
+
+    enum {
+        UNKNOWN,
+        FOUND,
+        NOT_FOUND
+    } state;
+};
+
+struct userdata {
+    pa_module *module;
+    pa_dbus_connection *dbus_connection;
+
+    char *sink_name;
+    char *hci, *hci_path;
+
+    pa_hashmap *bondings;
+
+    unsigned n_found;
+    unsigned n_unknown;
+
+    pa_bool_t muted;
+};
+
+static void update_volume(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->muted && u->n_found > 0) {
+        pa_sink *s;
+
+        u->muted = FALSE;
+
+        if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+            pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
+            return;
+        }
+
+        pa_log_info("Found %u BT devices, unmuting.", u->n_found);
+        pa_sink_set_mute(s, FALSE);
+
+    } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
+        pa_sink *s;
+
+        u->muted = TRUE;
+
+        if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+            pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
+            return;
+        }
+
+        pa_log_info("No BT devices found, muting.");
+        pa_sink_set_mute(s, TRUE);
+
+    } else
+        pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);
+}
+
+static void bonding_free(struct bonding *b) {
+    pa_assert(b);
+
+    if (b->state == FOUND)
+        pa_assert_se(b->userdata->n_found-- >= 1);
+
+    if (b->state == UNKNOWN)
+        pa_assert_se(b->userdata->n_unknown-- >= 1);
+
+    if (b->pid != (pid_t) -1) {
+        kill(b->pid, SIGTERM);
+        waitpid(b->pid, NULL, 0);
+    }
+
+    if (b->fd >= 0)
+        pa_close(b->fd);
+
+    if (b->io_event)
+        b->userdata->module->core->mainloop->io_free(b->io_event);
+
+    pa_xfree(b);
+}
+
+static void io_event_cb(
+        pa_mainloop_api*a,
+        pa_io_event* e,
+        int fd,
+        pa_io_event_flags_t events,
+        void *userdata) {
+
+    struct bonding *b = userdata;
+    char x;
+    ssize_t r;
+
+    pa_assert(b);
+
+    if ((r = read(fd, &x, 1)) <= 0) {
+        pa_log_warn("Child watching '%s' died abnormally: %s", b->address, r == 0 ? "EOF" : pa_cstrerror(errno));
+
+        pa_assert_se(pa_hashmap_remove(b->userdata->bondings, b->address) == b);
+        bonding_free(b);
+        return;
+    }
+
+    pa_assert_se(r == 1);
+
+    if (b->state == UNKNOWN)
+        pa_assert_se(b->userdata->n_unknown-- >= 1);
+
+    if (x == '+') {
+        pa_assert(b->state == UNKNOWN || b->state == NOT_FOUND);
+
+        b->state = FOUND;
+        b->userdata->n_found++;
+
+        pa_log_info("Device '%s' is alive.", b->address);
+
+    } else {
+        pa_assert(x == '-');
+        pa_assert(b->state == UNKNOWN || b->state == FOUND);
+
+        if (b->state == FOUND)
+            b->userdata->n_found--;
+
+        b->state = NOT_FOUND;
+
+        pa_log_info("Device '%s' is dead.", b->address);
+    }
+
+    update_volume(b->userdata);
+}
+
+static struct bonding* bonding_new(struct userdata *u, const char *a) {
+    struct bonding *b = NULL;
+    DBusMessage *m = NULL, *r = NULL;
+    DBusError e;
+    const char *class;
+
+    pa_assert(u);
+    pa_assert(a);
+
+    pa_return_val_if_fail(strlen(a) == 17, NULL);
+    pa_return_val_if_fail(!pa_hashmap_get(u->bondings, a), NULL);
+
+    dbus_error_init(&e);
+
+    pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "GetRemoteMajorClass"));
+    pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID));
+    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), m, -1, &e);
+
+    if (!r) {
+        pa_log("org.bluez.Adapter.GetRemoteMajorClass(%s) failed: %s", a, e.message);
+        goto fail;
+    }
+
+    if (!(dbus_message_get_args(r, &e, DBUS_TYPE_STRING, &class, DBUS_TYPE_INVALID))) {
+        pa_log("Malformed org.bluez.Adapter.GetRemoteMajorClass signal: %s", e.message);
+        goto fail;
+    }
+
+    if (strcmp(class, "phone")) {
+        pa_log_info("Found device '%s' of class '%s', ignoring.", a, class);
+        goto fail;
+    }
+
+    b = pa_xnew(struct bonding, 1);
+    b->userdata = u;
+    pa_strlcpy(b->address, a, sizeof(b->address));
+    b->pid = (pid_t) -1;
+    b->fd = -1;
+    b->io_event = NULL;
+    b->state = UNKNOWN;
+    u->n_unknown ++;
+
+    pa_log_info("Watching device '%s' of class '%s'.", b->address, class);
+
+    if ((b->fd = pa_start_child_for_read(PA_BT_PROXIMITY_HELPER, a, &b->pid)) < 0) {
+        pa_log("Failed to start helper tool.");
+        goto fail;
+    }
+
+    b->io_event = u->module->core->mainloop->io_new(
+            u->module->core->mainloop,
+            b->fd,
+            PA_IO_EVENT_INPUT,
+            io_event_cb,
+            b);
+
+    dbus_message_unref(m);
+    dbus_message_unref(r);
+
+    pa_hashmap_put(u->bondings, b->address, b);
+
+    return b;
+
+fail:
+    if (m)
+        dbus_message_unref(m);
+    if (r)
+        dbus_message_unref(r);
+
+    if (b)
+        bonding_free(b);
+
+    dbus_error_free(&e);
+    return NULL;
+}
+
+static void bonding_remove(struct userdata *u, const char *a) {
+    struct bonding *b;
+    pa_assert(u);
+
+    pa_return_if_fail((b = pa_hashmap_remove(u->bondings, a)));
+
+    pa_log_info("No longer watching device '%s'", b->address);
+    bonding_free(b);
+}
+
+static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *m, void *userdata) {
+    struct userdata *u = userdata;
+    DBusError e;
+
+    dbus_error_init(&e);
+
+    if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingCreated")) {
+        const char *a;
+
+        if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
+            pa_log("Malformed org.bluez.Adapter.BondingCreated signal: %s", e.message);
+            goto finish;
+        }
+
+        bonding_new(u, a);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+
+    } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingRemoved")) {
+
+        const char *a;
+
+        if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
+            pa_log("Malformed org.bluez.Adapter.BondingRemoved signal: %s", e.message);
+            goto finish;
+        }
+
+        bonding_remove(u, a);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+finish:
+
+    dbus_error_free(&e);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int add_matches(struct userdata *u, pa_bool_t add) {
+    char *filter1, *filter2;
+    DBusError e;
+    int r = -1;
+
+    pa_assert(u);
+    dbus_error_init(&e);
+
+    filter1 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingCreated',path='%s'", u->hci_path);
+    filter2 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingRemoved',path='%s'", u->hci_path);
+
+    if (add) {
+        dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
+
+        if (dbus_error_is_set(&e)) {
+            pa_log("dbus_bus_add_match(%s) failed: %s", filter1, e.message);
+            goto finish;
+        }
+    } else
+        dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
+
+
+    if (add) {
+        dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+
+        if (dbus_error_is_set(&e)) {
+            pa_log("dbus_bus_add_match(%s) failed: %s", filter2, e.message);
+            dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+            goto finish;
+        }
+    } else
+        dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
+
+
+    if (add)
+        pa_assert_se(dbus_connection_add_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u, NULL));
+    else
+        dbus_connection_remove_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u);
+
+    r = 0;
+
+finish:
+    pa_xfree(filter1);
+    pa_xfree(filter2);
+    dbus_error_free(&e);
+
+    return r;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    DBusError e;
+    DBusMessage *msg = NULL, *r = NULL;
+    DBusMessageIter iter, sub;
+
+    pa_assert(m);
+    dbus_error_init(&e);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+    u->hci = pa_xstrdup(pa_modargs_get_value(ma, "hci", DEFAULT_HCI));
+    u->hci_path = pa_sprintf_malloc("/org/bluez/%s", u->hci);
+    u->n_found = u->n_unknown = 0;
+    u->muted = FALSE;
+
+    u->bondings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    if (!(u->dbus_connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &e))) {
+        pa_log("Failed to get D-Bus connection: %s", e.message);
+        goto fail;
+    }
+
+    if (add_matches(u, TRUE) < 0)
+        goto fail;
+
+    pa_assert_se(msg = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "ListBondings"));
+
+    if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), msg, -1, &e))) {
+        pa_log("org.bluez.Adapter.ListBondings failed: %s", e.message);
+        goto fail;
+    }
+
+    dbus_message_iter_init(r, &iter);
+
+    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+        pa_log("Malformed reply to org.bluez.Adapter.ListBondings.");
+        goto fail;
+    }
+
+    dbus_message_iter_recurse(&iter, &sub);
+
+    while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
+        const char *a = NULL;
+
+        dbus_message_iter_get_basic(&sub, &a);
+        bonding_new(u, a);
+
+        dbus_message_iter_next(&sub);
+    }
+
+    dbus_message_unref(r);
+    dbus_message_unref(msg);
+
+    pa_modargs_free(ma);
+
+    if (pa_hashmap_size(u->bondings) == 0)
+        pa_log_warn("Warning: no phone device bonded.");
+
+    update_volume(u);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    dbus_error_free(&e);
+
+    if (msg)
+        dbus_message_unref(msg);
+
+    if (r)
+        dbus_message_unref(r);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->bondings) {
+        struct bonding *b;
+
+        while ((b = pa_hashmap_steal_first(u->bondings)))
+            bonding_free(b);
+
+        pa_hashmap_free(u->bondings, NULL, NULL);
+    }
+
+    if (u->dbus_connection) {
+        add_matches(u, FALSE);
+        pa_dbus_connection_unref(u->dbus_connection);
+    }
+
+    pa_xfree(u->sink_name);
+    pa_xfree(u->hci_path);
+    pa_xfree(u->hci);
+    pa_xfree(u);
+}
diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c
new file mode 100644 (file)
index 0000000..df7783f
--- /dev/null
@@ -0,0 +1,121 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulsecore/module.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/cli.h>
+#include <pulsecore/sioman.h>
+#include <pulsecore/log.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+
+#include "module-cli-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Command line interface");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("exit_on_eof=<exit daemon after EOF?>");
+
+static const char* const valid_modargs[] = {
+    "exit_on_eof",
+    NULL
+};
+
+static void eof_and_unload_cb(pa_cli*c, void *userdata) {
+    pa_module *m = userdata;
+
+    pa_assert(c);
+    pa_assert(m);
+
+    pa_module_unload_request(m);
+}
+
+static void eof_and_exit_cb(pa_cli*c, void *userdata) {
+    pa_module *m = userdata;
+
+    pa_assert(c);
+    pa_assert(m);
+
+    m->core->mainloop->quit(m->core->mainloop, 0);
+}
+
+int pa__init(pa_module*m) {
+    pa_iochannel *io;
+    pa_modargs *ma;
+    pa_bool_t exit_on_eof = FALSE;
+
+    pa_assert(m);
+
+    if (m->core->running_as_daemon) {
+        pa_log_info("Running as daemon, refusing to load this module.");
+        return 0;
+    }
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "exit_on_eof", &exit_on_eof) < 0) {
+        pa_log("exit_on_eof= expects boolean argument.");
+        goto fail;
+    }
+
+    if (pa_stdio_acquire() < 0) {
+        pa_log("STDIN/STDUSE already in use.");
+        goto fail;
+    }
+
+    io = pa_iochannel_new(m->core->mainloop, STDIN_FILENO, STDOUT_FILENO);
+    pa_iochannel_set_noclose(io, 1);
+
+    m->userdata = pa_cli_new(m->core, io, m);
+
+    pa_cli_set_eof_callback(m->userdata, exit_on_eof ? eof_and_exit_cb : eof_and_unload_cb, m);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    pa_assert(m);
+
+    if (m->core->running_as_daemon == 0) {
+        pa_cli_free(m->userdata);
+        pa_stdio_release();
+    }
+}
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
new file mode 100644 (file)
index 0000000..cef7a99
--- /dev/null
@@ -0,0 +1,1256 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/module.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/time-smoother.h>
+
+#include "module-combine-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Combine multiple sinks to one");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "slaves=<slave sinks> "
+        "adjust_time=<seconds> "
+        "resample_method=<method> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "channel_map=<channel map>");
+
+#define DEFAULT_SINK_NAME "combined"
+
+#define MEMBLOCKQ_MAXLENGTH (1024*1024*16)
+
+#define DEFAULT_ADJUST_TIME 10
+
+#define REQUEST_LATENCY_USEC (PA_USEC_PER_MSEC * 200)
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "slaves",
+    "adjust_time",
+    "resample_method",
+    "format",
+    "channels",
+    "rate",
+    "channel_map",
+    NULL
+};
+
+struct output {
+    struct userdata *userdata;
+
+    pa_sink *sink;
+    pa_sink_input *sink_input;
+
+    pa_asyncmsgq *inq,    /* Message queue from the sink thread to this sink input */
+                 *outq;   /* Message queue from this sink input to the sink thread */
+    pa_rtpoll_item *inq_rtpoll_item_read, *inq_rtpoll_item_write;
+    pa_rtpoll_item *outq_rtpoll_item_read, *outq_rtpoll_item_write;
+
+    pa_memblockq *memblockq;
+
+    pa_usec_t total_latency;
+
+    pa_atomic_t max_request;
+
+    PA_LLIST_FIELDS(struct output);
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    pa_time_event *time_event;
+    uint32_t adjust_time;
+
+    pa_bool_t automatic;
+
+    pa_hook_slot *sink_put_slot, *sink_unlink_slot, *sink_state_changed_slot;
+
+    pa_resample_method_t resample_method;
+
+    struct timeval adjust_timestamp;
+
+    pa_usec_t block_usec;
+
+    pa_idxset* outputs; /* managed in main context */
+
+    struct {
+        PA_LLIST_HEAD(struct output, active_outputs); /* managed in IO thread context */
+        pa_atomic_t running;  /* we cache that value here, so that every thread can query it cheaply */
+        pa_usec_t timestamp;
+        pa_bool_t in_null_mode;
+        pa_smoother *smoother;
+        uint64_t counter;
+    } thread_info;
+};
+
+enum {
+    SINK_MESSAGE_ADD_OUTPUT = PA_SINK_MESSAGE_MAX,
+    SINK_MESSAGE_REMOVE_OUTPUT,
+    SINK_MESSAGE_NEED,
+    SINK_MESSAGE_UPDATE_LATENCY,
+    SINK_MESSAGE_UPDATE_MAX_REQUEST
+};
+
+enum {
+    SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
+};
+
+static void output_free(struct output *o);
+static int output_create_sink_input(struct output *o);
+
+static void adjust_rates(struct userdata *u) {
+    struct output *o;
+    pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency, avg_total_latency = 0;
+    uint32_t base_rate;
+    uint32_t idx;
+    unsigned n = 0;
+
+    pa_assert(u);
+    pa_sink_assert_ref(u->sink);
+
+    if (pa_idxset_size(u->outputs) <= 0)
+        return;
+
+    if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
+        return;
+
+    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
+        pa_usec_t sink_latency;
+
+        if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
+            continue;
+
+        o->total_latency = pa_sink_input_get_latency(o->sink_input, &sink_latency);
+        o->total_latency += sink_latency;
+
+        if (sink_latency > max_sink_latency)
+            max_sink_latency = sink_latency;
+
+        if (min_total_latency == (pa_usec_t) -1 || o->total_latency < min_total_latency)
+            min_total_latency = o->total_latency;
+
+        avg_total_latency += o->total_latency;
+        n++;
+    }
+
+    if (min_total_latency == (pa_usec_t) -1)
+        return;
+
+    avg_total_latency /= n;
+
+    target_latency = max_sink_latency > min_total_latency ? max_sink_latency : min_total_latency;
+
+    pa_log_info("[%s] avg total latency is %0.2f msec.", u->sink->name, (double) avg_total_latency / PA_USEC_PER_MSEC);
+    pa_log_info("[%s] target latency is %0.2f msec.", u->sink->name, (double) target_latency / PA_USEC_PER_MSEC);
+
+    base_rate = u->sink->sample_spec.rate;
+
+    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
+        uint32_t r = base_rate;
+
+        if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
+            continue;
+
+        if (o->total_latency < target_latency)
+            r -= (uint32_t) (((((double) target_latency - o->total_latency))/u->adjust_time)*r/PA_USEC_PER_SEC);
+        else if (o->total_latency > target_latency)
+            r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/PA_USEC_PER_SEC);
+
+        if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) {
+            pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), base_rate, r);
+            pa_sink_input_set_rate(o->sink_input, base_rate);
+        } else {
+            pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), r, (double) r / base_rate, (float) o->total_latency);
+            pa_sink_input_set_rate(o->sink_input, r);
+        }
+    }
+
+    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, NULL, (int64_t) avg_total_latency, NULL);
+}
+
+static void time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+    struct timeval n;
+
+    pa_assert(u);
+    pa_assert(a);
+    pa_assert(u->time_event == e);
+
+    adjust_rates(u);
+
+    pa_gettimeofday(&n);
+    n.tv_sec += u->adjust_time;
+    u->sink->core->mainloop->time_restart(e, &n);
+}
+
+static void process_render_null(struct userdata *u, pa_usec_t now) {
+    size_t ate = 0;
+    pa_assert(u);
+
+    if (u->thread_info.in_null_mode)
+        u->thread_info.timestamp = now;
+
+    while (u->thread_info.timestamp < now + u->block_usec) {
+        pa_memchunk chunk;
+
+        pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk);
+        pa_memblock_unref(chunk.memblock);
+
+        u->thread_info.counter += chunk.length;
+
+/*         pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */
+        u->thread_info.timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
+
+        ate += chunk.length;
+
+        if (ate >= u->sink->thread_info.max_request)
+            break;
+    }
+
+/*     pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */
+
+    pa_smoother_put(u->thread_info.smoother, now,
+                    pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec) - (u->thread_info.timestamp - now));
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority+1);
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    u->thread_info.timestamp = pa_rtclock_usec();
+    u->thread_info.in_null_mode = FALSE;
+
+    for (;;) {
+        int ret;
+
+        /* If no outputs are connected, render some data and drop it immediately. */
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && !u->thread_info.active_outputs) {
+            pa_usec_t now;
+
+            now = pa_rtclock_usec();
+
+            if (!u->thread_info.in_null_mode || u->thread_info.timestamp <= now)
+                process_render_null(u, now);
+
+            pa_rtpoll_set_timer_absolute(u->rtpoll, u->thread_info.timestamp);
+            u->thread_info.in_null_mode = TRUE;
+        } else {
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+            u->thread_info.in_null_mode = FALSE;
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
+            pa_log_info("pa_rtpoll_run() = %i", ret);
+            goto fail;
+        }
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+/* Called from I/O thread context */
+static void render_memblock(struct userdata *u, struct output *o, size_t length) {
+    pa_assert(u);
+    pa_assert(o);
+
+    /* We are run by the sink thread, on behalf of an output (o). The
+     * output is waiting for us, hence it is safe to access its
+     * mainblockq and asyncmsgq directly. */
+
+    /* If we are not running, we cannot produce any data */
+    if (!pa_atomic_load(&u->thread_info.running))
+        return;
+
+    /* Maybe there's some data in the requesting output's queue
+     * now? */
+    while (pa_asyncmsgq_process_one(o->inq) > 0)
+        ;
+
+    /* Ok, now let's prepare some data if we really have to */
+    while (!pa_memblockq_is_readable(o->memblockq)) {
+        struct output *j;
+        pa_memchunk chunk;
+
+        /* Render data! */
+        pa_sink_render(u->sink, length, &chunk);
+
+        u->thread_info.counter += chunk.length;
+
+        /* OK, let's send this data to the other threads */
+        for (j = u->thread_info.active_outputs; j; j = j->next)
+
+            /* Send to other outputs, which are not the requesting
+             * one */
+
+            if (j != o)
+                pa_asyncmsgq_post(j->inq, PA_MSGOBJECT(j->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, &chunk, NULL);
+
+        /* And place it directly into the requesting output's queue */
+        if (o)
+            pa_memblockq_push_align(o->memblockq, &chunk);
+
+        pa_memblock_unref(chunk.memblock);
+    }
+}
+
+/* Called from I/O thread context */
+static void request_memblock(struct output *o, size_t length) {
+    pa_assert(o);
+    pa_sink_input_assert_ref(o->sink_input);
+    pa_sink_assert_ref(o->userdata->sink);
+
+    /* If another thread already prepared some data we received
+     * the data over the asyncmsgq, hence let's first process
+     * it. */
+    while (pa_asyncmsgq_process_one(o->inq) > 0)
+        ;
+
+    /* Check whether we're now readable */
+    if (pa_memblockq_is_readable(o->memblockq))
+        return;
+
+    /* OK, we need to prepare new data, but only if the sink is actually running */
+    if (pa_atomic_load(&o->userdata->thread_info.running))
+        pa_asyncmsgq_send(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_NEED, o, length, NULL);
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    /* If necessary, get some new data */
+    request_memblock(o, nbytes);
+
+    if (pa_memblockq_peek(o->memblockq, chunk) < 0)
+        return -1;
+
+    pa_memblockq_drop(o->memblockq, chunk->length);
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(nbytes > 0);
+    pa_assert_se(o = i->userdata);
+
+    pa_memblockq_rewind(o->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    pa_memblockq_set_maxrewind(o->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    if (pa_atomic_load(&o->max_request) == (int) nbytes)
+        return;
+
+    pa_atomic_store(&o->max_request, (int) nbytes);
+
+    pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    /* Set up the queue from the sink thread to us */
+    pa_assert(!o->inq_rtpoll_item_read && !o->outq_rtpoll_item_write);
+
+    o->inq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+            i->sink->rtpoll,
+            PA_RTPOLL_LATE,  /* This one is not that important, since we check for data in _peek() anyway. */
+            o->inq);
+
+    o->outq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+            i->sink->rtpoll,
+            PA_RTPOLL_EARLY,
+            o->outq);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(o = i->userdata);
+
+    /* Shut down the queue from the sink thread to us */
+    pa_assert(o->inq_rtpoll_item_read && o->outq_rtpoll_item_write);
+
+    pa_rtpoll_item_free(o->inq_rtpoll_item_read);
+    o->inq_rtpoll_item_read = NULL;
+
+    pa_rtpoll_item_free(o->outq_rtpoll_item_write);
+    o->outq_rtpoll_item_write = NULL;
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct output *o;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(o = i->userdata);
+
+    pa_module_unload_request(o->userdata->module);
+    output_free(o);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT)
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct output *o = PA_SINK_INPUT(obj)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+             pa_usec_t *r = data;
+
+            *r = pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &o->sink_input->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+            break;
+        }
+
+        case SINK_INPUT_MESSAGE_POST:
+
+            if (PA_SINK_IS_OPENED(o->sink_input->sink->thread_info.state))
+                pa_memblockq_push_align(o->memblockq, chunk);
+            else
+                pa_memblockq_flush(o->memblockq);
+
+            return 0;
+    }
+
+    return pa_sink_input_process_msg(obj, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static void disable_output(struct output *o) {
+    pa_assert(o);
+
+    if (!o->sink_input)
+        return;
+
+    pa_sink_input_unlink(o->sink_input);
+    pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
+    pa_sink_input_unref(o->sink_input);
+    o->sink_input = NULL;
+}
+
+/* Called from main context */
+static void enable_output(struct output *o) {
+    pa_assert(o);
+
+    if (o->sink_input)
+        return;
+
+    if (output_create_sink_input(o) >= 0) {
+
+        pa_memblockq_flush(o->memblockq);
+
+        pa_sink_input_put(o->sink_input);
+
+        if (o->userdata->sink && PA_SINK_IS_LINKED(pa_sink_get_state(o->userdata->sink)))
+            pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
+    }
+}
+
+/* Called from main context */
+static void suspend(struct userdata *u) {
+    struct output *o;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    /* Let's suspend by unlinking all streams */
+    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
+        disable_output(o);
+
+    pa_log_info("Device suspended...");
+}
+
+/* Called from main context */
+static void unsuspend(struct userdata *u) {
+    struct output *o;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    /* Let's resume */
+    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
+
+        pa_sink_suspend(o->sink, FALSE);
+
+        if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
+            enable_output(o);
+    }
+
+    pa_log_info("Resumed successfully...");
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(sink);
+    pa_assert_se(u = sink->userdata);
+
+    /* Please note that in contrast to the ALSA modules we call
+     * suspend/unsuspend from main context here! */
+
+    switch (state) {
+        case PA_SINK_SUSPENDED:
+            pa_assert(PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
+
+            suspend(u);
+            break;
+
+        case PA_SINK_IDLE:
+        case PA_SINK_RUNNING:
+
+            if (pa_sink_get_state(u->sink) == PA_SINK_SUSPENDED)
+                unsuspend(u);
+
+            break;
+
+        case PA_SINK_UNLINKED:
+        case PA_SINK_INIT:
+            ;
+    }
+
+    return 0;
+}
+
+/* Called from IO context */
+static void update_max_request(struct userdata *u) {
+    size_t max_request = 0;
+    struct output *o;
+
+    for (o = u->thread_info.active_outputs; o; o = o->next) {
+        size_t mr = (size_t) pa_atomic_load(&o->max_request);
+
+        if (mr > max_request)
+            max_request = mr;
+    }
+
+    if (max_request <= 0)
+        max_request = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
+    pa_sink_set_max_request(u->sink, max_request);
+}
+
+/* Called from thread context of the io thread */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_SET_STATE:
+            pa_atomic_store(&u->thread_info.running, PA_PTR_TO_UINT(data) == PA_SINK_RUNNING);
+
+            if (PA_PTR_TO_UINT(data) == PA_SINK_SUSPENDED)
+                pa_smoother_pause(u->thread_info.smoother, pa_rtclock_usec());
+            else
+                pa_smoother_resume(u->thread_info.smoother, pa_rtclock_usec());
+
+            break;
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t x, y, c, *delay = data;
+
+            x = pa_rtclock_usec();
+            y = pa_smoother_get(u->thread_info.smoother, x);
+
+            c = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
+
+            if (y < c)
+                *delay = c - y;
+            else
+                *delay = 0;
+
+            return 0;
+        }
+
+        case SINK_MESSAGE_ADD_OUTPUT: {
+            struct output *op = data;
+
+            PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, op);
+
+            pa_assert(!op->outq_rtpoll_item_read && !op->inq_rtpoll_item_write);
+
+            op->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+                    u->rtpoll,
+                    PA_RTPOLL_EARLY-1,  /* This item is very important */
+                    op->outq);
+            op->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+                    u->rtpoll,
+                    PA_RTPOLL_EARLY,
+                    op->inq);
+
+            update_max_request(u);
+            return 0;
+        }
+
+        case SINK_MESSAGE_REMOVE_OUTPUT: {
+            struct output *op = data;
+
+            PA_LLIST_REMOVE(struct output, u->thread_info.active_outputs, op);
+
+            pa_assert(op->outq_rtpoll_item_read && op->inq_rtpoll_item_write);
+
+            pa_rtpoll_item_free(op->outq_rtpoll_item_read);
+            op->outq_rtpoll_item_read = NULL;
+
+            pa_rtpoll_item_free(op->inq_rtpoll_item_write);
+            op->inq_rtpoll_item_write = NULL;
+
+            update_max_request(u);
+            return 0;
+        }
+
+        case SINK_MESSAGE_NEED:
+            render_memblock(u, (struct output*) data, (size_t) offset);
+            return 0;
+
+        case SINK_MESSAGE_UPDATE_LATENCY: {
+            pa_usec_t x, y, latency = (pa_usec_t) offset;
+
+            x = pa_rtclock_usec();
+            y = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
+
+            if (y > latency)
+                y -= latency;
+            else
+                y = 0;
+
+            pa_smoother_put(u->thread_info.smoother, x, y);
+            return 0;
+        }
+
+        case SINK_MESSAGE_UPDATE_MAX_REQUEST:
+
+            update_max_request(u);
+            break;
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void update_description(struct userdata *u) {
+    pa_bool_t first = TRUE;
+    char *t;
+    struct output *o;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    if (pa_idxset_isempty(u->outputs)) {
+        pa_sink_set_description(u->sink, "Simultaneous output");
+        return;
+    }
+
+    t = pa_xstrdup("Simultaneous output to");
+
+    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
+        char *e;
+
+        if (first) {
+            e = pa_sprintf_malloc("%s %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+            first = FALSE;
+        } else
+            e = pa_sprintf_malloc("%s, %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+
+        pa_xfree(t);
+        t = e;
+    }
+
+    pa_sink_set_description(u->sink, t);
+    pa_xfree(t);
+}
+
+static int output_create_sink_input(struct output *o) {
+    pa_sink_input_new_data data;
+
+    pa_assert(o);
+
+    if (o->sink_input)
+        return 0;
+
+    pa_sink_input_new_data_init(&data);
+    data.sink = o->sink;
+    data.driver = __FILE__;
+    pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&data, &o->userdata->sink->sample_spec);
+    pa_sink_input_new_data_set_channel_map(&data, &o->userdata->sink->channel_map);
+    data.module = o->userdata->module;
+    data.resample_method = o->userdata->resample_method;
+
+    o->sink_input = pa_sink_input_new(o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE);
+
+    pa_sink_input_new_data_done(&data);
+
+    if (!o->sink_input)
+        return -1;
+
+    o->sink_input->parent.process_msg = sink_input_process_msg;
+    o->sink_input->pop = sink_input_pop_cb;
+    o->sink_input->process_rewind = sink_input_process_rewind_cb;
+    o->sink_input->state_change = sink_input_state_change_cb;
+    o->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    o->sink_input->update_max_request = sink_input_update_max_request_cb;
+    o->sink_input->attach = sink_input_attach_cb;
+    o->sink_input->detach = sink_input_detach_cb;
+    o->sink_input->kill = sink_input_kill_cb;
+    o->sink_input->userdata = o;
+
+    pa_sink_input_set_requested_latency(o->sink_input, REQUEST_LATENCY_USEC);
+
+    return 0;
+}
+
+static struct output *output_new(struct userdata *u, pa_sink *sink) {
+    struct output *o;
+    pa_sink_state_t state;
+
+    pa_assert(u);
+    pa_assert(sink);
+    pa_assert(u->sink);
+
+    o = pa_xnew(struct output, 1);
+    o->userdata = u;
+    o->inq = pa_asyncmsgq_new(0);
+    o->outq = pa_asyncmsgq_new(0);
+    o->inq_rtpoll_item_write = o->inq_rtpoll_item_read = NULL;
+    o->outq_rtpoll_item_write = o->outq_rtpoll_item_read = NULL;
+    o->sink = sink;
+    o->sink_input = NULL;
+    o->memblockq = pa_memblockq_new(
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            MEMBLOCKQ_MAXLENGTH,
+            pa_frame_size(&u->sink->sample_spec),
+            1,
+            0,
+            0,
+            NULL);
+    pa_atomic_store(&o->max_request, 0);
+    PA_LLIST_INIT(struct output, o);
+
+    pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
+
+    state = pa_sink_get_state(u->sink);
+
+    if (state != PA_SINK_INIT)
+        pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
+    else {
+        /* If the sink is not yet started, we need to do the activation ourselves */
+        PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, o);
+
+        o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
+                u->rtpoll,
+                PA_RTPOLL_EARLY-1,  /* This item is very important */
+                o->outq);
+        o->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
+                u->rtpoll,
+                PA_RTPOLL_EARLY,
+                o->inq);
+    }
+
+    if (PA_SINK_IS_OPENED(state) || state == PA_SINK_INIT) {
+        pa_sink_suspend(sink, FALSE);
+
+        if (PA_SINK_IS_OPENED(pa_sink_get_state(sink)))
+            if (output_create_sink_input(o) < 0)
+                goto fail;
+    }
+
+    update_description(u);
+
+    return o;
+
+fail:
+
+    if (o) {
+        pa_idxset_remove_by_data(u->outputs, o, NULL);
+
+        if (o->sink_input) {
+            pa_sink_input_unlink(o->sink_input);
+            pa_sink_input_unref(o->sink_input);
+        }
+
+        if (o->memblockq)
+            pa_memblockq_free(o->memblockq);
+
+        if (o->inq)
+            pa_asyncmsgq_unref(o->inq);
+
+        if (o->outq)
+            pa_asyncmsgq_unref(o->outq);
+
+        pa_xfree(o);
+    }
+
+    return NULL;
+}
+
+static pa_bool_t is_suitable_sink(struct userdata *u, pa_sink *s) {
+    const char *t;
+
+    pa_sink_assert_ref(s);
+
+    if (!(s->flags & PA_SINK_HARDWARE))
+        return FALSE;
+
+    if (s == u->sink)
+        return FALSE;
+
+    if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS)))
+        if (strcmp(t, "sound"))
+            return FALSE;
+
+    return TRUE;
+}
+
+static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+    struct output *o;
+
+    pa_core_assert_ref(c);
+    pa_sink_assert_ref(s);
+    pa_assert(u);
+    pa_assert(u->automatic);
+
+    if (!is_suitable_sink(u, s))
+        return PA_HOOK_OK;
+
+    pa_log_info("Configuring new sink: %s", s->name);
+
+    if (!(o = output_new(u, s))) {
+        pa_log("Failed to create sink input on sink '%s'.", s->name);
+        return PA_HOOK_OK;
+    }
+
+    if (o->sink_input)
+        pa_sink_input_put(o->sink_input);
+
+    return PA_HOOK_OK;
+}
+
+static struct output* find_output(struct userdata *u, pa_sink *s) {
+    struct output *o;
+    uint32_t idx;
+
+    pa_assert(u);
+    pa_assert(s);
+
+    if (u->sink == s)
+        return NULL;
+
+    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
+        if (o->sink == s)
+            return o;
+
+    return NULL;
+}
+
+static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+    struct output *o;
+
+    pa_assert(c);
+    pa_sink_assert_ref(s);
+    pa_assert(u);
+
+    if (!(o = find_output(u, s)))
+        return PA_HOOK_OK;
+
+    pa_log_info("Unconfiguring sink: %s", s->name);
+
+    output_free(o);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
+    struct output *o;
+    pa_sink_state_t state;
+
+    if (!(o = find_output(u, s)))
+        return PA_HOOK_OK;
+
+    state = pa_sink_get_state(s);
+
+    if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input)
+        enable_output(o);
+
+    if (state == PA_SINK_SUSPENDED && o->sink_input)
+        disable_output(o);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    const char *slaves, *rm;
+    int resample_method = PA_RESAMPLER_TRIVIAL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    struct output *o;
+    uint32_t idx;
+    pa_sink_new_data data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments");
+        goto fail;
+    }
+
+    if ((rm = pa_modargs_get_value(ma, "resample_method", NULL))) {
+        if ((resample_method = pa_parse_resample_method(rm)) < 0) {
+            pa_log("invalid resample method '%s'", rm);
+            goto fail;
+        }
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->sink = NULL;
+    u->time_event = NULL;
+    u->adjust_time = DEFAULT_ADJUST_TIME;
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+    u->thread = NULL;
+    u->resample_method = resample_method;
+    u->outputs = pa_idxset_new(NULL, NULL);
+    memset(&u->adjust_timestamp, 0, sizeof(u->adjust_timestamp));
+    u->sink_put_slot = u->sink_unlink_slot = u->sink_state_changed_slot = NULL;
+    PA_LLIST_HEAD_INIT(struct output, u->thread_info.active_outputs);
+    pa_atomic_store(&u->thread_info.running, FALSE);
+    u->thread_info.in_null_mode = FALSE;
+    u->thread_info.counter = 0;
+    u->thread_info.smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+
+    if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) {
+        pa_log("Failed to parse adjust_time value");
+        goto fail;
+    }
+
+    slaves = pa_modargs_get_value(ma, "slaves", NULL);
+    u->automatic = !slaves;
+    ss = m->core->default_sample_spec;
+
+    if ((pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0)) {
+        pa_log("Invalid sample specification.");
+        goto fail;
+    }
+
+    pa_sink_new_data_init(&data);
+    data.namereg_fail = FALSE;
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output");
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+
+    if (slaves)
+        pa_proplist_sets(data.proplist, "combine.slaves", slaves);
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->set_state = sink_set_state;
+    u->sink->userdata = u;
+
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+
+    pa_sink_set_latency_range(u->sink, REQUEST_LATENCY_USEC, REQUEST_LATENCY_USEC);
+    u->block_usec = u->sink->thread_info.max_latency;
+
+    u->sink->thread_info.max_request =
+        pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
+    if (!u->automatic) {
+        const char*split_state;
+        char *n = NULL;
+        pa_assert(slaves);
+
+        /* The slaves have been specified manually */
+
+        split_state = NULL;
+        while ((n = pa_split(slaves, ",", &split_state))) {
+            pa_sink *slave_sink;
+
+            if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, TRUE)) || slave_sink == u->sink) {
+                pa_log("Invalid slave sink '%s'", n);
+                pa_xfree(n);
+                goto fail;
+            }
+
+            pa_xfree(n);
+
+            if (!output_new(u, slave_sink)) {
+                pa_log("Failed to create slave sink input on sink '%s'.", slave_sink->name);
+                goto fail;
+            }
+        }
+
+        if (pa_idxset_size(u->outputs) <= 1)
+            pa_log_warn("No slave sinks specified.");
+
+        u->sink_put_slot = NULL;
+
+    } else {
+        pa_sink *s;
+
+        /* We're in automatic mode, we add every sink that matches our needs  */
+
+        for (s = pa_idxset_first(m->core->sinks, &idx); s; s = pa_idxset_next(m->core->sinks, &idx)) {
+
+            if (!is_suitable_sink(u, s))
+                continue;
+
+            if (!output_new(u, s)) {
+                pa_log("Failed to create sink input on sink '%s'.", s->name);
+                goto fail;
+            }
+        }
+
+        u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_cb, u);
+    }
+
+    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_hook_cb, u);
+    u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_hook_cb, u);
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    /* Activate the sink and the sink inputs */
+    pa_sink_put(u->sink);
+
+    for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
+        if (o->sink_input)
+            pa_sink_input_put(o->sink_input);
+
+    if (u->adjust_time > 0) {
+        struct timeval tv;
+        pa_gettimeofday(&tv);
+        tv.tv_sec += u->adjust_time;
+        u->time_event = m->core->mainloop->time_new(m->core->mainloop, &tv, time_callback, u);
+    }
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+static void output_free(struct output *o) {
+    pa_assert(o);
+
+    disable_output(o);
+
+    pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL));
+
+    update_description(o->userdata);
+
+    if (o->inq_rtpoll_item_read)
+        pa_rtpoll_item_free(o->inq_rtpoll_item_read);
+    if (o->inq_rtpoll_item_write)
+        pa_rtpoll_item_free(o->inq_rtpoll_item_write);
+
+    if (o->outq_rtpoll_item_read)
+        pa_rtpoll_item_free(o->outq_rtpoll_item_read);
+    if (o->outq_rtpoll_item_write)
+        pa_rtpoll_item_free(o->outq_rtpoll_item_write);
+
+    if (o->inq)
+        pa_asyncmsgq_unref(o->inq);
+
+    if (o->outq)
+        pa_asyncmsgq_unref(o->outq);
+
+    if (o->memblockq)
+        pa_memblockq_free(o->memblockq);
+
+    pa_xfree(o);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    struct output *o;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink_put_slot)
+        pa_hook_slot_free(u->sink_put_slot);
+
+    if (u->sink_unlink_slot)
+        pa_hook_slot_free(u->sink_unlink_slot);
+
+    if (u->sink_state_changed_slot)
+        pa_hook_slot_free(u->sink_state_changed_slot);
+
+    if (u->outputs) {
+        while ((o = pa_idxset_first(u->outputs, NULL)))
+            output_free(o);
+
+        pa_idxset_free(u->outputs, NULL, NULL);
+    }
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->time_event)
+        u->core->mainloop->time_free(u->time_event);
+
+    if (u->thread_info.smoother)
+        pa_smoother_free(u->thread_info.smoother);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c
new file mode 100644 (file)
index 0000000..3adee99
--- /dev/null
@@ -0,0 +1,334 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2008 Lennart Poettering
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; 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 <stdlib.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/modargs.h>
+
+#include "dbus-util.h"
+#include "module-console-kit-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Create a client for each ConsoleKit session of this user");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct session {
+    char *id;
+    pa_client *client;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_dbus_connection *connection;
+    pa_hashmap *sessions;
+};
+
+static void add_session(struct userdata *u, const char *id) {
+    DBusError error;
+    DBusMessage *m = NULL, *reply = NULL;
+    int32_t uid;
+    struct session *session;
+    char *t;
+
+    dbus_error_init (&error);
+
+    if (pa_hashmap_get(u->sessions, id)) {
+        pa_log_warn("Duplicate session %s, ignoring.", id);
+        return;
+    }
+
+    if (!(m = dbus_message_new_method_call("org.freedesktop.ConsoleKit", id, "org.freedesktop.ConsoleKit.Session", "GetUnixUser"))) {
+        pa_log("Failed to allocate GetUnixUser() method call.");
+        goto fail;
+    }
+
+    if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
+        pa_log("GetUnixUser() call failed: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    /* FIXME: Why is this in int32? and not an uint32? */
+    if (!dbus_message_get_args(reply, &error, DBUS_TYPE_INT32, &uid, DBUS_TYPE_INVALID)) {
+        pa_log("Failed to parse GetUnixUser() result: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    /* We only care about our own sessions */
+    if ((uid_t) uid != getuid())
+        goto fail;
+
+    session = pa_xnew(struct session, 1);
+    session->id = pa_xstrdup(id);
+
+    t = pa_sprintf_malloc("ConsoleKit Session %s", id);
+    session->client = pa_client_new(u->core, __FILE__, t);
+    pa_xfree(t);
+
+    pa_proplist_sets(session->client->proplist, "console-kit.session", id);
+
+    pa_hashmap_put(u->sessions, session->id, session);
+
+    pa_log_debug("Added new session %s", id);
+
+fail:
+
+    if (m)
+        dbus_message_unref(m);
+
+    if (reply)
+        dbus_message_unref(reply);
+
+    dbus_error_free(&error);
+}
+
+static void free_session(struct session *session) {
+    pa_assert(session);
+
+    pa_log_debug("Removing session %s", session->id);
+
+    pa_client_free(session->client);
+    pa_xfree(session->id);
+    pa_xfree(session);
+}
+
+static void remove_session(struct userdata *u, const char *id) {
+    struct session *session;
+
+    if (!(session = pa_hashmap_remove(u->sessions, id)))
+        return;
+
+    free_session(session);
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
+    struct userdata *u = userdata;
+    DBusError error;
+    const char *path;
+
+    pa_assert(bus);
+    pa_assert(message);
+    pa_assert(u);
+
+    dbus_error_init(&error);
+
+    pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
+                 dbus_message_get_interface(message),
+                 dbus_message_get_path(message),
+                 dbus_message_get_member(message));
+
+    if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionAdded")) {
+
+        if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+            pa_log_error("Failed to parse SessionAdded message: %s: %s", error.name, error.message);
+            goto finish;
+        }
+
+        add_session(u, path);
+
+    } else if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionRemoved")) {
+
+        if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+            pa_log_error("Failed to parse SessionRemoved message: %s: %s", error.name, error.message);
+            goto finish;
+        }
+
+        remove_session(u, path);
+    }
+
+finish:
+    dbus_error_free(&error);
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static int get_session_list(struct userdata *u) {
+    DBusError error;
+    DBusMessage *m = NULL, *reply = NULL;
+    uint32_t uid;
+    DBusMessageIter iter, sub;
+    int ret = -1;
+
+    pa_assert(u);
+
+    dbus_error_init(&error);
+
+    if (!(m = dbus_message_new_method_call("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "GetSessionsForUnixUser"))) {
+        pa_log("Failed to allocate GetSessionsForUnixUser() method call.");
+        goto fail;
+    }
+
+    uid = (uint32_t) getuid();
+    if (!(dbus_message_append_args(m, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID))) {
+        pa_log("Failed to append arguments to GetSessionsForUnixUser() method call.");
+        goto fail;
+    }
+
+    if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
+        pa_log("GetSessionsForUnixUser() call failed: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    dbus_message_iter_init(reply, &iter);
+
+    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+        dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_OBJECT_PATH) {
+        pa_log("Failed to parse GetSessionsForUnixUser() result.");
+        goto fail;
+    }
+
+    dbus_message_iter_recurse(&iter, &sub);
+
+    for (;;) {
+        int at;
+        const char *id;
+
+        if ((at = dbus_message_iter_get_arg_type(&sub)) == DBUS_TYPE_INVALID)
+            break;
+
+        assert(at == DBUS_TYPE_OBJECT_PATH);
+        dbus_message_iter_get_basic(&sub, &id);
+
+        add_session(u, id);
+
+        dbus_message_iter_next(&sub);
+    }
+
+    ret = 0;
+
+fail:
+
+    if (m)
+        dbus_message_unref(m);
+
+    if (reply)
+        dbus_message_unref(reply);
+
+    dbus_error_free(&error);
+
+    return ret;
+}
+
+int pa__init(pa_module*m) {
+    DBusError error;
+    pa_dbus_connection *connection;
+    struct userdata *u = NULL;
+    pa_modargs *ma;
+
+    pa_assert(m);
+
+    dbus_error_init(&error);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
+
+        if (connection)
+            pa_dbus_connection_unref(connection);
+
+        pa_log_error("Unable to contact D-Bus system bus: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->connection = connection;
+    u->sessions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    if (!dbus_connection_add_filter(pa_dbus_connection_get(connection), filter_cb, u, NULL)) {
+        pa_log_error("Failed to add filter function");
+        goto fail;
+    }
+
+    dbus_bus_add_match(pa_dbus_connection_get(connection), "type='signal',sender='org.freedesktop.ConsoleKit', interface='org.freedesktop.ConsoleKit.Seat'", &error);
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("Unable to subscribe to ConsoleKit signals: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    if (get_session_list(u) < 0)
+        goto fail;
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    dbus_error_free(&error);
+    pa__done(m);
+
+    return -1;
+}
+
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+    struct session *session;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sessions) {
+        while ((session = pa_hashmap_steal_first(u->sessions)))
+            free_session(session);
+
+        pa_hashmap_free(u->sessions, NULL, NULL);
+    }
+
+    if (u->connection)
+        pa_dbus_connection_unref(u->connection);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
new file mode 100644 (file)
index 0000000..2168ac7
--- /dev/null
@@ -0,0 +1,201 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-error.h>
+
+#include "module-default-device-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the default sink and source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define DEFAULT_SINK_FILE "default-sink"
+#define DEFAULT_SOURCE_FILE "default-source"
+#define DEFAULT_SAVE_INTERVAL 5
+
+struct userdata {
+    pa_core *core;
+    pa_subscription *subscription;
+    pa_time_event *time_event;
+    char *sink_filename, *source_filename;
+    pa_bool_t modified;
+};
+
+static void load(struct userdata *u) {
+    FILE *f;
+
+    /* We never overwrite manually configured settings */
+
+    if (u->core->default_sink_name)
+        pa_log_info("Manually configured default sink, not overwriting.");
+    else if ((f = fopen(u->sink_filename, "r"))) {
+        char ln[256] = "";
+
+        fgets(ln, sizeof(ln)-1, f);
+        pa_strip_nl(ln);
+        fclose(f);
+
+        if (!ln[0])
+            pa_log_info("No previous default sink setting, ignoring.");
+        else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) {
+            pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
+            pa_log_info("Restored default sink '%s'.", ln);
+        } else
+            pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln);
+
+    } else if (errno != ENOENT)
+        pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
+
+    if (u->core->default_source_name)
+        pa_log_info("Manually configured default source, not overwriting.");
+    else if ((f = fopen(u->source_filename, "r"))) {
+        char ln[256] = "";
+
+        fgets(ln, sizeof(ln)-1, f);
+        pa_strip_nl(ln);
+        fclose(f);
+
+        if (!ln[0])
+            pa_log_info("No previous default source setting, ignoring.");
+        else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) {
+            pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
+            pa_log_info("Restored default source '%s'.", ln);
+        } else
+            pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
+
+    } else if (errno != ENOENT)
+            pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
+}
+
+static void save(struct userdata *u) {
+    FILE *f;
+
+    if (!u->modified)
+        return;
+
+    if (u->sink_filename) {
+        if ((f = fopen(u->sink_filename, "w"))) {
+            const char *n = pa_namereg_get_default_sink_name(u->core);
+            fprintf(f, "%s\n", pa_strempty(n));
+            fclose(f);
+        } else
+            pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
+    }
+
+    if (u->source_filename) {
+        if ((f = fopen(u->source_filename, "w"))) {
+            const char *n = pa_namereg_get_default_source_name(u->core);
+            fprintf(f, "%s\n", pa_strempty(n));
+            fclose(f);
+        } else
+            pa_log("Failed to save default source: %s", pa_cstrerror(errno));
+    }
+
+    u->modified = FALSE;
+}
+
+static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+    save(u);
+
+    if (u->time_event) {
+        u->core->mainloop->time_free(u->time_event);
+        u->time_event = NULL;
+    }
+}
+
+static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    u->modified = TRUE;
+
+    if (!u->time_event) {
+        struct timeval tv;
+        pa_gettimeofday(&tv);
+        pa_timeval_add(&tv, DEFAULT_SAVE_INTERVAL*PA_USEC_PER_SEC);
+        u->time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, time_cb, u);
+    }
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+
+    if (!(u->sink_filename = pa_state_path(DEFAULT_SINK_FILE)))
+        goto fail;
+
+    if (!(u->source_filename = pa_state_path(DEFAULT_SOURCE_FILE)))
+        goto fail;
+
+    load(u);
+
+    u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    save(u);
+
+    if (u->subscription)
+        pa_subscription_free(u->subscription);
+
+    if (u->time_event)
+        m->core->mainloop->time_free(u->time_event);
+
+    pa_xfree(u->sink_filename);
+    pa_xfree(u->source_filename);
+    pa_xfree(u);
+}
similarity index 50%
rename from polyp/module-defs.h.m4
rename to src/modules/module-defs.h.m4
index 2b9cdf9e26533c480f0334e9012da4f064682609..64ce1928911f711a5198c4c80da317dce510de8d 100644 (file)
@@ -1,6 +1,5 @@
-dnl $Id$
 changecom(`/*', `*/')dnl
-define(`module_name', patsubst(patsubst(fname, `-symdef.h$'), `[^0-9a-zA-Z]', `_'))dnl
+define(`module_name', patsubst(patsubst(patsubst(fname, `-symdef.h$'), `^.*/'), `[^0-9a-zA-Z]', `_'))dnl
 define(`c_symbol', patsubst(module_name, `[^0-9a-zA-Z]', `_'))dnl
 define(`c_macro', patsubst(module_name, `[^0-9a-zA-Z]', `'))dnl
 define(`incmacro', `foo'c_macro`symdeffoo')dnl
@@ -8,8 +7,9 @@ define(`gen_symbol', `#define $1 'module_name`_LTX_$1')dnl
 #ifndef incmacro
 #define incmacro
 
-#include "core.h"
-#include "module.h"
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/macro.h>
 
 gen_symbol(pa__init)
 gen_symbol(pa__done)
@@ -17,8 +17,15 @@ gen_symbol(pa__get_author)
 gen_symbol(pa__get_description)
 gen_symbol(pa__get_usage)
 gen_symbol(pa__get_version)
+gen_symbol(pa__load_once)
 
-int pa__init(struct pa_core *c, struct pa_module*m);
-void pa__done(struct pa_core *c, struct pa_module*m);
+int pa__init(pa_module*m);
+void pa__done(pa_module*m);
+
+const char* pa__get_author(void);
+const char* pa__get_description(void);
+const char* pa__get_usage(void);
+const char* pa__get_version(void);
+pa_bool_t pa__load_once(void);
 
 #endif
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
new file mode 100644 (file)
index 0000000..13bcfcd
--- /dev/null
@@ -0,0 +1,270 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+  Copyright 2006 Diego Pettenò
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "module-detect-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("just-one=<boolean>");
+
+static const char* const valid_modargs[] = {
+    "just-one",
+    NULL
+};
+
+#ifdef HAVE_ALSA
+
+static int detect_alsa(pa_core *c, int just_one) {
+    FILE *f;
+    int n = 0, n_sink = 0, n_source = 0;
+
+    if (!(f = fopen("/proc/asound/devices", "r"))) {
+
+        if (errno != ENOENT)
+            pa_log_error("open(\"/proc/asound/devices\") failed: %s", pa_cstrerror(errno));
+
+        return -1;
+    }
+
+    while (!feof(f)) {
+        char line[64], args[64];
+        unsigned device, subdevice;
+        int is_sink;
+
+        if (!fgets(line, sizeof(line), f))
+            break;
+
+        line[strcspn(line, "\r\n")] = 0;
+
+        if (pa_endswith(line, "digital audio playback"))
+            is_sink = 1;
+        else if (pa_endswith(line, "digital audio capture"))
+            is_sink = 0;
+        else
+            continue;
+
+        if (just_one && is_sink && n_sink >= 1)
+            continue;
+
+        if (just_one && !is_sink && n_source >= 1)
+            continue;
+
+        if (sscanf(line, " %*i: [%u- %u]: ", &device, &subdevice) != 2)
+            continue;
+
+        /* Only one sink per device */
+        if (subdevice != 0)
+            continue;
+
+        pa_snprintf(args, sizeof(args), "device=hw:%u", device);
+        if (!pa_module_load(c, is_sink ? "module-alsa-sink" : "module-alsa-source", args))
+            continue;
+
+        n++;
+
+        if (is_sink)
+            n_sink++;
+        else
+            n_source++;
+    }
+
+    fclose(f);
+
+    return n;
+}
+#endif
+
+#ifdef HAVE_OSS
+static int detect_oss(pa_core *c, int just_one) {
+    FILE *f;
+    int n = 0, b = 0;
+
+    if (!(f = fopen("/dev/sndstat", "r")) &&
+        !(f = fopen("/proc/sndstat", "r")) &&
+        !(f = fopen("/proc/asound/oss/sndstat", "r"))) {
+
+        if (errno != ENOENT)
+            pa_log_error("failed to open OSS sndstat device: %s", pa_cstrerror(errno));
+
+        return -1;
+    }
+
+    while (!feof(f)) {
+        char line[64], args[64];
+        unsigned device;
+
+        if (!fgets(line, sizeof(line), f))
+            break;
+
+        line[strcspn(line, "\r\n")] = 0;
+
+        if (!b) {
+             b = strcmp(line, "Audio devices:") == 0 || strcmp(line, "Installed devices:") == 0;
+            continue;
+        }
+
+        if (line[0] == 0)
+            break;
+
+        if (sscanf(line, "%u: ", &device) == 1) {
+            if (device == 0)
+                pa_snprintf(args, sizeof(args), "device=/dev/dsp");
+            else
+                pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", device);
+
+            if (!pa_module_load(c, "module-oss", args))
+                continue;
+
+        } else if (sscanf(line, "pcm%u: ", &device) == 1) {
+            /* FreeBSD support, the devices are named /dev/dsp0.0, dsp0.1 and so on */
+            pa_snprintf(args, sizeof(args), "device=/dev/dsp%u.0", device);
+
+            if (!pa_module_load(c, "module-oss", args))
+                continue;
+        }
+
+        n++;
+
+        if (just_one)
+            break;
+    }
+
+    fclose(f);
+    return n;
+}
+#endif
+
+#ifdef HAVE_SOLARIS
+static int detect_solaris(pa_core *c, int just_one) {
+    struct stat s;
+    const char *dev;
+    char args[64];
+
+    dev = getenv("AUDIODEV");
+    if (!dev)
+        dev = "/dev/audio";
+
+    if (stat(dev, &s) < 0) {
+        if (errno != ENOENT)
+            pa_log_error("failed to open device %s: %s", dev, pa_cstrerror(errno));
+        return -1;
+    }
+
+    if (!S_ISCHR(s.st_mode))
+        return 0;
+
+    pa_snprintf(args, sizeof(args), "device=%s", dev);
+
+    if (!pa_module_load(c, "module-solaris", args))
+        return 0;
+
+    return 1;
+}
+#endif
+
+#ifdef OS_IS_WIN32
+static int detect_waveout(pa_core *c, int just_one) {
+    /*
+     * FIXME: No point in enumerating devices until the plugin supports
+     * selecting anything but the first.
+     */
+    if (!pa_module_load(c, "module-waveout", ""))
+        return 0;
+
+    return 1;
+}
+#endif
+
+int pa__init(pa_module*m) {
+    pa_bool_t just_one = FALSE;
+    int n = 0;
+    pa_modargs *ma;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "just-one", &just_one) < 0) {
+        pa_log("just_one= expects a boolean argument.");
+        goto fail;
+    }
+
+#if HAVE_ALSA
+    if ((n = detect_alsa(m->core, just_one)) <= 0)
+#endif
+#if HAVE_OSS
+    if ((n = detect_oss(m->core, just_one)) <= 0)
+#endif
+#if HAVE_SOLARIS
+    if ((n = detect_solaris(m->core, just_one)) <= 0)
+#endif
+#if OS_IS_WIN32
+    if ((n = detect_waveout(m->core, just_one)) <= 0)
+#endif
+    {
+        pa_log_warn("failed to detect any sound hardware.");
+        goto fail;
+    }
+
+    pa_log_info("loaded %i modules.", n);
+
+    /* We were successful and can unload ourselves now. */
+    pa_module_unload_request(m);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
new file mode 100644 (file)
index 0000000..b7a1e1b
--- /dev/null
@@ -0,0 +1,348 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <gdbm.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/namereg.h>
+
+#include "module-device-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SAVE_INTERVAL 10
+
+static const char* const valid_modargs[] = {
+    NULL,
+};
+
+struct userdata {
+    pa_core *core;
+    pa_subscription *subscription;
+    pa_hook_slot *sink_fixate_hook_slot, *source_fixate_hook_slot;
+    pa_time_event *save_time_event;
+    GDBM_FILE gdbm_file;
+};
+
+struct entry {
+    pa_cvolume volume;
+    int muted;
+};
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(tv);
+    pa_assert(u);
+
+    pa_assert(e == u->save_time_event);
+    u->core->mainloop->time_free(u->save_time_event);
+    u->save_time_event = NULL;
+
+    gdbm_sync(u->gdbm_file);
+    pa_log_info("Synced.");
+}
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    struct userdata *u = userdata;
+    struct entry entry;
+    char *name;
+    datum key, data;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
+        pa_sink *sink;
+
+        if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
+            return;
+
+        name = pa_sprintf_malloc("sink:%s", sink->name);
+        entry.volume = *pa_sink_get_volume(sink);
+        entry.muted = pa_sink_get_mute(sink);
+
+    } else {
+        pa_source *source;
+
+        pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
+
+        if (!(source = pa_idxset_get_by_index(c->sources, idx)))
+            return;
+
+        name = pa_sprintf_malloc("source:%s", source->name);
+        entry.volume = *pa_source_get_volume(source);
+        entry.muted = pa_source_get_mute(source);
+    }
+
+    key.dptr = name;
+    key.dsize = strlen(name);
+
+    data = gdbm_fetch(u->gdbm_file, key);
+
+    if (data.dptr) {
+
+        if (data.dsize == sizeof(struct entry)) {
+            struct entry *old = (struct entry*) data.dptr;
+
+            if (pa_cvolume_valid(&old->volume)) {
+
+                if (pa_cvolume_equal(&old->volume, &entry.volume) &&
+                    !old->muted == !entry.muted) {
+
+                    pa_xfree(data.dptr);
+                    pa_xfree(name);
+                    return;
+                }
+            } else
+                pa_log_warn("Invalid volume stored in database for device %s", name);
+
+        } else
+            pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+
+        pa_xfree(data.dptr);
+    }
+
+    data.dptr = (void*) &entry;
+    data.dsize = sizeof(entry);
+
+    pa_log_info("Storing volume/mute for device %s.", name);
+
+    gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
+
+    if (!u->save_time_event) {
+        struct timeval tv;
+        pa_gettimeofday(&tv);
+        tv.tv_sec += SAVE_INTERVAL;
+        u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
+    }
+
+    pa_xfree(name);
+}
+
+static struct entry* read_entry(struct userdata *u, char *name) {
+    datum key, data;
+    struct entry *e;
+
+    pa_assert(u);
+    pa_assert(name);
+
+    key.dptr = name;
+    key.dsize = strlen(name);
+
+    data = gdbm_fetch(u->gdbm_file, key);
+
+    if (!data.dptr)
+        goto fail;
+
+    if (data.dsize != sizeof(struct entry)) {
+        pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+        goto fail;
+    }
+
+    e = (struct entry*) data.dptr;
+
+    if (!(pa_cvolume_valid(&e->volume))) {
+        pa_log_warn("Invalid volume stored in database for device %s", name);
+        goto fail;
+    }
+
+    return e;
+
+fail:
+
+    pa_xfree(data.dptr);
+    return NULL;
+}
+
+
+static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(new_data);
+
+    name = pa_sprintf_malloc("sink:%s", new_data->name);
+
+    if ((e = read_entry(u, name))) {
+
+        if (e->volume.channels == new_data->sample_spec.channels) {
+            pa_log_info("Restoring volume for sink %s.", new_data->name);
+            pa_sink_new_data_set_volume(new_data, &e->volume);
+        }
+
+        pa_log_info("Restoring mute state for sink %s.", new_data->name);
+        pa_sink_new_data_set_muted(new_data, e->muted);
+        pa_xfree(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(new_data);
+
+    name = pa_sprintf_malloc("source:%s", new_data->name);
+
+    if ((e = read_entry(u, name))) {
+
+        if (e->volume.channels == new_data->sample_spec.channels) {
+            pa_log_info("Restoring volume for source %s.", new_data->name);
+            pa_source_new_data_set_volume(new_data, &e->volume);
+        }
+
+        pa_log_info("Restoring mute state for source %s.", new_data->name);
+        pa_source_new_data_set_muted(new_data, e->muted);
+        pa_xfree(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    char *fname, *fn;
+    char hn[256];
+    pa_sink *sink;
+    pa_source *source;
+    uint32_t idx;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->save_time_event = NULL;
+
+    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
+
+    u->sink_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_fixate_hook_callback, u);
+    u->source_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_fixate_hook_callback, u);
+
+    m->userdata = u;
+
+    if (!pa_get_host_name(hn, sizeof(hn)))
+        goto fail;
+
+    fn = pa_sprintf_malloc("device-volumes.%s.gdbm", hn);
+    fname = pa_state_path(fn);
+    pa_xfree(fn);
+
+    if (!fname)
+        goto fail;
+
+    if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) {
+        pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
+        pa_xfree(fname);
+        goto fail;
+    }
+
+    pa_log_info("Sucessfully opened database file '%s'.", fname);
+    pa_xfree(fname);
+
+    for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
+
+    for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return  -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->subscription)
+        pa_subscription_free(u->subscription);
+
+    if (u->sink_fixate_hook_slot)
+        pa_hook_slot_free(u->sink_fixate_hook_slot);
+    if (u->source_fixate_hook_slot)
+        pa_hook_slot_free(u->source_fixate_hook_slot);
+
+    if (u->save_time_event)
+        u->core->mainloop->time_free(u->save_time_event);
+
+    if (u->gdbm_file)
+        gdbm_close(u->gdbm_file);
+
+    pa_xfree(u);
+}
similarity index 56%
rename from polyp/module-esound-compat-spawnfd.c
rename to src/modules/module-esound-compat-spawnfd.c
index de2cf4e6381aeff7e1bc58f102e72d3618b1cd9e..8eb4985b2bbcf4d861c81e04ec0e0157758d0a00 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #endif
 
 #include <unistd.h>
-#include <assert.h>
 #include <string.h>
 #include <errno.h>
 
-#include "module.h"
-#include "util.h"
-#include "modargs.h"
-#include "log.h"
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
 #include "module-esound-compat-spawnfd-symdef.h"
 
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnfd emulation")
-PA_MODULE_USAGE("fd=<file descriptor>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnfd emulation");
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_USAGE("fd=<file descriptor>");
 
 static const char* const valid_modargs[] = {
     "fd",
     NULL,
 };
 
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct pa_modargs *ma = NULL;
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
     int ret = -1, fd = -1;
     char x = 1;
-    assert(c && m);
+
+    pa_assert(m);
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
         pa_modargs_get_value_s32(ma, "fd", &fd) < 0 ||
         fd < 0) {
-        pa_log(__FILE__": Failed to parse module arguments\n");
+
+        pa_log("Failed to parse module arguments");
         goto finish;
     }
 
-    if (pa_loop_write(fd, &x, sizeof(x)) != sizeof(x))
-        pa_log(__FILE__": WARNING: write(%u, 1, 1) failed: %s\n", fd, strerror(errno));
+    if (pa_loop_write(fd, &x, sizeof(x), NULL) != sizeof(x))
+        pa_log_warn("write(%u, 1, 1) failed: %s", fd, pa_cstrerror(errno));
 
-    close(fd);
+    pa_assert_se(pa_close(fd) == 0);
 
     pa_module_unload_request(m);
 
@@ -72,9 +76,3 @@ finish:
 
     return ret;
 }
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    assert(c && m);
-}
-
-
similarity index 60%
rename from polyp/module-esound-compat-spawnpid.c
rename to src/modules/module-esound-compat-spawnpid.c
index 6f945728ea96faaa28ad0305fe6b4ce01a52c38f..67f0a2312a31e219ea1f418db69e23797a6dd8b9 100644 (file)
@@ -1,19 +1,21 @@
 
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #endif
 
 #include <unistd.h>
-#include <assert.h>
 #include <string.h>
 #include <errno.h>
 #include <signal.h>
 
-#include "module.h"
-#include "util.h"
-#include "modargs.h"
-#include "log.h"
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+
 #include "module-esound-compat-spawnpid-symdef.h"
 
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnpid emulation")
-PA_MODULE_USAGE("pid=<process id>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnpid emulation");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("pid=<process id>");
 
 static const char* const valid_modargs[] = {
     "pid",
     NULL,
 };
 
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct pa_modargs *ma = NULL;
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
     int ret = -1;
     uint32_t pid = 0;
-    assert(c && m);
+
+    pa_assert(m);
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs)) ||
         pa_modargs_get_value_u32(ma, "pid", &pid) < 0 ||
         !pid) {
-        pa_log(__FILE__": Failed to parse module arguments\n");
+        pa_log("Failed to parse module arguments");
         goto finish;
     }
 
     if (kill(pid, SIGUSR1) < 0)
-        pa_log(__FILE__": WARNING: kill(%u) failed: %s\n", pid, strerror(errno));
+        pa_log_warn("kill(%u) failed: %s", pid, pa_cstrerror(errno));
 
     pa_module_unload_request(m);
 
@@ -70,9 +75,3 @@ finish:
 
     return ret;
 }
-
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    assert(c && m);
-}
-
-
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
new file mode 100644 (file)
index 0000000..e189feb
--- /dev/null
@@ -0,0 +1,665 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_LINUX_SOCKIOS_H
+#include <linux/sockios.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/esound.h>
+#include <pulsecore/authkey.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/socket-util.h>
+
+#include "module-esound-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ESOUND Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "server=<address> cookie=<filename>  "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate>");
+
+#define DEFAULT_SINK_NAME "esound_out"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+    pa_thread *thread;
+
+    pa_memchunk memchunk;
+
+    void *write_data;
+    size_t write_length, write_index;
+
+    void *read_data;
+    size_t read_length, read_index;
+
+    enum {
+        STATE_AUTH,
+        STATE_LATENCY,
+        STATE_PREPARE,
+        STATE_RUNNING,
+        STATE_DEAD
+    } state;
+
+    pa_usec_t latency;
+
+    esd_format_t format;
+    int32_t rate;
+
+    pa_smoother *smoother;
+    int fd;
+
+    int64_t offset;
+
+    pa_iochannel *io;
+    pa_socket_client *client;
+
+    size_t block_size;
+};
+
+static const char* const valid_modargs[] = {
+    "server",
+    "cookie",
+    "rate",
+    "format",
+    "channels",
+    "sink_name",
+    NULL
+};
+
+enum {
+    SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX
+};
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SINK_SUSPENDED:
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+                    pa_smoother_pause(u->smoother, pa_rtclock_usec());
+                    break;
+
+                case PA_SINK_IDLE:
+                case PA_SINK_RUNNING:
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
+                        pa_smoother_resume(u->smoother, pa_rtclock_usec());
+
+                    break;
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                    ;
+            }
+
+            break;
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t w, r;
+
+            r = pa_smoother_get(u->smoother, pa_rtclock_usec());
+            w = pa_bytes_to_usec(u->offset + u->memchunk.length, &u->sink->sample_spec);
+
+            *((pa_usec_t*) data) = w > r ? w - r : 0;
+            break;
+        }
+
+        case SINK_MESSAGE_PASS_SOCKET: {
+            struct pollfd *pollfd;
+
+            pa_assert(!u->rtpoll_item);
+
+            u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+            pollfd->fd = u->fd;
+            pollfd->events = pollfd->revents = 0;
+
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    int write_type = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
+    for (;;) {
+        int ret;
+
+        if (u->rtpoll_item) {
+            struct pollfd *pollfd;
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+            /* Render some data and write it to the fifo */
+            if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+                pa_usec_t usec;
+                int64_t n;
+
+                for (;;) {
+                    ssize_t l;
+                    void *p;
+
+                    if (u->memchunk.length <= 0)
+                        pa_sink_render(u->sink, u->block_size, &u->memchunk);
+
+                    pa_assert(u->memchunk.length > 0);
+
+                    p = pa_memblock_acquire(u->memchunk.memblock);
+                    l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
+                    pa_memblock_release(u->memchunk.memblock);
+
+                    pa_assert(l != 0);
+
+                    if (l < 0) {
+
+                        if (errno == EINTR)
+                            continue;
+                        else if (errno == EAGAIN) {
+
+                            /* OK, we filled all socket buffers up
+                             * now. */
+                            goto filled_up;
+
+                        } else {
+                            pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+                            goto fail;
+                        }
+
+                    } else {
+                        u->offset += l;
+
+                        u->memchunk.index += l;
+                        u->memchunk.length -= l;
+
+                        if (u->memchunk.length <= 0) {
+                            pa_memblock_unref(u->memchunk.memblock);
+                            pa_memchunk_reset(&u->memchunk);
+                        }
+
+                        pollfd->revents = 0;
+
+                        if (u->memchunk.length > 0)
+
+                            /* OK, we wrote less that we asked for,
+                             * hence we can assume that the socket
+                             * buffers are full now */
+                            goto filled_up;
+                    }
+                }
+
+            filled_up:
+
+                /* At this spot we know that the socket buffers are
+                 * fully filled up. This is the best time to estimate
+                 * the playback position of the server */
+
+                n = u->offset;
+
+#ifdef SIOCOUTQ
+                {
+                    int l;
+                    if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
+                        n -= l;
+                }
+#endif
+
+                usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
+
+                if (usec > u->latency)
+                    usec -= u->latency;
+                else
+                    usec = 0;
+
+                pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
+            }
+
+            /* Hmm, nothing to do. Let's sleep */
+            pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
+        }
+
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        if (u->rtpoll_item) {
+            struct pollfd* pollfd;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+            if (pollfd->revents & ~POLLOUT) {
+                pa_log("FIFO shutdown.");
+                goto fail;
+            }
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+static int do_write(struct userdata *u) {
+    ssize_t r;
+    pa_assert(u);
+
+    if (!pa_iochannel_is_writable(u->io))
+        return 0;
+
+    if (u->write_data) {
+        pa_assert(u->write_index < u->write_length);
+
+        if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) <= 0) {
+            pa_log("write() failed: %s", pa_cstrerror(errno));
+            return -1;
+        }
+
+        u->write_index += r;
+        pa_assert(u->write_index <= u->write_length);
+
+        if (u->write_index == u->write_length) {
+            pa_xfree(u->write_data);
+            u->write_data = NULL;
+            u->write_index = u->write_length = 0;
+        }
+    }
+
+    if (!u->write_data && u->state == STATE_PREPARE) {
+        /* OK, we're done with sending all control data we need to, so
+         * let's hand the socket over to the IO thread now */
+
+        pa_assert(u->fd < 0);
+        u->fd = pa_iochannel_get_send_fd(u->io);
+
+        pa_iochannel_set_noclose(u->io, TRUE);
+        pa_iochannel_free(u->io);
+        u->io = NULL;
+
+        pa_make_tcp_socket_low_delay(u->fd);
+
+        pa_log_debug("Connection authenticated, handing fd to IO thread...");
+
+        pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL);
+        u->state = STATE_RUNNING;
+    }
+
+    return 0;
+}
+
+static int handle_response(struct userdata *u) {
+    pa_assert(u);
+
+    switch (u->state) {
+
+        case STATE_AUTH:
+            pa_assert(u->read_length == sizeof(int32_t));
+
+            /* Process auth data */
+            if (!*(int32_t*) u->read_data) {
+                pa_log("Authentication failed: %s", pa_cstrerror(errno));
+                return -1;
+            }
+
+            /* Request latency data */
+            pa_assert(!u->write_data);
+            *(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY;
+
+            u->write_index = 0;
+            u->state = STATE_LATENCY;
+
+            /* Space for next response */
+            pa_assert(u->read_length >= sizeof(int32_t));
+            u->read_index = 0;
+            u->read_length = sizeof(int32_t);
+
+            break;
+
+        case STATE_LATENCY: {
+            int32_t *p;
+            pa_assert(u->read_length == sizeof(int32_t));
+
+            /* Process latency info */
+            u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100);
+            if (u->latency > 10000000) {
+                pa_log_warn("Invalid latency information received from server");
+                u->latency = 0;
+            }
+
+            /* Create stream */
+            pa_assert(!u->write_data);
+            p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX);
+            *(p++) = ESD_PROTO_STREAM_PLAY;
+            *(p++) = u->format;
+            *(p++) = u->rate;
+            pa_strlcpy((char*) p, "PulseAudio Tunnel", ESD_NAME_MAX);
+
+            u->write_index = 0;
+            u->state = STATE_PREPARE;
+
+            /* Don't read any further */
+            pa_xfree(u->read_data);
+            u->read_data = NULL;
+            u->read_index = u->read_length = 0;
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    return 0;
+}
+
+static int do_read(struct userdata *u) {
+    pa_assert(u);
+
+    if (!pa_iochannel_is_readable(u->io))
+        return 0;
+
+    if (u->state == STATE_AUTH || u->state == STATE_LATENCY) {
+        ssize_t r;
+
+        if (!u->read_data)
+            return 0;
+
+        pa_assert(u->read_index < u->read_length);
+
+        if ((r = pa_iochannel_read(u->io, (uint8_t*) u->read_data + u->read_index, u->read_length - u->read_index)) <= 0) {
+            pa_log("read() failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            return -1;
+        }
+
+        u->read_index += r;
+        pa_assert(u->read_index <= u->read_length);
+
+        if (u->read_index == u->read_length)
+            return handle_response(u);
+    }
+
+    return 0;
+}
+
+static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
+    struct userdata *u = userdata;
+    pa_assert(u);
+
+    if (do_read(u) < 0 || do_write(u) < 0) {
+
+        if (u->io) {
+            pa_iochannel_free(u->io);
+            u->io = NULL;
+        }
+
+       pa_module_unload_request(u->module);
+    }
+}
+
+static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_socket_client_unref(u->client);
+    u->client = NULL;
+
+    if (!io) {
+        pa_log("Connection failed: %s", pa_cstrerror(errno));
+        pa_module_unload_request(u->module);
+        return;
+    }
+
+    pa_assert(!u->io);
+    u->io = io;
+    pa_iochannel_set_callback(u->io, io_callback, u);
+
+    pa_log_debug("Connection established, authenticating ...");
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    pa_modargs *ma = NULL;
+    const char *espeaker;
+    uint32_t key;
+    pa_sink_new_data data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+        pa_log("invalid sample format specification");
+        goto fail;
+    }
+
+    if ((ss.format != PA_SAMPLE_U8 && ss.format != PA_SAMPLE_S16NE) ||
+        (ss.channels > 2)) {
+        pa_log("esound sample type support is limited to mono/stereo and U8 or S16NE sample data");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->fd = -1;
+    u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+    pa_memchunk_reset(&u->memchunk);
+    u->offset = 0;
+
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+    u->rtpoll_item = NULL;
+
+    u->format =
+        (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) |
+        (ss.channels == 2 ? ESD_STEREO : ESD_MONO);
+    u->rate = ss.rate;
+    u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss);
+
+    u->read_data = u->write_data = NULL;
+    u->read_index = u->write_index = u->read_length = u->write_length = 0;
+
+    u->state = STATE_AUTH;
+    u->latency = 0;
+
+    if (!(espeaker = getenv("ESPEAKER")))
+        espeaker = ESD_UNIX_SOCKET_NAME;
+
+    espeaker = pa_modargs_get_value(ma, "server", espeaker);
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, espeaker);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Esound sink '%s'", espeaker);
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    if (!(u->client = pa_socket_client_new_string(u->core->mainloop, espeaker, ESD_DEFAULT_PORT))) {
+        pa_log("Failed to connect to server.");
+        goto fail;
+    }
+
+    pa_socket_client_set_callback(u->client, on_connection, u);
+
+    /* Prepare the initial request */
+    u->write_data = pa_xmalloc(u->write_length = ESD_KEY_LEN + sizeof(int32_t));
+    if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", ".esd_auth"), u->write_data, ESD_KEY_LEN) < 0) {
+        pa_log("Failed to load cookie");
+        goto fail;
+    }
+
+    key = ESD_ENDIAN_KEY;
+    memcpy((uint8_t*) u->write_data + ESD_KEY_LEN, &key, sizeof(key));
+
+    /* Reserve space for the response */
+    u->read_data = pa_xmalloc(u->read_length = sizeof(int32_t));
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_sink_put(u->sink);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->io)
+        pa_iochannel_free(u->io);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->client)
+        pa_socket_client_unref(u->client);
+
+    pa_xfree(u->read_data);
+    pa_xfree(u->write_data);
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
+    if (u->fd >= 0)
+        pa_close(u->fd);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
new file mode 100644 (file)
index 0000000..19430a3
--- /dev/null
@@ -0,0 +1,849 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2006 Lennart Poettering
+    Copyright 2006 Shams E. King
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2 of the License,
+    or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with PulseAudio; 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 <stdlib.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/modargs.h>
+
+#include <hal/libhal.h>
+
+#include "dbus-util.h"
+#include "module-hal-detect-symdef.h"
+
+PA_MODULE_AUTHOR("Shahms King");
+PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+#if defined(HAVE_ALSA) && defined(HAVE_OSS)
+PA_MODULE_USAGE("api=<alsa or oss>");
+#elif defined(HAVE_ALSA)
+PA_MODULE_USAGE("api=<alsa>");
+#elif defined(HAVE_OSS)
+PA_MODULE_USAGE("api=<oss>");
+#endif
+
+struct device {
+    uint32_t index;
+    char *udi;
+    char *sink_name, *source_name;
+    int acl_race_fix;
+};
+
+struct userdata {
+    pa_core *core;
+    LibHalContext *context;
+    pa_dbus_connection *connection;
+    pa_hashmap *devices;
+    const char *capability;
+};
+
+struct timerdata {
+    struct userdata *u;
+    char *udi;
+};
+
+#define CAPABILITY_ALSA "alsa"
+#define CAPABILITY_OSS "oss"
+
+static const char* const valid_modargs[] = {
+    "api",
+    NULL
+};
+
+static void hal_device_free(struct device* d) {
+    pa_assert(d);
+
+    pa_xfree(d->udi);
+    pa_xfree(d->sink_name);
+    pa_xfree(d->source_name);
+    pa_xfree(d);
+}
+
+static void hal_device_free_cb(void *d, PA_GCC_UNUSED void *data) {
+    hal_device_free(d);
+}
+
+static const char *strip_udi(const char *udi) {
+    const char *slash;
+
+    if ((slash = strrchr(udi, '/')))
+        return slash+1;
+
+    return udi;
+}
+
+#ifdef HAVE_ALSA
+
+typedef enum {
+    ALSA_TYPE_SINK,
+    ALSA_TYPE_SOURCE,
+    ALSA_TYPE_OTHER,
+    ALSA_TYPE_MAX
+} alsa_type_t;
+
+static alsa_type_t hal_alsa_device_get_type(LibHalContext *context, const char *udi, DBusError *error) {
+    char *type;
+    alsa_type_t t;
+
+    if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", error)))
+        return ALSA_TYPE_OTHER;
+
+    if (!strcmp(type, "playback"))
+        t = ALSA_TYPE_SINK;
+    else if (!strcmp(type, "capture"))
+        t = ALSA_TYPE_SOURCE;
+    else
+        t = ALSA_TYPE_OTHER;
+
+    libhal_free_string(type);
+
+    return t;
+}
+
+static int hal_alsa_device_is_modem(LibHalContext *context, const char *udi, DBusError *error) {
+    char *class;
+    int r;
+
+    if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", error)))
+        return 0;
+
+    r = strcmp(class, "modem") == 0;
+    pa_xfree(class);
+
+    return r;
+}
+
+static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
+    char *args;
+    alsa_type_t type;
+    int device, card;
+    const char *module_name;
+    DBusError error;
+    pa_module *m;
+
+    dbus_error_init(&error);
+
+    pa_assert(u);
+    pa_assert(sink_name);
+    pa_assert(source_name);
+
+    *sink_name = *source_name = NULL;
+
+    type = hal_alsa_device_get_type(u->context, udi, &error);
+    if (dbus_error_is_set(&error) || type == ALSA_TYPE_OTHER)
+        goto fail;
+
+    device = libhal_device_get_property_int(u->context, udi, "alsa.device", &error);
+    if (dbus_error_is_set(&error) || device != 0)
+        goto fail;
+
+    card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
+    if (dbus_error_is_set(&error))
+        goto fail;
+
+    if (hal_alsa_device_is_modem(u->context, udi, &error))
+        goto fail;
+
+    if (type == ALSA_TYPE_SINK) {
+        *sink_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
+
+        module_name = "module-alsa-sink";
+        args = pa_sprintf_malloc("device_id=%u sink_name=%s", card, *sink_name);
+    } else {
+        *source_name = pa_sprintf_malloc("alsa_input.%s", strip_udi(udi));
+
+        module_name = "module-alsa-source";
+        args = pa_sprintf_malloc("device_id=%u source_name=%s", card, *source_name);
+    }
+
+    pa_log_debug("Loading %s with arguments '%s'", module_name, args);
+
+    m = pa_module_load(u->core, module_name, args);
+
+    pa_xfree(args);
+
+    if (!m) {
+        pa_xfree(*sink_name);
+        pa_xfree(*source_name);
+        *sink_name = *source_name = NULL;
+    }
+
+    return m;
+
+fail:
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("D-Bus error while parsing ALSA data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
+    }
+
+    return NULL;
+}
+
+#endif
+
+#ifdef HAVE_OSS
+
+static int hal_oss_device_is_pcm(LibHalContext *context, const char *udi, DBusError *error) {
+    char *class = NULL, *dev = NULL, *e;
+    int device;
+    int r = 0;
+
+    class = libhal_device_get_property_string(context, udi, "oss.type", error);
+    if (dbus_error_is_set(error) || !class)
+        goto finish;
+
+    if (strcmp(class, "pcm"))
+        goto finish;
+
+    dev = libhal_device_get_property_string(context, udi, "oss.device_file", error);
+    if (dbus_error_is_set(error) || !dev)
+        goto finish;
+
+    if ((e = strrchr(dev, '/')))
+        if (pa_startswith(e + 1, "audio"))
+            goto finish;
+
+    device = libhal_device_get_property_int(context, udi, "oss.device", error);
+    if (dbus_error_is_set(error) || device != 0)
+        goto finish;
+
+    r = 1;
+
+finish:
+
+    libhal_free_string(class);
+    libhal_free_string(dev);
+
+    return r;
+}
+
+static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
+    char* args;
+    char* device;
+    DBusError error;
+    pa_module *m;
+
+    dbus_error_init(&error);
+
+    pa_assert(u);
+    pa_assert(sink_name);
+    pa_assert(source_name);
+
+    *sink_name = *source_name = NULL;
+
+    if (!hal_oss_device_is_pcm(u->context, udi, &error) || dbus_error_is_set(&error))
+        goto fail;
+
+    device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error);
+    if (!device || dbus_error_is_set(&error))
+        goto fail;
+
+    *sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
+    *source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
+
+    args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, *sink_name, *source_name);
+    libhal_free_string(device);
+
+    pa_log_debug("Loading module-oss with arguments '%s'", args);
+    m = pa_module_load(u->core, "module-oss", args);
+    pa_xfree(args);
+
+    if (!m) {
+        pa_xfree(*sink_name);
+        pa_xfree(*source_name);
+        *sink_name = *source_name = NULL;
+    }
+
+    return m;
+
+fail:
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("D-Bus error while parsing OSS data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
+    }
+
+    return NULL;
+}
+#endif
+
+static struct device* hal_device_add(struct userdata *u, const char *udi) {
+    pa_module* m = NULL;
+    struct device *d;
+    char *sink_name = NULL, *source_name = NULL;
+
+    pa_assert(u);
+    pa_assert(u->capability);
+    pa_assert(!pa_hashmap_get(u->devices, udi));
+
+#ifdef HAVE_ALSA
+    if (strcmp(u->capability, CAPABILITY_ALSA) == 0)
+        m = hal_device_load_alsa(u, udi, &sink_name, &source_name);
+#endif
+#ifdef HAVE_OSS
+    if (strcmp(u->capability, CAPABILITY_OSS) == 0)
+        m = hal_device_load_oss(u, udi, &sink_name, &source_name);
+#endif
+
+    if (!m)
+        return NULL;
+
+    d = pa_xnew(struct device, 1);
+    d->acl_race_fix = 0;
+    d->udi = pa_xstrdup(udi);
+    d->index = m->index;
+    d->sink_name = sink_name;
+    d->source_name = source_name;
+    pa_hashmap_put(u->devices, d->udi, d);
+
+    return d;
+}
+
+static int hal_device_add_all(struct userdata *u, const char *capability) {
+    DBusError error;
+    int i, n, count = 0;
+    char** udis;
+
+    pa_assert(u);
+
+    dbus_error_init(&error);
+
+    if (u->capability && strcmp(u->capability, capability) != 0)
+        return 0;
+
+    pa_log_info("Trying capability %s", capability);
+
+    udis = libhal_find_device_by_capability(u->context, capability, &n, &error);
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("Error finding devices: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
+        return -1;
+    }
+
+    if (n > 0) {
+        u->capability = capability;
+
+        for (i = 0; i < n; i++) {
+            struct device *d;
+
+            if (!(d = hal_device_add(u, udis[i])))
+                pa_log_debug("Not loaded device %s", udis[i]);
+            else {
+                if (d->sink_name)
+                    pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
+                count++;
+            }
+        }
+    }
+
+    libhal_free_string_array(udis);
+    return count;
+}
+
+static dbus_bool_t device_has_capability(LibHalContext *context, const char *udi, const char* cap, DBusError *error){
+    dbus_bool_t has_prop;
+
+    has_prop = libhal_device_property_exists(context, udi, "info.capabilities", error);
+    if (!has_prop || dbus_error_is_set(error))
+        return FALSE;
+
+    return libhal_device_query_capability(context, udi, cap, error);
+}
+
+static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const struct timeval *tv, void *userdata) {
+    DBusError error;
+    struct timerdata *td = userdata;
+
+    dbus_error_init(&error);
+
+    if (!pa_hashmap_get(td->u->devices, td->udi)) {
+        int b;
+        struct device *d;
+
+        b = libhal_device_exists(td->u->context, td->udi, &error);
+
+        if (dbus_error_is_set(&error)) {
+            pa_log_error("Error adding device: %s: %s", error.name, error.message);
+            dbus_error_free(&error);
+        } else if (b) {
+            if (!(d = hal_device_add(td->u, td->udi)))
+                pa_log_debug("Not loaded device %s", td->udi);
+            else {
+                if (d->sink_name)
+                    pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
+            }
+        }
+    }
+
+    pa_xfree(td->udi);
+    pa_xfree(td);
+    ea->time_free(ev);
+}
+
+static void device_added_cb(LibHalContext *context, const char *udi) {
+    DBusError error;
+    struct timeval tv;
+    struct timerdata *t;
+    struct userdata *u;
+    int good = 0;
+
+    pa_assert_se(u = libhal_ctx_get_user_data(context));
+
+    if (pa_hashmap_get(u->devices, udi))
+        return;
+
+    pa_log_debug("HAL Device added: %s", udi);
+
+    dbus_error_init(&error);
+
+    if (u->capability) {
+
+        good = device_has_capability(context, udi, u->capability, &error);
+
+        if (dbus_error_is_set(&error)) {
+            pa_log_error("Error getting capability: %s: %s", error.name, error.message);
+            dbus_error_free(&error);
+            return;
+        }
+
+    } else {
+
+#ifdef HAVE_ALSA
+        good = device_has_capability(context, udi, CAPABILITY_ALSA, &error);
+
+        if (dbus_error_is_set(&error)) {
+            pa_log_error("Error getting capability: %s: %s", error.name, error.message);
+            dbus_error_free(&error);
+            return;
+        }
+
+        if (good)
+            u->capability = CAPABILITY_ALSA;
+#endif
+#if defined(HAVE_OSS) && defined(HAVE_ALSA)
+        if (!good) {
+#endif
+#ifdef HAS_OSS
+            good = device_has_capability(context, udi, CAPABILITY_OSS, &error);
+
+            if (dbus_error_is_set(&error)) {
+                pa_log_error("Error getting capability: %s: %s", error.name, error.message);
+                dbus_error_free(&error);
+                return;
+            }
+
+            if (good)
+                u->capability = CAPABILITY_OSS;
+
+#endif
+#if defined(HAVE_OSS) && defined(HAVE_ALSA)
+        }
+#endif
+    }
+
+    if (!good)
+        return;
+
+    /* actually add the device 1/2 second later */
+    t = pa_xnew(struct timerdata, 1);
+    t->u = u;
+    t->udi = pa_xstrdup(udi);
+
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, 500000);
+    u->core->mainloop->time_new(u->core->mainloop, &tv, device_added_time_cb, t);
+}
+
+static void device_removed_cb(LibHalContext* context, const char *udi) {
+    struct device *d;
+    struct userdata *u;
+
+    pa_assert_se(u = libhal_ctx_get_user_data(context));
+
+    pa_log_debug("Device removed: %s", udi);
+
+    if ((d = pa_hashmap_remove(u->devices, udi))) {
+        pa_module_unload_by_index(u->core, d->index);
+        hal_device_free(d);
+    }
+}
+
+static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
+    struct userdata *u;
+
+    pa_assert_se(u = libhal_ctx_get_user_data(context));
+
+    if (!u->capability || strcmp(u->capability, capability) == 0)
+        /* capability we care about, pretend it's a new device */
+        device_added_cb(context, udi);
+}
+
+static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
+    struct userdata *u;
+
+    pa_assert_se(u = libhal_ctx_get_user_data(context));
+
+    if (u->capability && strcmp(u->capability, capability) == 0)
+        /* capability we care about, pretend it was removed */
+        device_removed_cb(context, udi);
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
+    struct userdata*u = userdata;
+    DBusError error;
+
+    pa_assert(bus);
+    pa_assert(message);
+    pa_assert(u);
+
+    dbus_error_init(&error);
+
+    pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
+                 dbus_message_get_interface(message),
+                 dbus_message_get_path(message),
+                 dbus_message_get_member(message));
+
+    if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
+        dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
+        uint32_t uid;
+        int suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
+
+        if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
+            pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
+            goto finish;
+        }
+
+        if (uid == getuid() || uid == geteuid()) {
+            struct device *d;
+            const char *udi;
+
+            udi = dbus_message_get_path(message);
+
+            if ((d = pa_hashmap_get(u->devices, udi))) {
+                int send_acl_race_fix_message = 0;
+
+                d->acl_race_fix = 0;
+
+                if (d->sink_name) {
+                    pa_sink *sink;
+
+                    if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
+                        int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
+
+                        if (prev_suspended && !suspend) {
+                            /* resume */
+                            if (pa_sink_suspend(sink, 0) >= 0)
+                                pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
+                            else
+                                d->acl_race_fix = 1;
+
+                        } else if (!prev_suspended && suspend) {
+                            /* suspend */
+                            if (pa_sink_suspend(sink, 1) >= 0)
+                                send_acl_race_fix_message = 1;
+                        }
+                    }
+                }
+
+                if (d->source_name) {
+                    pa_source *source;
+
+                    if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
+                        int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+
+                        if (prev_suspended && !suspend) {
+                            /* resume */
+                            if (pa_source_suspend(source, 0) < 0)
+                                d->acl_race_fix = 1;
+
+                        } else if (!prev_suspended && suspend) {
+                            /* suspend */
+                            if (pa_source_suspend(source, 0) >= 0)
+                                send_acl_race_fix_message = 1;
+                        }
+                    }
+                }
+
+                if (send_acl_race_fix_message) {
+                    DBusMessage *msg;
+                    msg = dbus_message_new_signal(udi, "org.pulseaudio.Server", "DirtyGiveUpMessage");
+                    dbus_connection_send(pa_dbus_connection_get(u->connection), msg, NULL);
+                    dbus_message_unref(msg);
+                }
+
+            } else if (!suspend)
+                device_added_cb(u->context, udi);
+        }
+
+    } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) {
+        /* We use this message to avoid a dirty race condition when we
+           get an ACLAdded message before the previously owning PA
+           sever has closed the device. We can remove this as
+           soon as HAL learns frevoke() */
+
+        const char *udi;
+        struct device *d;
+
+        udi = dbus_message_get_path(message);
+
+        if ((d = pa_hashmap_get(u->devices, udi)) && d->acl_race_fix) {
+            pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
+
+            d->acl_race_fix = 0;
+
+            if (d->sink_name) {
+                pa_sink *sink;
+
+                if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
+
+                    int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
+
+                    if (prev_suspended) {
+                        /* resume */
+                        if (pa_sink_suspend(sink, 0) >= 0)
+                            pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
+                    }
+                }
+            }
+
+            if (d->source_name) {
+                pa_source *source;
+
+                if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
+
+                    int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+
+                    if (prev_suspended)
+                        pa_source_suspend(source, 0);
+                }
+            }
+
+        } else
+            /* Yes, we don't check the UDI for validity, but hopefully HAL will */
+            device_added_cb(u->context, udi);
+    }
+
+finish:
+    dbus_error_free(&error);
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void hal_context_free(LibHalContext* hal_context) {
+    DBusError error;
+
+    dbus_error_init(&error);
+
+    libhal_ctx_shutdown(hal_context, &error);
+    libhal_ctx_free(hal_context);
+
+    dbus_error_free(&error);
+}
+
+static LibHalContext* hal_context_new(pa_core* c, DBusConnection *conn) {
+    DBusError error;
+    LibHalContext *hal_context = NULL;
+
+    dbus_error_init(&error);
+
+    if (!(hal_context = libhal_ctx_new())) {
+        pa_log_error("libhal_ctx_new() failed");
+        goto fail;
+    }
+
+    if (!libhal_ctx_set_dbus_connection(hal_context, conn)) {
+        pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    if (!libhal_ctx_init(hal_context, &error)) {
+        pa_log_error("Couldn't connect to hald: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    return hal_context;
+
+fail:
+    if (hal_context)
+        hal_context_free(hal_context);
+
+    dbus_error_free(&error);
+
+    return NULL;
+}
+
+int pa__init(pa_module*m) {
+    DBusError error;
+    pa_dbus_connection *conn;
+    struct userdata *u = NULL;
+    LibHalContext *hal_context = NULL;
+    int n = 0;
+    pa_modargs *ma;
+    const char *api;
+
+    pa_assert(m);
+
+    dbus_error_init(&error);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if ((api = pa_modargs_get_value(ma, "api", NULL))) {
+        int good = 0;
+
+#ifdef HAVE_ALSA
+        if (strcmp(api, CAPABILITY_ALSA) == 0) {
+            good = 1;
+            api = CAPABILITY_ALSA;
+        }
+#endif
+#ifdef HAVE_OSS
+        if (strcmp(api, CAPABILITY_OSS) == 0) {
+            good = 1;
+            api = CAPABILITY_OSS;
+        }
+#endif
+
+        if (!good) {
+            pa_log_error("Invalid API specification.");
+            goto fail;
+        }
+    }
+
+    if (!(conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
+        if (conn)
+            pa_dbus_connection_unref(conn);
+        pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    if (!(hal_context = hal_context_new(m->core, pa_dbus_connection_get(conn)))) {
+        /* pa_hal_context_new() logs appropriate errors */
+        pa_dbus_connection_unref(conn);
+        goto fail;
+    }
+
+    u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->context = hal_context;
+    u->connection = conn;
+    u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    u->capability = api;
+    m->userdata = u;
+
+#ifdef HAVE_ALSA
+    n = hal_device_add_all(u, CAPABILITY_ALSA);
+#endif
+#if defined(HAVE_ALSA) && defined(HAVE_OSS)
+    if (n <= 0)
+#endif
+#ifdef HAVE_OSS
+        n += hal_device_add_all(u, CAPABILITY_OSS);
+#endif
+
+    libhal_ctx_set_user_data(hal_context, u);
+    libhal_ctx_set_device_added(hal_context, device_added_cb);
+    libhal_ctx_set_device_removed(hal_context, device_removed_cb);
+    libhal_ctx_set_device_new_capability(hal_context, new_capability_cb);
+    libhal_ctx_set_device_lost_capability(hal_context, lost_capability_cb);
+
+    if (!libhal_device_property_watch_all(hal_context, &error)) {
+        pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    if (!dbus_connection_add_filter(pa_dbus_connection_get(conn), filter_cb, u, NULL)) {
+        pa_log_error("Failed to add filter function");
+        goto fail;
+    }
+
+    dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',interface='org.pulseaudio.Server'", &error);
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("Unable to subscribe to PulseAudio signals: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    pa_log_info("Loaded %i modules.", n);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    dbus_error_free(&error);
+    pa__done(m);
+
+    return -1;
+}
+
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->context)
+        hal_context_free(u->context);
+
+    if (u->devices)
+        pa_hashmap_free(u->devices, hal_device_free_cb, NULL);
+
+    if (u->connection)
+        pa_dbus_connection_unref(u->connection);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c
new file mode 100644 (file)
index 0000000..c4d47f8
--- /dev/null
@@ -0,0 +1,463 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <jack/jack.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+
+#include "module-jack-sink-symdef.h"
+
+/* General overview:
+ *
+ * Because JACK has a very unflexible event loop management which
+ * doesn't allow us to add our own event sources to the event thread
+ * we cannot use the JACK real-time thread for dispatching our PA
+ * work. Instead, we run an additional RT thread which does most of
+ * the PA handling, and have the JACK RT thread request data from it
+ * via pa_asyncmsgq. The cost is an additional context switch which
+ * should hopefully not be that expensive if RT scheduling is
+ * enabled. A better fix would only be possible with additional event
+ * source support in JACK.
+ */
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("JACK Sink");
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_USAGE(
+        "sink_name=<name of sink> "
+        "server_name=<jack server name> "
+        "client_name=<jack client name> "
+        "channels=<number of channels> "
+        "connect=<connect ports?> "
+        "channel_map=<channel map>");
+
+#define DEFAULT_SINK_NAME "jack_out"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    unsigned channels;
+
+    jack_port_t* port[PA_CHANNELS_MAX];
+    jack_client_t *client;
+
+    void *buffer[PA_CHANNELS_MAX];
+
+    pa_thread_mq thread_mq;
+    pa_asyncmsgq *jack_msgq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+
+    pa_thread *thread;
+
+    jack_nframes_t frames_in_buffer;
+    jack_nframes_t saved_frame_time;
+    pa_bool_t saved_frame_time_valid;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "server_name",
+    "client_name",
+    "channels",
+    "connect",
+    "channel_map",
+    NULL
+};
+
+enum {
+    SINK_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX,
+    SINK_MESSAGE_ON_SHUTDOWN
+};
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *memchunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case SINK_MESSAGE_RENDER:
+
+            /* Handle the request from the JACK thread */
+
+            if (u->sink->thread_info.state == PA_SINK_RUNNING) {
+                pa_memchunk chunk;
+                size_t nbytes;
+                void *p;
+
+                pa_assert(offset > 0);
+                nbytes = offset * pa_frame_size(&u->sink->sample_spec);
+
+                pa_sink_render_full(u->sink, nbytes, &chunk);
+
+                p = (uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index;
+                pa_deinterleave(p, u->buffer, u->channels, sizeof(float), offset);
+                pa_memblock_release(chunk.memblock);
+
+                pa_memblock_unref(chunk.memblock);
+            } else {
+                unsigned c;
+                pa_sample_spec ss;
+
+                /* Humm, we're not RUNNING, hence let's write some silence */
+
+                ss = u->sink->sample_spec;
+                ss.channels = 1;
+
+                for (c = 0; c < u->channels; c++)
+                    pa_silence_memory(u->buffer[c], offset * pa_sample_size(&ss), &ss);
+            }
+
+            u->frames_in_buffer = offset;
+            u->saved_frame_time = * (jack_nframes_t*) data;
+            u->saved_frame_time_valid = TRUE;
+
+            return 0;
+
+        case SINK_MESSAGE_ON_SHUTDOWN:
+            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            jack_nframes_t l, ft, d;
+            size_t n;
+
+            /* This is the "worst-case" latency */
+            l = jack_port_get_total_latency(u->client, u->port[0]) + u->frames_in_buffer;
+
+            if (u->saved_frame_time_valid) {
+                /* Adjust the worst case latency by the time that
+                 * passed since we last handed data to JACK */
+
+                ft = jack_frame_time(u->client);
+                d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0;
+                l = l > d ? l - d : 0;
+            }
+
+            /* Convert it to usec */
+            n = l * pa_frame_size(&u->sink->sample_spec);
+            *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
+
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, memchunk);
+}
+
+static int jack_process(jack_nframes_t nframes, void *arg) {
+    struct userdata *u = arg;
+    unsigned c;
+    jack_nframes_t frame_time;
+    pa_assert(u);
+
+    /* We just forward the request to our other RT thread */
+
+    for (c = 0; c < u->channels; c++)
+        pa_assert_se(u->buffer[c] = jack_port_get_buffer(u->port[c], nframes));
+
+    frame_time = jack_frame_time(u->client);
+
+    pa_assert_se(pa_asyncmsgq_send(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RENDER, &frame_time, nframes, NULL) == 0);
+    return 0;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    for (;;) {
+        int ret;
+
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+static void jack_error_func(const char*t) {
+    char *s;
+
+    s = pa_xstrndup(t, strcspn(t, "\n\r"));
+    pa_log_warn("JACK error >%s<", s);
+    pa_xfree(s);
+}
+
+static void jack_init(void *arg) {
+    struct userdata *u = arg;
+
+    pa_log_info("JACK thread starting up.");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority+4);
+}
+
+static void jack_shutdown(void* arg) {
+    struct userdata *u = arg;
+
+    pa_log_info("JACK thread shutting down..");
+    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL);
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    jack_status_t status;
+    const char *server_name, *client_name;
+    uint32_t channels = 0;
+    pa_bool_t do_connect = TRUE;
+    unsigned i;
+    const char **ports = NULL, **p;
+    pa_sink_new_data data;
+
+    pa_assert(m);
+
+    jack_set_error_function(jack_error_func);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
+        pa_log("Failed to parse connect= argument.");
+        goto fail;
+    }
+
+    server_name = pa_modargs_get_value(ma, "server_name", NULL);
+    client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink");
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->saved_frame_time_valid = FALSE;
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+    /* The queue linking the JACK thread and our RT thread */
+    u->jack_msgq = pa_asyncmsgq_new(0);
+
+    /* The msgq from the JACK RT thread should have an even higher
+     * priority than the normal message queues, to match the guarantee
+     * all other drivers make: supplying the audio device with data is
+     * the top priority -- and as long as that is possible we don't do
+     * anything else */
+    u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
+
+    if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
+        pa_log("jack_client_open() failed.");
+        goto fail;
+    }
+
+    ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
+
+    channels = 0;
+    for (p = ports; *p; p++)
+        channels++;
+
+    if (!channels)
+        channels = m->core->default_sample_spec.channels;
+
+    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || channels <= 0 || channels >= PA_CHANNELS_MAX) {
+        pa_log("Failed to parse channels= argument.");
+        goto fail;
+    }
+
+    pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA);
+    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {
+        pa_log("Failed to parse channel_map= argument.");
+        goto fail;
+    }
+
+    pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client));
+
+    ss.channels = u->channels = channels;
+    ss.rate = jack_get_sample_rate(u->client);
+    ss.format = PA_SAMPLE_FLOAT32NE;
+
+    pa_assert(pa_sample_spec_valid(&ss));
+
+    for (i = 0; i < ss.channels; i++) {
+        if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) {
+            pa_log("jack_port_register() failed.");
+            goto fail;
+        }
+    }
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
+    if (server_name)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client));
+    pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    jack_set_process_callback(u->client, jack_process, u);
+    jack_on_shutdown(u->client, jack_shutdown, u);
+    jack_set_thread_init_callback(u->client, jack_init, u);
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    if (jack_activate(u->client)) {
+        pa_log("jack_activate() failed");
+        goto fail;
+    }
+
+    if (do_connect) {
+        for (i = 0, p = ports; i < ss.channels; i++, p++) {
+
+            if (!*p) {
+                pa_log("Not enough physical output ports, leaving unconnected.");
+                break;
+            }
+
+            pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p);
+
+            if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) {
+                pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
+                break;
+            }
+        }
+    }
+
+    pa_sink_put(u->sink);
+
+    free(ports);
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    free(ports);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->client)
+        jack_client_close(u->client);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->jack_msgq)
+        pa_asyncmsgq_unref(u->jack_msgq);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c
new file mode 100644 (file)
index 0000000..03f9d15
--- /dev/null
@@ -0,0 +1,434 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <jack/jack.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+
+#include "module-jack-source-symdef.h"
+
+/* See module-jack-sink for a few comments how this module basically
+ * works */
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("JACK Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+        "source_name=<name of source> "
+        "server_name=<jack server name> "
+        "client_name=<jack client name> "
+        "channels=<number of channels> "
+        "connect=<connect ports?>"
+        "channel_map=<channel map>");
+
+#define DEFAULT_SOURCE_NAME "jack_in"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_source *source;
+
+    unsigned channels;
+
+    jack_port_t* port[PA_CHANNELS_MAX];
+    jack_client_t *client;
+
+    pa_thread_mq thread_mq;
+    pa_asyncmsgq *jack_msgq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+
+    pa_thread *thread;
+
+    jack_nframes_t saved_frame_time;
+    pa_bool_t saved_frame_time_valid;
+};
+
+static const char* const valid_modargs[] = {
+    "source_name",
+    "server_name",
+    "client_name",
+    "channels",
+    "connect",
+    "channel_map",
+    NULL
+};
+
+enum {
+    SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
+    SOURCE_MESSAGE_ON_SHUTDOWN
+};
+
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case SOURCE_MESSAGE_POST:
+
+            /* Handle the new block from the JACK thread */
+            pa_assert(chunk);
+            pa_assert(chunk->length > 0);
+
+            if (u->source->thread_info.state == PA_SOURCE_RUNNING)
+                pa_source_post(u->source, chunk);
+
+            u->saved_frame_time = offset;
+            u->saved_frame_time_valid = TRUE;
+
+            return 0;
+
+        case SOURCE_MESSAGE_ON_SHUTDOWN:
+            pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            jack_nframes_t l, ft, d;
+            size_t n;
+
+            /* This is the "worst-case" latency */
+            l = jack_port_get_total_latency(u->client, u->port[0]);
+
+            if (u->saved_frame_time_valid) {
+                /* Adjust the worst case latency by the time that
+                 * passed since we last handed data to JACK */
+
+                ft = jack_frame_time(u->client);
+                d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0;
+                l += d;
+            }
+
+            /* Convert it to usec */
+            n = l * pa_frame_size(&u->source->sample_spec);
+            *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec);
+
+            return 0;
+        }
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static int jack_process(jack_nframes_t nframes, void *arg) {
+    unsigned c;
+    struct userdata *u = arg;
+    const void *buffer[PA_CHANNELS_MAX];
+    void *p;
+    jack_nframes_t frame_time;
+    pa_memchunk chunk;
+
+    pa_assert(u);
+
+    for (c = 0; c < u->channels; c++)
+        pa_assert(buffer[c] = jack_port_get_buffer(u->port[c], nframes));
+
+    /* We interleave the data and pass it on to the other RT thread */
+
+    pa_memchunk_reset(&chunk);
+    chunk.length = nframes * pa_frame_size(&u->source->sample_spec);
+    chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length);
+    p = pa_memblock_acquire(chunk.memblock);
+    pa_interleave(buffer, u->channels, p, sizeof(float), nframes);
+    pa_memblock_release(chunk.memblock);
+
+    frame_time = jack_frame_time(u->client);
+
+    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, NULL, frame_time, &chunk, NULL);
+
+    pa_memblock_unref(chunk.memblock);
+
+    return 0;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    for (;;) {
+        int ret;
+
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+static void jack_error_func(const char*t) {
+    char *s;
+
+    s = pa_xstrndup(t, strcspn(t, "\n\r"));
+    pa_log_warn("JACK error >%s<", s);
+    pa_xfree(s);
+}
+
+static void jack_init(void *arg) {
+    struct userdata *u = arg;
+
+    pa_log_info("JACK thread starting up.");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority+4);
+}
+
+static void jack_shutdown(void* arg) {
+    struct userdata *u = arg;
+
+    pa_log_info("JACK thread shutting down..");
+    pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL);
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    jack_status_t status;
+    const char *server_name, *client_name;
+    uint32_t channels = 0;
+    pa_bool_t do_connect = TRUE;
+    unsigned i;
+    const char **ports = NULL, **p;
+    pa_source_new_data data;
+
+    pa_assert(m);
+
+    jack_set_error_function(jack_error_func);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
+        pa_log("Failed to parse connect= argument.");
+        goto fail;
+    }
+
+    server_name = pa_modargs_get_value(ma, "server_name", NULL);
+    client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Source");
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->saved_frame_time_valid = FALSE;
+
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+    u->jack_msgq = pa_asyncmsgq_new(0);
+    u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);
+
+    if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
+        pa_log("jack_client_open() failed.");
+        goto fail;
+    }
+
+    ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput);
+
+    channels = 0;
+    for (p = ports; *p; p++)
+        channels++;
+
+    if (!channels)
+        channels = m->core->default_sample_spec.channels;
+
+    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || channels <= 0 || channels >= PA_CHANNELS_MAX) {
+        pa_log("failed to parse channels= argument.");
+        goto fail;
+    }
+
+    pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA);
+    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {
+        pa_log("failed to parse channel_map= argument.");
+        goto fail;
+    }
+
+    pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client));
+
+    ss.channels = u->channels = channels;
+    ss.rate = jack_get_sample_rate(u->client);
+    ss.format = PA_SAMPLE_FLOAT32NE;
+
+    pa_assert(pa_sample_spec_valid(&ss));
+
+    for (i = 0; i < ss.channels; i++) {
+        if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput|JackPortIsTerminal, 0))) {
+            pa_log("jack_port_register() failed.");
+            goto fail;
+        }
+    }
+
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+    pa_source_new_data_set_sample_spec(&data, &ss);
+    pa_source_new_data_set_channel_map(&data, &map);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
+    if (server_name)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack source (%s)", jack_get_client_name(u->client));
+    pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));
+
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
+    pa_source_new_data_done(&data);
+
+    if (!u->source) {
+        pa_log("Failed to create source.");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg;
+    u->source->userdata = u;
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+
+    jack_set_process_callback(u->client, jack_process, u);
+    jack_on_shutdown(u->client, jack_shutdown, u);
+    jack_set_thread_init_callback(u->client, jack_init, u);
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    if (jack_activate(u->client)) {
+        pa_log("jack_activate() failed");
+        goto fail;
+    }
+
+    if (do_connect) {
+        for (i = 0, p = ports; i < ss.channels; i++, p++) {
+
+            if (!*p) {
+                pa_log("not enough physical output ports, leaving unconnected.");
+                break;
+            }
+
+            pa_log_info("connecting %s to %s", jack_port_name(u->port[i]), *p);
+
+            if (jack_connect(u->client, *p, jack_port_name(u->port[i]))) {
+                pa_log("failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
+                break;
+            }
+        }
+
+    }
+
+    pa_source_put(u->source);
+
+    free(ports);
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    free(ports);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->client)
+        jack_client_close(u->client);
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->jack_msgq)
+        pa_asyncmsgq_unref(u->jack_msgq);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
new file mode 100644 (file)
index 0000000..3e0babf
--- /dev/null
@@ -0,0 +1,799 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* TODO: Some plugins cause latency, and some even report it by using a control
+   out port. We don't currently use the latency information. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/ltdl-helper.h>
+
+#include "module-ladspa-sink-symdef.h"
+#include "ladspa.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Virtual LADSPA sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "master=<name of sink to remap> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "channel_map=<channel map> "
+        "plugin=<ladspa plugin name> "
+        "label=<ladspa plugin label> "
+        "control=<comma seperated list of input control values>");
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_sink *sink, *master;
+    pa_sink_input *sink_input;
+
+    const LADSPA_Descriptor *descriptor;
+    unsigned channels;
+    LADSPA_Handle handle[PA_CHANNELS_MAX];
+    LADSPA_Data *input, *output;
+    size_t block_size;
+    unsigned long input_port, output_port;
+    LADSPA_Data *control;
+
+    /* This is a dummy buffer. Every port must be connected, but we don't care
+       about control out ports. We connect them all to this single buffer. */
+    LADSPA_Data control_out;
+
+    pa_memblockq *memblockq;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "master",
+    "format",
+    "channels",
+    "rate",
+    "channel_map",
+    "plugin",
+    "label",
+    "control",
+    NULL
+};
+
+/* Called from I/O thread context */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t usec = 0;
+
+            /* Get the latency of the master sink */
+            if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+                usec = 0;
+
+            /* Add the latency internal to our sink input on top */
+            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+
+            *((pa_usec_t*) data) = usec;
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (PA_SINK_IS_LINKED(state) &&
+        u->sink_input &&
+        PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+
+        pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_request_rewind(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_set_requested_latency_within_thread(
+            u->sink_input,
+            pa_sink_get_requested_latency_within_thread(s));
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+    float *src, *dst;
+    size_t fs;
+    unsigned n, c;
+    pa_memchunk tchunk;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+        return -1;
+
+    while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
+        pa_memchunk nchunk;
+
+        pa_sink_render(u->sink, nbytes, &nchunk);
+        pa_memblockq_push(u->memblockq, &nchunk);
+        pa_memblock_unref(nchunk.memblock);
+    }
+
+    tchunk.length = PA_MIN(nbytes, tchunk.length);
+    pa_assert(tchunk.length > 0);
+
+    fs = pa_frame_size(&i->sample_spec);
+    n = PA_MIN(tchunk.length, u->block_size) / fs;
+
+    pa_assert(n > 0);
+
+    chunk->index = 0;
+    chunk->length = n*fs;
+    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
+
+    pa_memblockq_drop(u->memblockq, chunk->length);
+
+    src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
+    dst = (float*) pa_memblock_acquire(chunk->memblock);
+
+    for (c = 0; c < u->channels; c++) {
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, u->channels*sizeof(float), n);
+        u->descriptor->run(u->handle[c], n);
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels*sizeof(float), u->output, sizeof(float), n);
+    }
+
+    pa_memblock_release(tchunk.memblock);
+    pa_memblock_release(chunk->memblock);
+
+    pa_memblock_unref(tchunk.memblock);
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+    pa_assert(nbytes > 0);
+
+    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+        return;
+
+    if (u->sink->thread_info.rewind_nbytes > 0) {
+        size_t max_rewrite, amount;
+
+        max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq);
+        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
+        u->sink->thread_info.rewind_nbytes = 0;
+
+        if (amount > 0) {
+            unsigned c;
+
+            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE);
+            pa_sink_process_rewind(u->sink, amount);
+
+            pa_log_debug("Resetting plugin");
+
+            /* Reset the plugin */
+            if (u->descriptor->deactivate)
+                for (c = 0; c < u->channels; c++)
+                    u->descriptor->deactivate(u->handle[c]);
+            if (u->descriptor->activate)
+                for (c = 0; c < u->channels; c++)
+                    u->descriptor->activate(u->handle[c]);
+        }
+    }
+
+    pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+    pa_sink_set_max_rewind(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_set_max_request(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_update_latency_range(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_detach_within_thread(u->sink);
+    pa_sink_set_asyncmsgq(u->sink, NULL);
+    pa_sink_set_rtpoll(u->sink, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
+    pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
+    pa_sink_attach_within_thread(u->sink);
+
+    pa_sink_update_latency_range(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_unlink(u->sink);
+    pa_sink_input_unlink(u->sink_input);
+
+    pa_sink_unref(u->sink);
+    u->sink = NULL;
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    pa_module_unload_request(u->module);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT) {
+        pa_log_debug("Requesting rewind due to state change.");
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+    }
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    char *t;
+    const char *z;
+    pa_sink *master;
+    pa_sink_input_new_data sink_input_data;
+    pa_sink_new_data sink_data;
+    const char *plugin, *label;
+    LADSPA_Descriptor_Function descriptor_func;
+    const char *e, *cdata;
+    const LADSPA_Descriptor *d;
+    unsigned long input_port, output_port, p, j, n_control;
+    unsigned c;
+    pa_bool_t *use_default = NULL;
+
+    pa_assert(m);
+
+    pa_assert(sizeof(LADSPA_Data) == sizeof(float));
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
+        pa_log("Master sink not found");
+        goto fail;
+    }
+
+    ss = master->sample_spec;
+    ss.format = PA_SAMPLE_FLOAT32;
+    map = master->channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    if (!(plugin = pa_modargs_get_value(ma, "plugin", NULL))) {
+        pa_log("Missing LADSPA plugin name");
+        goto fail;
+    }
+
+    if (!(label = pa_modargs_get_value(ma, "label", NULL))) {
+        pa_log("Missing LADSPA plugin label");
+        goto fail;
+    }
+
+    cdata = pa_modargs_get_value(ma, "control", NULL);
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->master = master;
+    u->sink = NULL;
+    u->sink_input = NULL;
+    u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
+
+    if (!(e = getenv("LADSPA_PATH")))
+        e = LADSPA_PATH;
+
+    /* FIXME: This is not exactly thread safe */
+    t = pa_xstrdup(lt_dlgetsearchpath());
+    lt_dlsetsearchpath(e);
+    m->dl = lt_dlopenext(plugin);
+    lt_dlsetsearchpath(t);
+    pa_xfree(t);
+
+    if (!m->dl) {
+        pa_log("Failed to load LADSPA plugin: %s", lt_dlerror());
+        goto fail;
+    }
+
+    if (!(descriptor_func = (LADSPA_Descriptor_Function) pa_load_sym(m->dl, NULL, "ladspa_descriptor"))) {
+        pa_log("LADSPA module lacks ladspa_descriptor() symbol.");
+        goto fail;
+    }
+
+    for (j = 0;; j++) {
+
+        if (!(d = descriptor_func(j))) {
+            pa_log("Failed to find plugin label '%s' in plugin '%s'.", label, plugin);
+            goto fail;
+        }
+
+        if (strcmp(d->Label, label) == 0)
+            break;
+    }
+
+    u->descriptor = d;
+
+    pa_log_debug("Module: %s", plugin);
+    pa_log_debug("Label: %s", d->Label);
+    pa_log_debug("Unique ID: %lu", d->UniqueID);
+    pa_log_debug("Name: %s", d->Name);
+    pa_log_debug("Maker: %s", d->Maker);
+    pa_log_debug("Copyright: %s", d->Copyright);
+
+    input_port = output_port = (unsigned long) -1;
+    n_control = 0;
+
+    for (p = 0; p < d->PortCount; p++) {
+
+        if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) {
+
+            if (strcmp(d->PortNames[p], "Input") == 0) {
+                pa_assert(input_port == (unsigned long) -1);
+                input_port = p;
+            } else {
+                pa_log("Found audio input port on plugin we cannot handle: %s", d->PortNames[p]);
+                goto fail;
+            }
+
+        } else if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) {
+
+            if (strcmp(d->PortNames[p], "Output") == 0) {
+                pa_assert(output_port == (unsigned long) -1);
+                output_port = p;
+            } else {
+                pa_log("Found audio output port on plugin we cannot handle: %s", d->PortNames[p]);
+                goto fail;
+            }
+
+        } else if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]))
+            n_control++;
+        else {
+            pa_assert(LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]));
+            pa_log_debug("Ignored control output port \"%s\".", d->PortNames[p]);
+        }
+    }
+
+    if ((input_port == (unsigned long) -1) || (output_port == (unsigned long) -1)) {
+        pa_log("Failed to identify input and output ports. "
+               "Right now this module can only deal with plugins which provide an 'Input' and an 'Output' audio port. "
+               "Patches welcome!");
+        goto fail;
+    }
+
+    u->block_size = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss);
+
+    u->input = (LADSPA_Data*) pa_xnew(uint8_t, u->block_size);
+    if (LADSPA_IS_INPLACE_BROKEN(d->Properties))
+        u->output = (LADSPA_Data*) pa_xnew(uint8_t, u->block_size);
+    else
+        u->output = u->input;
+
+    u->channels = ss.channels;
+
+    for (c = 0; c < ss.channels; c++) {
+        if (!(u->handle[c] = d->instantiate(d, ss.rate))) {
+            pa_log("Failed to instantiate plugin %s with label %s for channel %i", plugin, d->Label, c);
+            goto fail;
+        }
+
+        d->connect_port(u->handle[c], input_port, u->input);
+        d->connect_port(u->handle[c], output_port, u->output);
+    }
+
+    if (!cdata && n_control > 0) {
+        pa_log("This plugin requires specification of %lu control parameters.", n_control);
+        goto fail;
+    }
+
+    if (n_control > 0) {
+        const char *state = NULL;
+        char *k;
+        unsigned long h;
+
+        u->control = pa_xnew(LADSPA_Data, n_control);
+        use_default = pa_xnew(pa_bool_t, n_control);
+        p = 0;
+
+        while ((k = pa_split(cdata, ",", &state)) && p < n_control) {
+            double f;
+
+            if (*k == 0) {
+                use_default[p++] = TRUE;
+                pa_xfree(k);
+                continue;
+            }
+
+            if (pa_atod(k, &f) < 0) {
+                pa_log("Failed to parse control value '%s'", k);
+                pa_xfree(k);
+                goto fail;
+            }
+
+            pa_xfree(k);
+
+            use_default[p] = FALSE;
+            u->control[p++] = f;
+        }
+
+        /* The previous loop doesn't take the last control value into account
+           if it is left empty, so we do it here. */
+        if (*cdata == 0 || cdata[strlen(cdata) - 1] == ',') {
+            if (p < n_control)
+                use_default[p] = TRUE;
+            p++;
+        }
+
+        if (p > n_control || k) {
+            pa_log("Too many control values passed, %lu expected.", n_control);
+            pa_xfree(k);
+            goto fail;
+        }
+
+        if (p < n_control) {
+            pa_log("Not enough control values passed, %lu expected, %lu passed.", n_control, p);
+            goto fail;
+        }
+
+        h = 0;
+        for (p = 0; p < d->PortCount; p++) {
+            LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;
+
+            if (!LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]))
+                continue;
+
+            if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
+                for (c = 0; c < ss.channels; c++)
+                    d->connect_port(u->handle[c], p, &u->control_out);
+                continue;
+            }
+
+            pa_assert(h < n_control);
+
+            if (use_default[h]) {
+                LADSPA_Data lower, upper;
+
+                if (!LADSPA_IS_HINT_HAS_DEFAULT(hint)) {
+                    pa_log("Control port value left empty but plugin defines no default.");
+                    goto fail;
+                }
+
+                lower = d->PortRangeHints[p].LowerBound;
+                upper = d->PortRangeHints[p].UpperBound;
+
+                if (LADSPA_IS_HINT_SAMPLE_RATE(hint)) {
+                    lower *= ss.rate;
+                    upper *= ss.rate;
+                }
+
+                switch (hint & LADSPA_HINT_DEFAULT_MASK) {
+
+                    case LADSPA_HINT_DEFAULT_MINIMUM:
+                        u->control[h] = lower;
+                        break;
+
+                    case LADSPA_HINT_DEFAULT_MAXIMUM:
+                        u->control[h] = upper;
+                        break;
+
+                    case LADSPA_HINT_DEFAULT_LOW:
+                        if (LADSPA_IS_HINT_LOGARITHMIC(hint))
+                            u->control[h] = exp(log(lower) * 0.75 + log(upper) * 0.25);
+                        else
+                            u->control[h] = lower * 0.75 + upper * 0.25;
+                        break;
+
+                    case LADSPA_HINT_DEFAULT_MIDDLE:
+                        if (LADSPA_IS_HINT_LOGARITHMIC(hint))
+                            u->control[h] = exp(log(lower) * 0.5 + log(upper) * 0.5);
+                        else
+                            u->control[h] = lower * 0.5 + upper * 0.5;
+                        break;
+
+                    case LADSPA_HINT_DEFAULT_HIGH:
+                        if (LADSPA_IS_HINT_LOGARITHMIC(hint))
+                            u->control[h] = exp(log(lower) * 0.25 + log(upper) * 0.75);
+                        else
+                            u->control[h] = lower * 0.25 + upper * 0.75;
+                        break;
+
+                    case LADSPA_HINT_DEFAULT_0:
+                        u->control[h] = 0;
+                        break;
+
+                    case LADSPA_HINT_DEFAULT_1:
+                        u->control[h] = 1;
+                        break;
+
+                    case LADSPA_HINT_DEFAULT_100:
+                        u->control[h] = 100;
+                        break;
+
+                    case LADSPA_HINT_DEFAULT_440:
+                        u->control[h] = 440;
+                        break;
+
+                    default:
+                        pa_assert_not_reached();
+                }
+            }
+
+            if (LADSPA_IS_HINT_INTEGER(hint))
+                u->control[h] = roundf(u->control[h]);
+
+            pa_log_debug("Binding %f to port %s", u->control[h], d->PortNames[p]);
+
+            for (c = 0; c < ss.channels; c++)
+                d->connect_port(u->handle[c], p, &u->control[h]);
+
+            h++;
+        }
+
+        pa_assert(h == n_control);
+    }
+
+    if (d->activate)
+        for (c = 0; c < u->channels; c++)
+            d->activate(u->handle[c]);
+
+    /* Create sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+    if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        sink_data.name = pa_sprintf_malloc("%s.ladspa", master->name);
+    sink_data.namereg_fail = FALSE;
+    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &map);
+    z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+    pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", label, z ? z : master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+    pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin);
+    pa_proplist_sets(sink_data.proplist, "device.ladspa.label", d->Label);
+    pa_proplist_sets(sink_data.proplist, "device.ladspa.name", d->Name);
+    pa_proplist_sets(sink_data.proplist, "device.ladspa.maker", d->Maker);
+    pa_proplist_sets(sink_data.proplist, "device.ladspa.copyright", d->Copyright);
+    pa_proplist_setf(sink_data.proplist, "device.ladspa.unique_id", "%lu", (unsigned long) d->UniqueID);
+
+    u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY);
+    pa_sink_new_data_done(&sink_data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->set_state = sink_set_state;
+    u->sink->update_requested_latency = sink_update_requested_latency;
+    u->sink->request_rewind = sink_request_rewind;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+    pa_sink_set_rtpoll(u->sink, master->rtpoll);
+
+    /* Create sink input */
+    pa_sink_input_new_data_init(&sink_input_data);
+    sink_input_data.driver = __FILE__;
+    sink_input_data.module = m;
+    sink_input_data.sink = u->master;
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+
+    u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
+    pa_sink_input_new_data_done(&sink_input_data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->update_max_request = sink_input_update_max_request_cb;
+    u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->attach = sink_input_attach_cb;
+    u->sink_input->detach = sink_input_detach_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->userdata = u;
+
+    pa_sink_put(u->sink);
+    pa_sink_input_put(u->sink_input);
+
+    pa_modargs_free(ma);
+
+    pa_xfree(use_default);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa_xfree(use_default);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    unsigned c;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink) {
+        pa_sink_unlink(u->sink);
+        pa_sink_unref(u->sink);
+    }
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+    }
+
+    for (c = 0; c < u->channels; c++)
+        if (u->handle[c]) {
+            if (u->descriptor->deactivate)
+                u->descriptor->deactivate(u->handle[c]);
+            u->descriptor->cleanup(u->handle[c]);
+        }
+
+    if (u->output != u->input)
+        pa_xfree(u->output);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    pa_xfree(u->input);
+
+    pa_xfree(u->control);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
new file mode 100644 (file)
index 0000000..0570a6a
--- /dev/null
@@ -0,0 +1,257 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+
+#include <lirc/lirc_client.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/macro.h>
+
+#include "module-lirc-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("LIRC volume control");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("config=<config file> sink=<sink name> appname=<lirc application name>");
+
+static const char* const valid_modargs[] = {
+    "config",
+    "sink",
+    "appname",
+    NULL,
+};
+
+struct userdata {
+    int lirc_fd;
+    pa_io_event *io;
+    struct lirc_config *config;
+    char *sink_name;
+    pa_module *module;
+    float mute_toggle_save;
+};
+
+static int lirc_in_use = 0;
+
+static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) {
+    struct userdata *u = userdata;
+    char *name = NULL, *code = NULL;
+
+    pa_assert(io);
+    pa_assert(u);
+
+    if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
+        pa_log("Lost connection to LIRC daemon.");
+        goto fail;
+    }
+
+    if (events & PA_IO_EVENT_INPUT) {
+        char *c;
+
+        if (lirc_nextcode(&code) != 0 || !code) {
+            pa_log("lirc_nextcode() failed.");
+            goto fail;
+        }
+
+        c = pa_xstrdup(code);
+        c[strcspn(c, "\n\r")] = 0;
+        pa_log_debug("Raw IR code '%s'", c);
+        pa_xfree(c);
+
+        while (lirc_code2char(u->config, code, &name) == 0 && name) {
+            enum {
+                INVALID,
+                UP,
+                DOWN,
+                MUTE,
+                RESET,
+                MUTE_TOGGLE
+            } volchange = INVALID;
+
+            pa_log_info("Translated IR code '%s'", name);
+
+            if (strcasecmp(name, "volume-up") == 0)
+                volchange = UP;
+            else if (strcasecmp(name, "volume-down") == 0)
+                volchange = DOWN;
+            else if (strcasecmp(name, "mute") == 0)
+                volchange = MUTE;
+            else if (strcasecmp(name, "mute-toggle") == 0)
+                volchange = MUTE_TOGGLE;
+            else if (strcasecmp(name, "reset") == 0)
+                volchange = RESET;
+
+            if (volchange == INVALID)
+                pa_log_warn("Recieved unknown IR code '%s'", name);
+            else {
+                pa_sink *s;
+
+                if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
+                    pa_log("Failed to get sink '%s'", u->sink_name);
+                else {
+                    int i;
+                    pa_cvolume cv = *pa_sink_get_volume(s);
+
+#define DELTA (PA_VOLUME_NORM/20)
+
+                    switch (volchange) {
+                        case UP:
+                            for (i = 0; i < cv.channels; i++) {
+                                cv.values[i] += DELTA;
+
+                                if (cv.values[i] > PA_VOLUME_NORM)
+                                    cv.values[i] = PA_VOLUME_NORM;
+                            }
+
+                            pa_sink_set_volume(s, &cv);
+                            break;
+
+                        case DOWN:
+                            for (i = 0; i < cv.channels; i++) {
+                                if (cv.values[i] >= DELTA)
+                                    cv.values[i] -= DELTA;
+                                else
+                                    cv.values[i] = PA_VOLUME_MUTED;
+                            }
+
+                            pa_sink_set_volume(s, &cv);
+                            break;
+
+                        case MUTE:
+                            pa_sink_set_mute(s, 0);
+                            break;
+
+                        case RESET:
+                            pa_sink_set_mute(s, 1);
+                            break;
+
+                        case MUTE_TOGGLE:
+
+                            pa_sink_set_mute(s, !pa_sink_get_mute(s));
+                            break;
+
+                        case INVALID:
+                            ;
+                    }
+                }
+            }
+        }
+    }
+
+    pa_xfree(code);
+
+    return;
+
+fail:
+    u->module->core->mainloop->io_free(u->io);
+    u->io = NULL;
+
+    pa_module_unload_request(u->module);
+
+    pa_xfree(code);
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (lirc_in_use) {
+        pa_log("module-lirc may no be loaded twice.");
+        return -1;
+    }
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->module = m;
+    u->io = NULL;
+    u->config = NULL;
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+    u->lirc_fd = -1;
+    u->mute_toggle_save = 0;
+
+    if ((u->lirc_fd = lirc_init((char*) pa_modargs_get_value(ma, "appname", "pulseaudio"), 1)) < 0) {
+        pa_log("lirc_init() failed.");
+        goto fail;
+    }
+
+    if (lirc_readconfig((char*) pa_modargs_get_value(ma, "config", NULL), &u->config, NULL) < 0) {
+        pa_log("lirc_readconfig() failed.");
+        goto fail;
+    }
+
+    u->io = m->core->mainloop->io_new(m->core->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
+
+    lirc_in_use = 1;
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->io)
+        m->core->mainloop->io_free(u->io);
+
+    if (u->config)
+        lirc_freeconfig(u->config);
+
+    if (u->lirc_fd >= 0)
+        lirc_deinit();
+
+    pa_xfree(u->sink_name);
+    pa_xfree(u);
+
+    lirc_in_use = 0;
+}
similarity index 56%
rename from polyp/module-match.c
rename to src/modules/module-match.c
index 3599a830b08017c4617ab945169cde1081e674ac..769a6b59ef32140a94332323ed98bd39af3e393a 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
@@ -24,7 +24,6 @@
 #endif
 
 #include <unistd.h>
-#include <assert.h>
 #include <string.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "module.h"
-#include "util.h"
-#include "modargs.h"
-#include "log.h"
-#include "subscribe.h"
-#include "xmalloc.h"
-#include "sink-input.h"
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/core-util.h>
+
 #include "module-match-symdef.h"
 
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Sink input matching module")
-PA_MODULE_USAGE("table=<filename>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Playback stream expression matching module");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("table=<filename>");
 
 #define WHITESPACE "\n\r \t"
 
-#ifndef DEFAULT_CONFIG_DIR
-#define DEFAULT_CONFIG_DIR "/etc/polypaudio"
-#endif
-
-#define DEFAULT_MATCH_TABLE_FILE DEFAULT_CONFIG_DIR"/match.table"
-#define DEFAULT_MATCH_TABLE_FILE_USER ".polypaudio/match.table"
+#define DEFAULT_MATCH_TABLE_FILE PA_DEFAULT_CONFIG_DIR"/match.table"
+#define DEFAULT_MATCH_TABLE_FILE_USER "match.table"
 
 static const char* const valid_modargs[] = {
     "table",
@@ -68,7 +68,7 @@ struct rule {
 
 struct userdata {
     struct rule *rules;
-    struct pa_subscription *subscription;
+    pa_subscription *subscription;
 };
 
 static int load_rules(struct userdata *u, const char *filename) {
@@ -78,15 +78,21 @@ static int load_rules(struct userdata *u, const char *filename) {
     struct rule *end = NULL;
     char *fn = NULL;
 
-    f = filename ?
-        fopen(fn = pa_xstrdup(filename), "r") :
-        pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
+    pa_assert(u);
+
+    if (filename)
+        f = fopen(fn = pa_xstrdup(filename), "r");
+    else
+        f = pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
 
     if (!f) {
-        pa_log(__FILE__": failed to open file '%s': %s\n", fn, strerror(errno));
+        pa_xfree(fn);
+        pa_log("Failed to open file config file: %s", pa_cstrerror(errno));
         goto finish;
     }
 
+    pa_lock_fd(fileno(f), 1);
+
     while (!feof(f)) {
         char *d, *v;
         pa_volume_t volume;
@@ -94,12 +100,12 @@ static int load_rules(struct userdata *u, const char *filename) {
         regex_t regex;
         char ln[256];
         struct rule *rule;
-        
+
         if (!fgets(ln, sizeof(ln), f))
             break;
 
         n++;
-        
+
         pa_strip_nl(ln);
 
         if (ln[0] == '#' || !*ln )
@@ -108,27 +114,27 @@ static int load_rules(struct userdata *u, const char *filename) {
         d = ln+strcspn(ln, WHITESPACE);
         v = d+strspn(d, WHITESPACE);
 
-        
+
         if (!*v) {
-            pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words\n", filename, n);
+            pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words", filename, n);
             goto finish;
         }
 
         *d = 0;
         if (pa_atou(v, &k) < 0) {
-            pa_log(__FILE__": [%s:%u] failed to parse volume\n", filename, n);
+            pa_log("[%s:%u] failed to parse volume", filename, n);
             goto finish;
         }
 
         volume = (pa_volume_t) k;
 
-        
+
         if (regcomp(&regex, ln, REG_EXTENDED|REG_NOSUB) != 0) {
-            pa_log(__FILE__": [%s:%u] invalid regular expression\n", filename, n);
+            pa_log("[%s:%u] invalid regular expression", filename, n);
             goto finish;
         }
 
-        rule = pa_xmalloc(sizeof(struct rule));
+        rule = pa_xnew(struct rule, 1);
         rule->regex = regex;
         rule->volume = volume;
         rule->next = NULL;
@@ -138,15 +144,17 @@ static int load_rules(struct userdata *u, const char *filename) {
         else
             u->rules = rule;
         end = rule;
-        
+
         *d = 0;
     }
 
     ret = 0;
-    
+
 finish:
-    if (f)
+    if (f) {
+        pa_lock_fd(fileno(f), 0);
         fclose(f);
+    }
 
     if (fn)
         pa_xfree(fn);
@@ -154,71 +162,78 @@ finish:
     return ret;
 }
 
-static void callback(struct pa_core *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) {
+static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u =  userdata;
-    struct pa_sink_input *si;
+    pa_sink_input *si;
     struct rule *r;
-    assert(c && u);
+    const char *n;
+
+    pa_assert(c);
+    pa_assert(u);
 
     if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW))
         return;
 
-    if (!(si = pa_idxset_get_by_index(c->sink_inputs, index)))
+    if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
         return;
 
-    if (!si->name)
+    if (!(n = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)))
         return;
-    
+
     for (r = u->rules; r; r = r->next) {
-        if (!regexec(&r->regex, si->name, 0, NULL, 0)) {
-            pa_log_debug(__FILE__": changing volume of sink input '%s' to 0x%03x\n", si->name, r->volume);
-            pa_sink_input_set_volume(si, r->volume);
+        if (!regexec(&r->regex, n, 0, NULL, 0)) {
+            pa_cvolume cv;
+            pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
+            pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
+            pa_sink_input_set_volume(si, &cv);
         }
     }
 }
 
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct pa_modargs *ma = NULL;
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
     struct userdata *u;
-    assert(c && m);
+
+    pa_assert(m);
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": Failed to parse module arguments\n");
+        pa_log("Failed to parse module arguments");
         goto fail;
     }
 
-    u = pa_xmalloc(sizeof(struct userdata));
+    u = pa_xnew(struct userdata, 1);
     u->rules = NULL;
     u->subscription = NULL;
     m->userdata = u;
-    
+
     if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0)
         goto fail;
 
-    u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u);
+    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u);
 
     pa_modargs_free(ma);
     return 0;
 
 fail:
-    pa__done(c, m);
+    pa__done(m);
 
     if (ma)
         pa_modargs_free(ma);
     return  -1;
 }
 
-void pa__done(struct pa_core *c, struct pa_module*m) {
+void pa__done(pa_module*m) {
     struct userdata* u;
     struct rule *r, *n;
-    assert(c && m);
+
+    pa_assert(m);
 
     if (!(u = m->userdata))
         return;
 
     if (u->subscription)
         pa_subscription_free(u->subscription);
-    
+
     for (r = u->rules; r; r = n) {
         n = r->next;
 
@@ -228,5 +243,3 @@ void pa__done(struct pa_core *c, struct pa_module*m) {
 
     pa_xfree(u);
 }
-
-
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
new file mode 100644 (file)
index 0000000..4388e49
--- /dev/null
@@ -0,0 +1,260 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <linux/input.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+
+#include "module-mmkbd-evdev-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Multimedia keyboard support via Linux evdev");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("device=<evdev device> sink=<sink name>");
+
+#define DEFAULT_DEVICE "/dev/input/event0"
+
+/*
+ * This isn't defined in older kernel headers and there is no way of
+ * detecting it.
+ */
+struct _input_id {
+    __u16 bustype;
+    __u16 vendor;
+    __u16 product;
+    __u16 version;
+};
+
+static const char* const valid_modargs[] = {
+    "device",
+    "sink",
+    NULL,
+};
+
+struct userdata {
+    int fd, fd_type;
+    pa_io_event *io;
+    char *sink_name;
+    pa_module *module;
+};
+
+static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(io);
+    pa_assert(u);
+
+    if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
+        pa_log("Lost connection to evdev device.");
+        goto fail;
+    }
+
+    if (events & PA_IO_EVENT_INPUT) {
+        struct input_event ev;
+
+        if (pa_loop_read(u->fd, &ev, sizeof(ev), &u->fd_type) <= 0) {
+            pa_log("Failed to read from event device: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+        if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) {
+            enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID;
+
+            pa_log_debug("Key code=%u, value=%u", ev.code, ev.value);
+
+            switch (ev.code) {
+                case KEY_VOLUMEDOWN:  volchange = DOWN; break;
+                case KEY_VOLUMEUP:    volchange = UP; break;
+                case KEY_MUTE:        volchange = MUTE_TOGGLE; break;
+            }
+
+            if (volchange != INVALID) {
+                pa_sink *s;
+
+                if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
+                    pa_log("Failed to get sink '%s'", u->sink_name);
+                else {
+                    int i;
+                    pa_cvolume cv = *pa_sink_get_volume(s);
+
+#define DELTA (PA_VOLUME_NORM/20)
+
+                    switch (volchange) {
+                        case UP:
+                            for (i = 0; i < cv.channels; i++) {
+                                cv.values[i] += DELTA;
+
+                                if (cv.values[i] > PA_VOLUME_NORM)
+                                    cv.values[i] = PA_VOLUME_NORM;
+                            }
+
+                            pa_sink_set_volume(s, &cv);
+                            break;
+
+                        case DOWN:
+                            for (i = 0; i < cv.channels; i++) {
+                                if (cv.values[i] >= DELTA)
+                                    cv.values[i] -= DELTA;
+                                else
+                                    cv.values[i] = PA_VOLUME_MUTED;
+                            }
+
+                            pa_sink_set_volume(s, &cv);
+                            break;
+
+                        case MUTE_TOGGLE:
+
+                            pa_sink_set_mute(s, !pa_sink_get_mute(s));
+                            break;
+
+                        case INVALID:
+                            ;
+                    }
+                }
+            }
+        }
+    }
+
+    return;
+
+fail:
+    u->module->core->mainloop->io_free(u->io);
+    u->io = NULL;
+
+    pa_module_unload_request(u->module);
+}
+
+#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
+
+int pa__init(pa_module*m) {
+
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    int version;
+    struct _input_id input_id;
+    char name[256];
+    uint8_t evtype_bitmask[EV_MAX/8 + 1];
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata,1);
+    u->module = m;
+    u->io = NULL;
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+    u->fd = -1;
+    u->fd_type = 0;
+
+    if ((u->fd = open(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), O_RDONLY)) < 0) {
+        pa_log("failed to open evdev device: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (ioctl(u->fd, EVIOCGVERSION, &version) < 0) {
+        pa_log("EVIOCGVERSION failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_log_info("evdev driver version %i.%i.%i", version >> 16, (version >> 8) & 0xff, version & 0xff);
+
+    if(ioctl(u->fd, EVIOCGID, &input_id)) {
+        pa_log("EVIOCGID failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_log_info("evdev vendor 0x%04hx product 0x%04hx version 0x%04hx bustype %u",
+                input_id.vendor, input_id.product, input_id.version, input_id.bustype);
+
+    memset(name, 0, sizeof(name));
+    if(ioctl(u->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
+        pa_log("EVIOCGNAME failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_log_info("evdev device name: %s", name);
+
+    memset(evtype_bitmask, 0, sizeof(evtype_bitmask));
+    if (ioctl(u->fd, EVIOCGBIT(0, EV_MAX), evtype_bitmask) < 0) {
+        pa_log("EVIOCGBIT failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (!test_bit(EV_KEY, evtype_bitmask)) {
+        pa_log("Device has no keys.");
+        goto fail;
+    }
+
+    u->io = m->core->mainloop->io_new(m->core->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->io)
+        m->core->mainloop->io_free(u->io);
+
+    if (u->fd >= 0)
+        pa_assert_se(pa_close(u->fd) == 0);
+
+    pa_xfree(u->sink_name);
+    pa_xfree(u);
+}
similarity index 53%
rename from polyp/module-native-protocol-fd.c
rename to src/modules/module-native-protocol-fd.c
index 11f6e57208fd0d4bf16bfe84def9edc266a11b6c..1a6f5368c0c0e16ae92acc7d30ae6e73a5c4949d 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #endif
 
 #include <stdio.h>
-#include <assert.h>
 #include <unistd.h>
 
-#include "module.h"
-#include "iochannel.h"
-#include "modargs.h"
-#include "protocol-native.h"
-#include "log.h"
+#include <pulsecore/module.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/protocol-native.h>
+#include <pulsecore/log.h>
+
 #include "module-native-protocol-fd-symdef.h"
 
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Native protocol autospawn helper")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Native protocol autospawn helper");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
 
 static const char* const valid_modargs[] = {
     "fd",
@@ -45,25 +47,26 @@ static const char* const valid_modargs[] = {
     NULL,
 };
 
-int pa__init(struct pa_core *c, struct pa_module*m) {
-    struct pa_iochannel *io;
-    struct pa_modargs *ma;
+int pa__init(pa_module*m) {
+    pa_iochannel *io;
+    pa_modargs *ma;
     int fd, r = -1;
-    assert(c && m);
+
+    pa_assert(m);
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments.\n");
+        pa_log("Failed to parse module arguments.");
         goto finish;
     }
 
     if (pa_modargs_get_value_s32(ma, "fd", &fd) < 0) {
-        pa_log(__FILE__": invalid file descriptor.\n");
+        pa_log("Invalid file descriptor.");
         goto finish;
     }
-    
-    io = pa_iochannel_new(c->mainloop, fd, fd);
 
-    if (!(m->userdata = pa_protocol_native_new_iochannel(c, io, m, ma))) {
+    io = pa_iochannel_new(m->core->mainloop, fd, fd);
+
+    if (!(m->userdata = pa_protocol_native_new_iochannel(m->core, io, m, ma))) {
         pa_iochannel_free(io);
         goto finish;
     }
@@ -73,12 +76,12 @@ int pa__init(struct pa_core *c, struct pa_module*m) {
 finish:
     if (ma)
         pa_modargs_free(ma);
-    
+
     return r;
 }
 
-void pa__done(struct pa_core *c, struct pa_module*m) {
-    assert(c && m);
+void pa__done(pa_module*m) {
+    pa_assert(m);
 
     pa_protocol_native_free(m->userdata);
 }
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
new file mode 100644 (file)
index 0000000..604ab15
--- /dev/null
@@ -0,0 +1,337 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/rtclock.h>
+
+#include "module-null-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Clocked NULL sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "sink_name=<name of sink> "
+        "channel_map=<channel map> "
+        "description=<description for the sink>");
+
+#define DEFAULT_SINK_NAME "null"
+#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    size_t block_size;
+
+    pa_usec_t block_usec;
+    pa_usec_t timestamp;
+};
+
+static const char* const valid_modargs[] = {
+    "rate",
+    "format",
+    "channels",
+    "sink_name",
+    "channel_map",
+    "description",
+    NULL
+};
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
+                u->timestamp = pa_rtclock_usec();
+
+            break;
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t now;
+
+            now = pa_rtclock_usec();
+            *((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0;
+
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    u = s->userdata;
+    pa_assert(u);
+
+    u->block_usec = pa_sink_get_requested_latency_within_thread(s);
+}
+
+static void process_rewind(struct userdata *u, pa_usec_t now) {
+    size_t rewind_nbytes, in_buffer;
+    pa_usec_t delay;
+
+    pa_assert(u);
+
+    /* Figure out how much we shall rewind and reset the counter */
+    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+    u->sink->thread_info.rewind_nbytes = 0;
+
+    pa_assert(rewind_nbytes > 0);
+    pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+
+    if (u->timestamp <= now)
+        return;
+
+    delay = u->timestamp - now;
+    in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);
+
+    if (in_buffer <= 0)
+        return;
+
+    if (rewind_nbytes > in_buffer)
+        rewind_nbytes = in_buffer;
+
+    pa_sink_process_rewind(u->sink, rewind_nbytes);
+    u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
+
+    pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+}
+
+static void process_render(struct userdata *u, pa_usec_t now) {
+    size_t ate = 0;
+
+    pa_assert(u);
+
+    /* This is the configured latency. Sink inputs connected to us
+    might not have a single frame more than the maxrequest value
+    queed. Hence: at maximum read this many bytes from the sink
+    inputs. */
+
+    /* Fill the buffer up the the latency size */
+    while (u->timestamp < now + u->block_usec) {
+        pa_memchunk chunk;
+
+        pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk);
+        pa_memblock_unref(chunk.memblock);
+
+/*         pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */
+        u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
+
+        ate += chunk.length;
+
+        if (ate >= u->sink->thread_info.max_request)
+            break;
+    }
+
+/*     pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    u->timestamp = pa_rtclock_usec();
+
+    for (;;) {
+        int ret;
+
+        /* Render some data and drop it immediately */
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+            pa_usec_t now;
+
+            now = pa_rtclock_usec();
+
+            if (u->sink->thread_info.rewind_nbytes > 0)
+                process_rewind(u, now);
+
+            if (u->timestamp <= now)
+                process_render(u, now);
+
+            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
+        } else
+            pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    pa_sink_new_data data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output"));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink object.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    pa_sink_set_latency_range(u->sink, (pa_usec_t) -1, MAX_LATENCY_USEC);
+    u->block_usec = u->sink->thread_info.max_latency;
+
+    u->sink->thread_info.max_rewind =
+        u->sink->thread_info.max_request =
+        pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_sink_put(u->sink);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c
new file mode 100644 (file)
index 0000000..76b13ec
--- /dev/null
@@ -0,0 +1,1530 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* General power management rules:
+ *
+ *   When SUSPENDED we close the audio device.
+ *
+ *   We make no difference between IDLE and RUNNING in our handling.
+ *
+ *   As long as we are in RUNNING/IDLE state we will *always* write data to
+ *   the device. If none is avilable from the inputs, we write silence
+ *   instead.
+ *
+ *   If power should be saved on IDLE module-suspend-on-idle should be used.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <signal.h>
+#include <poll.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+
+#include "oss-util.h"
+#include "module-oss-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("OSS Sink/Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "source_name=<name for the source> "
+        "device=<OSS device> "
+        "record=<enable source?> "
+        "playback=<enable sink?> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "fragments=<number of fragments> "
+        "fragment_size=<fragment size> "
+        "channel_map=<channel map> "
+        "mmap=<enable memory mapping?>");
+
+#define DEFAULT_DEVICE "/dev/dsp"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    char *device_name;
+
+    pa_memchunk memchunk;
+
+    size_t frame_size;
+    uint32_t in_fragment_size, out_fragment_size, in_nfrags, out_nfrags, in_hwbuf_size, out_hwbuf_size;
+    pa_bool_t use_getospace, use_getispace;
+    pa_bool_t use_getodelay;
+
+    pa_bool_t sink_suspended, source_suspended;
+
+    int fd;
+    int mode;
+
+    int mixer_fd;
+    int mixer_devmask;
+
+    int nfrags, frag_size;
+
+    pa_bool_t use_mmap;
+    unsigned out_mmap_current, in_mmap_current;
+    void *in_mmap, *out_mmap;
+    pa_memblock **in_mmap_memblocks, **out_mmap_memblocks;
+
+    int in_mmap_saved_nfrags, out_mmap_saved_nfrags;
+
+    pa_rtpoll_item *rtpoll_item;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "source_name",
+    "device",
+    "record",
+    "playback",
+    "fragments",
+    "fragment_size",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "mmap",
+    NULL
+};
+
+static void trigger(struct userdata *u, pa_bool_t quick) {
+    int enable_bits = 0, zero = 0;
+
+    pa_assert(u);
+
+    if (u->fd < 0)
+        return;
+
+     pa_log_debug("trigger");
+
+    if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+        enable_bits |= PCM_ENABLE_INPUT;
+
+    if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
+        enable_bits |= PCM_ENABLE_OUTPUT;
+
+    pa_log_debug("trigger: %i", enable_bits);
+
+
+    if (u->use_mmap) {
+
+        if (!quick)
+            ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero);
+
+#ifdef SNDCTL_DSP_HALT
+        if (enable_bits == 0)
+            if (ioctl(u->fd, SNDCTL_DSP_HALT, NULL) < 0)
+                pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno));
+#endif
+
+        if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0)
+            pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno));
+
+        if (u->sink && !(enable_bits & PCM_ENABLE_OUTPUT)) {
+            pa_log_debug("clearing playback buffer");
+            pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &u->sink->sample_spec);
+        }
+
+    } else {
+
+        if (enable_bits)
+            if (ioctl(u->fd, SNDCTL_DSP_POST, NULL) < 0)
+                pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno));
+
+        if (!quick) {
+            /*
+             * Some crappy drivers do not start the recording until we
+             * read something.  Without this snippet, poll will never
+             * register the fd as ready.
+             */
+
+            if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+                uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);
+                pa_read(u->fd, buf, u->in_fragment_size, NULL);
+                pa_xfree(buf);
+            }
+        }
+    }
+}
+
+static void mmap_fill_memblocks(struct userdata *u, unsigned n) {
+    pa_assert(u);
+    pa_assert(u->out_mmap_memblocks);
+
+/*     pa_log("Mmmap writing %u blocks", n); */
+
+    while (n > 0) {
+        pa_memchunk chunk;
+
+        if (u->out_mmap_memblocks[u->out_mmap_current])
+            pa_memblock_unref_fixed(u->out_mmap_memblocks[u->out_mmap_current]);
+
+        chunk.memblock = u->out_mmap_memblocks[u->out_mmap_current] =
+            pa_memblock_new_fixed(
+                    u->core->mempool,
+                    (uint8_t*) u->out_mmap + u->out_fragment_size * u->out_mmap_current,
+                    u->out_fragment_size,
+                    1);
+
+        chunk.length = pa_memblock_get_length(chunk.memblock);
+        chunk.index = 0;
+
+        pa_sink_render_into_full(u->sink, &chunk);
+
+        u->out_mmap_current++;
+        while (u->out_mmap_current >= u->out_nfrags)
+            u->out_mmap_current -= u->out_nfrags;
+
+        n--;
+    }
+}
+
+static int mmap_write(struct userdata *u) {
+    struct count_info info;
+
+    pa_assert(u);
+    pa_assert(u->sink);
+
+/*     pa_log("Mmmap writing..."); */
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
+        pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    info.blocks += u->out_mmap_saved_nfrags;
+    u->out_mmap_saved_nfrags = 0;
+
+    if (info.blocks > 0)
+        mmap_fill_memblocks(u, info.blocks);
+
+    return info.blocks;
+}
+
+static void mmap_post_memblocks(struct userdata *u, unsigned n) {
+    pa_assert(u);
+    pa_assert(u->in_mmap_memblocks);
+
+/*     pa_log("Mmmap reading %u blocks", n); */
+
+    while (n > 0) {
+        pa_memchunk chunk;
+
+        if (!u->in_mmap_memblocks[u->in_mmap_current]) {
+
+            chunk.memblock = u->in_mmap_memblocks[u->in_mmap_current] =
+                pa_memblock_new_fixed(
+                        u->core->mempool,
+                        (uint8_t*) u->in_mmap + u->in_fragment_size*u->in_mmap_current,
+                        u->in_fragment_size,
+                        1);
+
+            chunk.length = pa_memblock_get_length(chunk.memblock);
+            chunk.index = 0;
+
+            pa_source_post(u->source, &chunk);
+        }
+
+        u->in_mmap_current++;
+        while (u->in_mmap_current >= u->in_nfrags)
+            u->in_mmap_current -= u->in_nfrags;
+
+        n--;
+    }
+}
+
+static void mmap_clear_memblocks(struct userdata*u, unsigned n) {
+    unsigned i = u->in_mmap_current;
+
+    pa_assert(u);
+    pa_assert(u->in_mmap_memblocks);
+
+    if (n > u->in_nfrags)
+        n = u->in_nfrags;
+
+    while (n > 0) {
+        if (u->in_mmap_memblocks[i]) {
+            pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
+            u->in_mmap_memblocks[i] = NULL;
+        }
+
+        i++;
+        while (i >= u->in_nfrags)
+            i -= u->in_nfrags;
+
+        n--;
+    }
+}
+
+static int mmap_read(struct userdata *u) {
+    struct count_info info;
+    pa_assert(u);
+    pa_assert(u->source);
+
+/*     pa_log("Mmmap reading..."); */
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
+        pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+/*     pa_log("... %i", info.blocks); */
+
+    info.blocks += u->in_mmap_saved_nfrags;
+    u->in_mmap_saved_nfrags = 0;
+
+    if (info.blocks > 0) {
+        mmap_post_memblocks(u, info.blocks);
+        mmap_clear_memblocks(u, u->in_nfrags/2);
+    }
+
+    return info.blocks;
+}
+
+static pa_usec_t mmap_sink_get_latency(struct userdata *u) {
+    struct count_info info;
+    size_t bpos, n;
+
+    pa_assert(u);
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
+        pa_log("SNDCTL_DSP_GETOPTR: %s", pa_cstrerror(errno));
+        return 0;
+    }
+
+    u->out_mmap_saved_nfrags += info.blocks;
+
+    bpos = ((u->out_mmap_current + u->out_mmap_saved_nfrags) * u->out_fragment_size) % u->out_hwbuf_size;
+
+    if (bpos <= (size_t) info.ptr)
+        n = u->out_hwbuf_size - (info.ptr - bpos);
+    else
+        n = bpos - info.ptr;
+
+/*     pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->out_fragment_size, u->out_fragments); */
+
+    return pa_bytes_to_usec(n, &u->sink->sample_spec);
+}
+
+static pa_usec_t mmap_source_get_latency(struct userdata *u) {
+    struct count_info info;
+    size_t bpos, n;
+
+    pa_assert(u);
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
+        pa_log("SNDCTL_DSP_GETIPTR: %s", pa_cstrerror(errno));
+        return 0;
+    }
+
+    u->in_mmap_saved_nfrags += info.blocks;
+    bpos = ((u->in_mmap_current + u->in_mmap_saved_nfrags) * u->in_fragment_size) % u->in_hwbuf_size;
+
+    if (bpos <= (size_t) info.ptr)
+        n = info.ptr - bpos;
+    else
+        n = u->in_hwbuf_size - bpos + info.ptr;
+
+/*     pa_log("n = %u, bpos = %u, ptr = %u, total=%u, fragsize = %u, n_frags = %u\n", n, bpos, (unsigned) info.ptr, total, u->in_fragment_size, u->in_fragments);  */
+
+    return pa_bytes_to_usec(n, &u->source->sample_spec);
+}
+
+static pa_usec_t io_sink_get_latency(struct userdata *u) {
+    pa_usec_t r = 0;
+
+    pa_assert(u);
+
+    if (u->use_getodelay) {
+        int arg;
+
+        if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
+            pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno));
+            u->use_getodelay = 0;
+        } else
+            r = pa_bytes_to_usec(arg, &u->sink->sample_spec);
+
+    }
+
+    if (!u->use_getodelay && u->use_getospace) {
+        struct audio_buf_info info;
+
+        if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
+            pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
+            u->use_getospace = 0;
+        } else
+            r = pa_bytes_to_usec(info.bytes, &u->sink->sample_spec);
+    }
+
+    if (u->memchunk.memblock)
+        r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
+
+    return r;
+}
+
+
+static pa_usec_t io_source_get_latency(struct userdata *u) {
+    pa_usec_t r = 0;
+
+    pa_assert(u);
+
+    if (u->use_getispace) {
+        struct audio_buf_info info;
+
+        if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
+            pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
+            u->use_getispace = 0;
+        } else
+            r = pa_bytes_to_usec(info.bytes, &u->source->sample_spec);
+    }
+
+    return r;
+}
+
+static void build_pollfd(struct userdata *u) {
+    struct pollfd *pollfd;
+
+    pa_assert(u);
+    pa_assert(u->fd >= 0);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = u->fd;
+    pollfd->events = 0;
+    pollfd->revents = 0;
+}
+
+static int suspend(struct userdata *u) {
+    pa_assert(u);
+    pa_assert(u->fd >= 0);
+
+    pa_log_info("Suspending...");
+
+    if (u->out_mmap_memblocks) {
+        unsigned i;
+        for (i = 0; i < u->out_nfrags; i++)
+            if (u->out_mmap_memblocks[i]) {
+                pa_memblock_unref_fixed(u->out_mmap_memblocks[i]);
+                u->out_mmap_memblocks[i] = NULL;
+            }
+    }
+
+    if (u->in_mmap_memblocks) {
+        unsigned i;
+        for (i = 0; i < u->in_nfrags; i++)
+            if (u->in_mmap_memblocks[i]) {
+                pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
+                u->in_mmap_memblocks[i] = NULL;
+            }
+    }
+
+    if (u->in_mmap && u->in_mmap != MAP_FAILED) {
+        munmap(u->in_mmap, u->in_hwbuf_size);
+        u->in_mmap = NULL;
+    }
+
+    if (u->out_mmap && u->out_mmap != MAP_FAILED) {
+        munmap(u->out_mmap, u->out_hwbuf_size);
+        u->out_mmap = NULL;
+    }
+
+    /* Let's suspend */
+    ioctl(u->fd, SNDCTL_DSP_SYNC, NULL);
+    pa_close(u->fd);
+    u->fd = -1;
+
+    if (u->rtpoll_item) {
+        pa_rtpoll_item_free(u->rtpoll_item);
+        u->rtpoll_item = NULL;
+    }
+
+    pa_log_info("Device suspended...");
+
+    return 0;
+}
+
+static int sink_get_volume(pa_sink *s);
+static int source_get_volume(pa_source *s);
+
+static int unsuspend(struct userdata *u) {
+    int m;
+    pa_sample_spec ss, *ss_original;
+    int frag_size, in_frag_size, out_frag_size;
+    int in_nfrags, out_nfrags;
+    struct audio_buf_info info;
+
+    pa_assert(u);
+    pa_assert(u->fd < 0);
+
+    m = u->mode;
+
+    pa_log_info("Trying resume...");
+
+    if ((u->fd = pa_oss_open(u->device_name, &m, NULL)) < 0) {
+        pa_log_warn("Resume failed, device busy (%s)", pa_cstrerror(errno));
+        return -1;
+
+    if (m != u->mode)
+        pa_log_warn("Resume failed, couldn't open device with original access mode.");
+        goto fail;
+    }
+
+    if (u->nfrags >= 2 && u->frag_size >= 1)
+        if (pa_oss_set_fragments(u->fd, u->nfrags, u->frag_size) < 0) {
+            pa_log_warn("Resume failed, couldn't set original fragment settings.");
+            goto fail;
+        }
+
+    ss = *(ss_original = u->sink ? &u->sink->sample_spec : &u->source->sample_spec);
+    if (pa_oss_auto_format(u->fd, &ss) < 0 || !pa_sample_spec_equal(&ss, ss_original)) {
+        pa_log_warn("Resume failed, couldn't set original sample format settings.");
+        goto fail;
+    }
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
+        pa_log_warn("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    in_frag_size = out_frag_size = frag_size;
+    in_nfrags = out_nfrags = u->nfrags;
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
+        in_frag_size = info.fragsize;
+        in_nfrags = info.fragstotal;
+    }
+
+    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
+        out_frag_size = info.fragsize;
+        out_nfrags = info.fragstotal;
+    }
+
+    if ((u->source && (in_frag_size != (int) u->in_fragment_size || in_nfrags != (int) u->in_nfrags)) ||
+        (u->sink && (out_frag_size != (int) u->out_fragment_size || out_nfrags != (int) u->out_nfrags))) {
+        pa_log_warn("Resume failed, input fragment settings don't match.");
+        goto fail;
+    }
+
+    if (u->use_mmap) {
+        if (u->source) {
+            if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
+                pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno));
+                goto fail;
+            }
+        }
+
+        if (u->sink) {
+            if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
+                pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno));
+                if (u->in_mmap && u->in_mmap != MAP_FAILED) {
+                    munmap(u->in_mmap, u->in_hwbuf_size);
+                    u->in_mmap = NULL;
+                }
+
+                goto fail;
+            }
+
+            pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss);
+        }
+    }
+
+    u->out_mmap_current = u->in_mmap_current = 0;
+    u->out_mmap_saved_nfrags = u->in_mmap_saved_nfrags = 0;
+
+    pa_assert(!u->rtpoll_item);
+
+    build_pollfd(u);
+
+    if (u->sink)
+        sink_get_volume(u->sink);
+    if (u->source)
+        source_get_volume(u->source);
+
+    pa_log_info("Resumed successfully...");
+
+    return 0;
+
+fail:
+    pa_close(u->fd);
+    u->fd = -1;
+    return -1;
+}
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+    int ret;
+    pa_bool_t do_trigger = FALSE, quick = TRUE;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t r = 0;
+
+            if (u->fd >= 0) {
+                if (u->use_mmap)
+                    r = mmap_sink_get_latency(u);
+                else
+                    r = io_sink_get_latency(u);
+            }
+
+            *((pa_usec_t*) data) = r;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+                case PA_SINK_SUSPENDED:
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+                    if (!u->source || u->source_suspended) {
+                        if (suspend(u) < 0)
+                            return -1;
+                    }
+
+                    do_trigger = TRUE;
+
+                    u->sink_suspended = TRUE;
+                    break;
+
+                case PA_SINK_IDLE:
+                case PA_SINK_RUNNING:
+
+                    if (u->sink->thread_info.state == PA_SINK_INIT) {
+                        do_trigger = TRUE;
+                        quick = u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state);
+                    }
+
+                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+
+                        if (!u->source || u->source_suspended) {
+                            if (unsuspend(u) < 0)
+                                return -1;
+                            quick = FALSE;
+                        }
+
+                        do_trigger = TRUE;
+
+                        u->out_mmap_current = 0;
+                        u->out_mmap_saved_nfrags = 0;
+
+                        u->sink_suspended = FALSE;
+                    }
+
+                    break;
+
+                case PA_SINK_UNLINKED:
+                case PA_SINK_INIT:
+                    ;
+            }
+
+            break;
+
+    }
+
+    ret = pa_sink_process_msg(o, code, data, offset, chunk);
+
+    if (do_trigger)
+        trigger(u, quick);
+
+    return ret;
+}
+
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+    int ret;
+    int do_trigger = FALSE, quick = TRUE;
+
+    switch (code) {
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            pa_usec_t r = 0;
+
+            if (u->fd >= 0) {
+                if (u->use_mmap)
+                    r = mmap_source_get_latency(u);
+                else
+                    r = io_source_get_latency(u);
+            }
+
+            *((pa_usec_t*) data) = r;
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SET_STATE:
+
+            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+                case PA_SOURCE_SUSPENDED:
+                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
+
+                    if (!u->sink || u->sink_suspended) {
+                        if (suspend(u) < 0)
+                            return -1;
+                    }
+
+                    do_trigger = TRUE;
+
+                    u->source_suspended = TRUE;
+                    break;
+
+                case PA_SOURCE_IDLE:
+                case PA_SOURCE_RUNNING:
+
+                    if (u->source->thread_info.state == PA_SOURCE_INIT) {
+                        do_trigger = TRUE;
+                        quick = u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state);
+                    }
+
+                    if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
+
+                        if (!u->sink || u->sink_suspended) {
+                            if (unsuspend(u) < 0)
+                                return -1;
+                            quick = FALSE;
+                        }
+
+                        do_trigger = TRUE;
+
+                        u->in_mmap_current = 0;
+                        u->in_mmap_saved_nfrags = 0;
+
+                        u->source_suspended = FALSE;
+                    }
+                    break;
+
+                case PA_SOURCE_UNLINKED:
+                case PA_SOURCE_INIT:
+                    ;
+
+            }
+            break;
+
+    }
+
+    ret = pa_source_process_msg(o, code, data, offset, chunk);
+
+    if (do_trigger)
+        trigger(u, quick);
+
+    return ret;
+}
+
+static int sink_get_volume(pa_sink *s) {
+    struct userdata *u;
+    int r;
+
+    pa_assert_se(u = s->userdata);
+
+    pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
+
+    if (u->mixer_devmask & SOUND_MASK_VOLUME)
+        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->volume)) >= 0)
+            return r;
+
+    if (u->mixer_devmask & SOUND_MASK_PCM)
+        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->volume)) >= 0)
+            return r;
+
+    pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
+    return -1;
+}
+
+static int sink_set_volume(pa_sink *s) {
+    struct userdata *u;
+    int r;
+
+    pa_assert_se(u = s->userdata);
+
+    pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
+
+    if (u->mixer_devmask & SOUND_MASK_VOLUME)
+        if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->volume)) >= 0)
+            return r;
+
+    if (u->mixer_devmask & SOUND_MASK_PCM)
+        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->volume)) >= 0)
+            return r;
+
+    pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
+    return -1;
+}
+
+static int source_get_volume(pa_source *s) {
+    struct userdata *u;
+    int r;
+
+    pa_assert_se(u = s->userdata);
+
+    pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
+
+    if (u->mixer_devmask & SOUND_MASK_IGAIN)
+        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume)) >= 0)
+            return r;
+
+    if (u->mixer_devmask & SOUND_MASK_RECLEV)
+        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume)) >= 0)
+            return r;
+
+    pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
+    return -1;
+}
+
+static int source_set_volume(pa_source *s) {
+    struct userdata *u;
+    int r;
+
+    pa_assert_se(u = s->userdata);
+
+    pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
+
+    if (u->mixer_devmask & SOUND_MASK_IGAIN)
+        if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume)) >= 0)
+            return r;
+
+    if (u->mixer_devmask & SOUND_MASK_RECLEV)
+        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume)) >= 0)
+            return r;
+
+    pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
+    return -1;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    int write_type = 0, read_type = 0;
+    unsigned short revents = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    for (;;) {
+        int ret;
+
+/*        pa_log("loop");    */
+
+        /* Render some data and write it to the dsp */
+
+        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
+
+            if (u->use_mmap) {
+
+                if ((ret = mmap_write(u)) < 0)
+                    goto fail;
+
+                revents &= ~POLLOUT;
+
+                if (ret > 0)
+                    continue;
+
+            } else {
+                ssize_t l;
+                pa_bool_t loop = FALSE, work_done = FALSE;
+
+                l = u->out_fragment_size;
+
+                if (u->use_getospace) {
+                    audio_buf_info info;
+
+                    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
+                        pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
+                        u->use_getospace = FALSE;
+                    } else {
+                        l = info.bytes;
+
+                        /* We loop only if GETOSPACE worked and we
+                         * actually *know* that we can write more than
+                         * one fragment at a time */
+                        loop = TRUE;
+                    }
+                }
+
+                /* Round down to multiples of the fragment size,
+                 * because OSS needs that (at least some versions
+                 * do) */
+                l = (l/u->out_fragment_size) * u->out_fragment_size;
+
+                /* Hmm, so poll() signalled us that we can read
+                 * something, but GETOSPACE told us there was nothing?
+                 * Hmm, make the best of it, try to read some data, to
+                 * avoid spinning forever. */
+                if (l <= 0 && (revents & POLLOUT)) {
+                    l = u->out_fragment_size;
+                    loop = FALSE;
+                }
+
+                while (l > 0) {
+                    void *p;
+                    ssize_t t;
+
+                    if (u->memchunk.length <= 0)
+                        pa_sink_render(u->sink, l, &u->memchunk);
+
+                    pa_assert(u->memchunk.length > 0);
+
+                    p = pa_memblock_acquire(u->memchunk.memblock);
+                    t = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
+                    pa_memblock_release(u->memchunk.memblock);
+
+/*                     pa_log("wrote %i bytes of %u", t, l); */
+
+                    pa_assert(t != 0);
+
+                    if (t < 0) {
+
+                        if (errno == EINTR)
+                            continue;
+
+                        else if (errno == EAGAIN) {
+                            pa_log_debug("EAGAIN");
+
+                            revents &= ~POLLOUT;
+                            break;
+
+                        } else {
+                            pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
+                            goto fail;
+                        }
+
+                    } else {
+
+                        u->memchunk.index += t;
+                        u->memchunk.length -= t;
+
+                        if (u->memchunk.length <= 0) {
+                            pa_memblock_unref(u->memchunk.memblock);
+                            pa_memchunk_reset(&u->memchunk);
+                        }
+
+                        l -= t;
+
+                        revents &= ~POLLOUT;
+                        work_done = TRUE;
+                    }
+
+                    if (!loop)
+                        break;
+                }
+
+                if (work_done)
+                    continue;
+            }
+        }
+
+        /* Try to read some data and pass it on to the source driver. */
+
+        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
+
+            if (u->use_mmap) {
+
+                if ((ret = mmap_read(u)) < 0)
+                    goto fail;
+
+                revents &= ~POLLIN;
+
+                if (ret > 0)
+                    continue;
+
+            } else {
+
+                void *p;
+                ssize_t l;
+                pa_memchunk memchunk;
+                pa_bool_t loop = FALSE, work_done = FALSE;
+
+                l = u->in_fragment_size;
+
+                if (u->use_getispace) {
+                    audio_buf_info info;
+
+                    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
+                        pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
+                        u->use_getispace = FALSE;
+                    } else {
+                        l = info.bytes;
+                        loop = TRUE;
+                    }
+                }
+
+                l = (l/u->in_fragment_size) * u->in_fragment_size;
+
+                if (l <= 0 && (revents & POLLIN)) {
+                    l = u->in_fragment_size;
+                    loop = FALSE;
+                }
+
+                while (l > 0) {
+                    ssize_t t, k;
+
+                    pa_assert(l > 0);
+
+                    memchunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
+
+                    k = pa_memblock_get_length(memchunk.memblock);
+
+                    if (k > l)
+                        k = l;
+
+                    k = (k/u->frame_size)*u->frame_size;
+
+                    p = pa_memblock_acquire(memchunk.memblock);
+                    t = pa_read(u->fd, p, k, &read_type);
+                    pa_memblock_release(memchunk.memblock);
+
+                    pa_assert(t != 0); /* EOF cannot happen */
+
+/*                     pa_log("read %i bytes of %u", t, l); */
+
+                    if (t < 0) {
+                        pa_memblock_unref(memchunk.memblock);
+
+                        if (errno == EINTR)
+                            continue;
+
+                        else if (errno == EAGAIN) {
+                            pa_log_debug("EAGAIN");
+
+                            revents &= ~POLLIN;
+                            break;
+
+                        } else {
+                            pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
+                            goto fail;
+                        }
+
+                    } else {
+                        memchunk.index = 0;
+                        memchunk.length = t;
+
+                        pa_source_post(u->source, &memchunk);
+                        pa_memblock_unref(memchunk.memblock);
+
+                        l -= t;
+
+                        revents &= ~POLLIN;
+                        work_done = TRUE;
+                    }
+
+                    if (!loop)
+                        break;
+                }
+
+                if (work_done)
+                    continue;
+            }
+        }
+
+/*         pa_log("loop2 revents=%i", revents); */
+
+        if (u->rtpoll_item) {
+            struct pollfd *pollfd;
+
+            pa_assert(u->fd >= 0);
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+            pollfd->events =
+                ((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
+                ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        if (u->rtpoll_item) {
+            struct pollfd *pollfd;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
+                pa_log("DSP shutdown.");
+                goto fail;
+            }
+
+            revents = pollfd->revents;
+        } else
+            revents = 0;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+
+    struct audio_buf_info info;
+    struct userdata *u = NULL;
+    const char *dev;
+    int fd = -1;
+    int nfrags, frag_size;
+    int mode, caps;
+    pa_bool_t record = TRUE, playback = TRUE, use_mmap = TRUE;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    char hwdesc[64];
+    const char *name;
+    pa_bool_t namereg_fail;
+    pa_sink_new_data sink_new_data;
+    pa_source_new_data source_new_data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
+        pa_log("record= and playback= expect boolean argument.");
+        goto fail;
+    }
+
+    if (!playback && !record) {
+        pa_log("Neither playback nor record enabled for device.");
+        goto fail;
+    }
+
+    mode = (playback && record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_OSS) < 0) {
+        pa_log("Failed to parse sample specification or channel map");
+        goto fail;
+    }
+
+    nfrags = m->core->default_n_fragments;
+    frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss);
+    if (frag_size <= 0)
+        frag_size = pa_frame_size(&ss);
+
+    if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
+        pa_log("Failed to parse fragments arguments");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
+        pa_log("Failed to parse mmap argument.");
+        goto fail;
+    }
+
+    if ((fd = pa_oss_open(dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
+        goto fail;
+
+    if (use_mmap && (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER))) {
+        pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
+        use_mmap = 0;
+    }
+
+    if (use_mmap && mode == O_WRONLY) {
+        pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
+        use_mmap = 0;
+    }
+
+    if (pa_oss_get_hw_description(dev, hwdesc, sizeof(hwdesc)) >= 0)
+        pa_log_info("Hardware name is '%s'.", hwdesc);
+    else
+        hwdesc[0] = 0;
+
+    pa_log_info("Device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+
+    if (nfrags >= 2 && frag_size >= 1)
+        if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
+            goto fail;
+
+    if (pa_oss_auto_format(fd, &ss) < 0)
+        goto fail;
+
+    if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
+        pa_log("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+    pa_assert(frag_size > 0);
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->fd = fd;
+    u->mixer_fd = -1;
+    u->use_getospace = u->use_getispace = TRUE;
+    u->use_getodelay = TRUE;
+    u->mode = mode;
+    u->frame_size = pa_frame_size(&ss);
+    u->device_name = pa_xstrdup(dev);
+    u->in_nfrags = u->out_nfrags = u->nfrags = nfrags;
+    u->out_fragment_size = u->in_fragment_size = u->frag_size = frag_size;
+    u->use_mmap = use_mmap;
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+    u->rtpoll_item = NULL;
+    build_pollfd(u);
+
+    if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
+        pa_log_info("Input -- %u fragments of size %u.", info.fragstotal, info.fragsize);
+        u->in_fragment_size = info.fragsize;
+        u->in_nfrags = info.fragstotal;
+        u->use_getispace = TRUE;
+    }
+
+    if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
+        pa_log_info("Output -- %u fragments of size %u.", info.fragstotal, info.fragsize);
+        u->out_fragment_size = info.fragsize;
+        u->out_nfrags = info.fragstotal;
+        u->use_getospace = TRUE;
+    }
+
+    u->in_hwbuf_size = u->in_nfrags * u->in_fragment_size;
+    u->out_hwbuf_size = u->out_nfrags * u->out_fragment_size;
+
+    if (mode != O_WRONLY) {
+        char *name_buf = NULL;
+
+        if (use_mmap) {
+            if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+                pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
+                use_mmap = u->use_mmap = FALSE;
+                u->in_mmap = NULL;
+            } else
+                pa_log_debug("Successfully mmap()ed input buffer.");
+        }
+
+        if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
+            namereg_fail = TRUE;
+        else {
+            name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev));
+            namereg_fail = FALSE;
+        }
+
+        pa_source_new_data_init(&source_new_data);
+        source_new_data.driver = __FILE__;
+        source_new_data.module = m;
+        pa_source_new_data_set_name(&source_new_data, name);
+        source_new_data.namereg_fail = namereg_fail;
+        pa_source_new_data_set_sample_spec(&source_new_data, &ss);
+        pa_source_new_data_set_channel_map(&source_new_data, &map);
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "oss");
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
+        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
+        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->in_hwbuf_size));
+        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->in_fragment_size));
+
+        u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+        pa_source_new_data_done(&source_new_data);
+        pa_xfree(name_buf);
+
+        if (!u->source) {
+            pa_log("Failed to create source object");
+            goto fail;
+        }
+
+        u->source->parent.process_msg = source_process_msg;
+        u->source->userdata = u;
+
+        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+        pa_source_set_rtpoll(u->source, u->rtpoll);
+        u->source->refresh_volume = TRUE;
+
+        if (use_mmap)
+            u->in_mmap_memblocks = pa_xnew0(pa_memblock*, u->in_nfrags);
+    }
+
+    if (mode != O_RDONLY) {
+        char *name_buf = NULL;
+
+        if (use_mmap) {
+            if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+                if (mode == O_RDWR) {
+                    pa_log_debug("mmap() failed for input. Changing to O_WRONLY mode.");
+                    mode = O_WRONLY;
+                    goto go_on;
+                } else {
+                    pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
+                    u->use_mmap = use_mmap = FALSE;
+                    u->out_mmap = NULL;
+                }
+            } else {
+                pa_log_debug("Successfully mmap()ed output buffer.");
+                pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss);
+            }
+        }
+
+        if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
+            namereg_fail = TRUE;
+        else {
+            name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev));
+            namereg_fail = FALSE;
+        }
+
+        pa_sink_new_data_init(&sink_new_data);
+        sink_new_data.driver = __FILE__;
+        sink_new_data.module = m;
+        pa_sink_new_data_set_name(&sink_new_data, name);
+        sink_new_data.namereg_fail = namereg_fail;
+        pa_sink_new_data_set_sample_spec(&sink_new_data, &ss);
+        pa_sink_new_data_set_channel_map(&sink_new_data, &map);
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "oss");
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
+        pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
+        pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->out_hwbuf_size));
+        pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->out_fragment_size));
+
+        u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+        pa_sink_new_data_done(&sink_new_data);
+        pa_xfree(name_buf);
+
+        if (!u->sink) {
+            pa_log("Failed to create sink object");
+            goto fail;
+        }
+
+        u->sink->parent.process_msg = sink_process_msg;
+        u->sink->userdata = u;
+
+        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+        pa_sink_set_rtpoll(u->sink, u->rtpoll);
+        u->sink->refresh_volume = TRUE;
+
+        u->sink->thread_info.max_request = u->out_hwbuf_size;
+
+        if (use_mmap)
+            u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags);
+    }
+
+    if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
+        pa_bool_t do_close = TRUE;
+        u->mixer_devmask = 0;
+
+        if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
+            pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno));
+
+        else {
+            if (u->sink && (u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM))) {
+                pa_log_debug("Found hardware mixer track for playback.");
+                u->sink->flags |= PA_SINK_HW_VOLUME_CTRL;
+                u->sink->get_volume = sink_get_volume;
+                u->sink->set_volume = sink_set_volume;
+                do_close = FALSE;
+            }
+
+            if (u->source && (u->mixer_devmask & (SOUND_MASK_RECLEV|SOUND_MASK_IGAIN))) {
+                pa_log_debug("Found hardware mixer track for recording.");
+                u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL;
+                u->source->get_volume = source_get_volume;
+                u->source->set_volume = source_set_volume;
+                do_close = FALSE;
+            }
+        }
+
+        if (do_close) {
+            pa_close(u->mixer_fd);
+            u->mixer_fd = -1;
+        }
+    }
+
+go_on:
+
+    pa_assert(u->source || u->sink);
+
+    pa_memchunk_reset(&u->memchunk);
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    /* Read mixer settings */
+    if (u->sink) {
+        if (sink_new_data.volume_is_set) {
+            if (u->sink->set_volume)
+                u->sink->set_volume(u->sink);
+        } else {
+            if (u->sink->get_volume)
+                u->sink->get_volume(u->sink);
+        }
+    }
+
+    if (u->source) {
+        if (source_new_data.volume_is_set) {
+            if (u->source->set_volume)
+                u->source->set_volume(u->source);
+        } else {
+            if (u->source->get_volume)
+                u->source->get_volume(u->source);
+        }
+    }
+
+    if (u->sink)
+        pa_sink_put(u->sink);
+    if (u->source)
+        pa_source_put(u->source);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+
+    if (u)
+        pa__done(m);
+    else if (fd >= 0)
+        pa_close(fd);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->out_mmap_memblocks) {
+        unsigned i;
+        for (i = 0; i < u->out_nfrags; i++)
+            if (u->out_mmap_memblocks[i])
+                pa_memblock_unref_fixed(u->out_mmap_memblocks[i]);
+        pa_xfree(u->out_mmap_memblocks);
+    }
+
+    if (u->in_mmap_memblocks) {
+        unsigned i;
+        for (i = 0; i < u->in_nfrags; i++)
+            if (u->in_mmap_memblocks[i])
+                pa_memblock_unref_fixed(u->in_mmap_memblocks[i]);
+        pa_xfree(u->in_mmap_memblocks);
+    }
+
+    if (u->in_mmap && u->in_mmap != MAP_FAILED)
+        munmap(u->in_mmap, u->in_hwbuf_size);
+
+    if (u->out_mmap && u->out_mmap != MAP_FAILED)
+        munmap(u->out_mmap, u->out_hwbuf_size);
+
+    if (u->fd >= 0)
+        pa_close(u->fd);
+
+    if (u->mixer_fd >= 0)
+        pa_close(u->mixer_fd);
+
+    pa_xfree(u->device_name);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
new file mode 100644 (file)
index 0000000..cd25b89
--- /dev/null
@@ -0,0 +1,364 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <poll.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+
+#include "module-pipe-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("UNIX pipe sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "file=<path of the FIFO> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate>"
+        "channel_map=<channel map>");
+
+#define DEFAULT_FILE_NAME "fifo_output"
+#define DEFAULT_SINK_NAME "fifo_output"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    char *filename;
+    int fd;
+
+    pa_memchunk memchunk;
+
+    pa_rtpoll_item *rtpoll_item;
+
+    int write_type;
+};
+
+static const char* const valid_modargs[] = {
+    "file",
+    "rate",
+    "format",
+    "channels",
+    "sink_name",
+    "channel_map",
+    NULL
+};
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            size_t n = 0;
+            int l;
+
+#ifdef TIOCINQ
+            if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0)
+                n = (size_t) l;
+#endif
+
+            n += u->memchunk.length;
+
+            *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec);
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void process_rewind(struct userdata *u) {
+    pa_assert(u);
+
+    pa_log_debug("Rewind requested but not supported by pipe sink. Ignoring.");
+    u->sink->thread_info.rewind_nbytes = 0;
+}
+
+static int process_render(struct userdata *u) {
+    pa_assert(u);
+
+    if (u->memchunk.length <= 0)
+        pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
+
+    pa_assert(u->memchunk.length > 0);
+
+    for (;;) {
+        ssize_t l;
+        void *p;
+
+        p = pa_memblock_acquire(u->memchunk.memblock);
+        l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &u->write_type);
+        pa_memblock_release(u->memchunk.memblock);
+
+        pa_assert(l != 0);
+
+        if (l < 0) {
+
+            if (errno == EINTR)
+                continue;
+            else if (errno != EAGAIN) {
+                pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+                return -1;
+            }
+
+        } else {
+
+            u->memchunk.index += l;
+            u->memchunk.length -= l;
+
+            if (u->memchunk.length <= 0) {
+                pa_memblock_unref(u->memchunk.memblock);
+                pa_memchunk_reset(&u->memchunk);
+            }
+        }
+
+        return 0;
+    }
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    for (;;) {
+        struct pollfd *pollfd;
+        int ret;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        /* Render some data and write it to the fifo */
+        if (u->sink->thread_info.state == PA_SINK_RUNNING) {
+
+            if (u->sink->thread_info.rewind_nbytes > 0)
+                process_rewind(u);
+
+            if (pollfd->revents) {
+                if (process_render(u) < 0)
+                    goto fail;
+
+                pollfd->revents = 0;
+            }
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        pollfd->events = u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0;
+
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        if (pollfd->revents & ~POLLOUT) {
+            pa_log("FIFO shutdown.");
+            goto fail;
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    struct stat st;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    struct pollfd *pollfd;
+    pa_sink_new_data data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    pa_memchunk_reset(&u->memchunk);
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+    u->write_type = 0;
+
+    u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
+
+    mkfifo(u->filename, 0666);
+    if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
+        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_make_fd_cloexec(u->fd);
+    pa_make_fd_nonblock(u->fd);
+
+    if (fstat(u->fd, &st) < 0) {
+        pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (!S_ISFIFO(st.st_mode)) {
+        pa_log("'%s' is not a FIFO.", u->filename);
+        goto fail;
+    }
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO sink %s", u->filename);
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = u->fd;
+    pollfd->events = pollfd->revents = 0;
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_sink_put(u->sink);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->memchunk.memblock)
+       pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->filename) {
+        unlink(u->filename);
+        pa_xfree(u->filename);
+    }
+
+    if (u->fd >= 0)
+        pa_assert_se(pa_close(u->fd) == 0);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
new file mode 100644 (file)
index 0000000..b0de34c
--- /dev/null
@@ -0,0 +1,315 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/poll.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+
+#include "module-pipe-source-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("UNIX pipe source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "source_name=<name for the source> "
+        "file=<path of the FIFO> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "channel_map=<channel map>");
+
+#define DEFAULT_FILE_NAME "/tmp/music.input"
+#define DEFAULT_SOURCE_NAME "fifo_input"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    char *filename;
+    int fd;
+
+    pa_memchunk memchunk;
+
+    pa_rtpoll_item *rtpoll_item;
+};
+
+static const char* const valid_modargs[] = {
+    "file",
+    "rate",
+    "channels",
+    "format",
+    "source_name",
+    "channel_map",
+    NULL
+};
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    int read_type = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    for (;;) {
+        int ret;
+        struct pollfd *pollfd;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        /* Try to read some data and pass it on to the source driver */
+        if (u->source->thread_info.state == PA_SOURCE_RUNNING && pollfd->revents) {
+            ssize_t l;
+            void *p;
+
+            if (!u->memchunk.memblock) {
+                u->memchunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF);
+                u->memchunk.index = u->memchunk.length = 0;
+            }
+
+            pa_assert(pa_memblock_get_length(u->memchunk.memblock) > u->memchunk.index);
+
+            p = pa_memblock_acquire(u->memchunk.memblock);
+            l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type);
+            pa_memblock_release(u->memchunk.memblock);
+
+            pa_assert(l != 0); /* EOF cannot happen, since we opened the fifo for both reading and writing */
+
+            if (l < 0) {
+
+                if (errno == EINTR)
+                    continue;
+                else if (errno != EAGAIN) {
+                    pa_log("Faile to read data from FIFO: %s", pa_cstrerror(errno));
+                    goto fail;
+                }
+
+            } else {
+
+                u->memchunk.length = l;
+                pa_source_post(u->source, &u->memchunk);
+                u->memchunk.index += l;
+
+                if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) {
+                    pa_memblock_unref(u->memchunk.memblock);
+                    pa_memchunk_reset(&u->memchunk);
+                }
+
+                pollfd->revents = 0;
+            }
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        pollfd->events = u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0;
+
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        if (pollfd->revents & ~POLLIN) {
+            pa_log("FIFO shutdown.");
+            goto fail;
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    struct stat st;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    struct pollfd *pollfd;
+    pa_source_new_data data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    pa_memchunk_reset(&u->memchunk);
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+    u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
+
+    mkfifo(u->filename, 0666);
+    if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) {
+        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_make_fd_cloexec(u->fd);
+    pa_make_fd_nonblock(u->fd);
+
+    if (fstat(u->fd, &st) < 0) {
+        pa_log("fstat('%s'): %s",u->filename, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (!S_ISFIFO(st.st_mode)) {
+        pa_log("'%s' is not a FIFO.", u->filename);
+        goto fail;
+    }
+
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO source %s", u->filename);
+    pa_source_new_data_set_sample_spec(&data, &ss);
+    pa_source_new_data_set_channel_map(&data, &map);
+
+    u->source = pa_source_new(m->core, &data, 0);
+    pa_source_new_data_done(&data);
+
+    if (!u->source) {
+        pa_log("Failed to create source.");
+        goto fail;
+    }
+
+    u->source->userdata = u;
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = u->fd;
+    pollfd->events = pollfd->revents = 0;
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_source_put(u->source);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->filename) {
+        unlink(u->filename);
+        pa_xfree(u->filename);
+    }
+
+    if (u->fd >= 0)
+        pa_assert_se(pa_close(u->fd) == 0);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
new file mode 100644 (file)
index 0000000..90e693a
--- /dev/null
@@ -0,0 +1,165 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/channelmap.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/sink-input.h>
+
+#include "module-position-event-sounds-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Position event sounds between L and R depending on the position on screen of the widget triggering them.");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_hook_slot *sink_input_fixate_hook_slot;
+};
+
+static pa_bool_t is_left(pa_channel_position_t p) {
+    return
+        p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_REAR_LEFT ||
+        p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+}
+
+static pa_bool_t is_right(pa_channel_position_t p) {
+    return
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_REAR_RIGHT||
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+}
+
+static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) {
+    const char *hpos;
+    double f;
+    unsigned c;
+    char t[PA_CVOLUME_SNPRINT_MAX];
+
+    pa_assert(data);
+
+    if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS)))
+        return PA_HOOK_OK;
+
+    if (pa_atod(hpos, &f) < 0) {
+        pa_log_warn("Failed to parse "PA_PROP_EVENT_MOUSE_HPOS" property '%s'.", hpos);
+        return PA_HOOK_OK;
+    }
+
+    if (f < 0.0 || f > 1.0) {
+        pa_log_warn("Property "PA_PROP_EVENT_MOUSE_HPOS" out of range %0.2f", f);
+        return PA_HOOK_OK;
+    }
+
+    pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f);
+
+    if (!data->volume_is_set) {
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+        data->volume_is_set = TRUE;
+    }
+
+    for (c = 0; c < data->sample_spec.channels; c++) {
+
+        if (is_left(data->channel_map.map[c]))
+            data->volume.values[c] =
+                pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f)));
+
+        if (is_right(data->channel_map.map[c]))
+            data->volume.values[c] =
+                pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f));
+    }
+
+    pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume));
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return  -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink_input_fixate_hook_slot)
+        pa_hook_slot_free(u->sink_input_fixate_hook_slot);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c
new file mode 100644 (file)
index 0000000..0c9529c
--- /dev/null
@@ -0,0 +1,367 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <unistd.h>
+#include <limits.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/winsock.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/socket-server.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/creds.h>
+
+#ifdef USE_TCP_SOCKETS
+#define SOCKET_DESCRIPTION "(TCP sockets)"
+#define SOCKET_USAGE "port=<TCP port number> listen=<address to listen on>"
+#else
+#define SOCKET_DESCRIPTION "(UNIX sockets)"
+#define SOCKET_USAGE "socket=<path to UNIX socket>"
+#endif
+
+#if defined(USE_PROTOCOL_SIMPLE)
+  #include <pulsecore/protocol-simple.h>
+  #define protocol_new pa_protocol_simple_new
+  #define protocol_free pa_protocol_simple_free
+  #define TCPWRAP_SERVICE "pulseaudio-simple"
+  #define IPV4_PORT 4711
+  #define UNIX_SOCKET "simple"
+  #define MODULE_ARGUMENTS "rate", "format", "channels", "sink", "source", "playback", "record",
+  #if defined(USE_TCP_SOCKETS)
+    #include "module-simple-protocol-tcp-symdef.h"
+  #else
+    #include "module-simple-protocol-unix-symdef.h"
+  #endif
+PA_MODULE_DESCRIPTION("Simple protocol "SOCKET_DESCRIPTION);
+  PA_MODULE_USAGE("rate=<sample rate> "
+                  "format=<sample format> "
+                  "channels=<number of channels> "
+                  "sink=<sink to connect to> "
+                  "source=<source to connect to> "
+                  "playback=<enable playback?> "
+                  "record=<enable record?> "
+                  SOCKET_USAGE);
+#elif defined(USE_PROTOCOL_CLI)
+  #include <pulsecore/protocol-cli.h>
+  #define protocol_new pa_protocol_cli_new
+  #define protocol_free pa_protocol_cli_free
+  #define TCPWRAP_SERVICE "pulseaudio-cli"
+  #define IPV4_PORT 4712
+  #define UNIX_SOCKET "cli"
+  #define MODULE_ARGUMENTS
+  #ifdef USE_TCP_SOCKETS
+    #include "module-cli-protocol-tcp-symdef.h"
+  #else
+    #include "module-cli-protocol-unix-symdef.h"
+  #endif
+  PA_MODULE_DESCRIPTION("Command line interface protocol "SOCKET_DESCRIPTION);
+  PA_MODULE_USAGE(SOCKET_USAGE);
+#elif defined(USE_PROTOCOL_HTTP)
+  #include <pulsecore/protocol-http.h>
+  #define protocol_new pa_protocol_http_new
+  #define protocol_free pa_protocol_http_free
+  #define TCPWRAP_SERVICE "pulseaudio-http"
+  #define IPV4_PORT 4714
+  #define UNIX_SOCKET "http"
+  #define MODULE_ARGUMENTS
+  #ifdef USE_TCP_SOCKETS
+    #include "module-http-protocol-tcp-symdef.h"
+  #else
+    #include "module-http-protocol-unix-symdef.h"
+  #endif
+  PA_MODULE_DESCRIPTION("HTTP "SOCKET_DESCRIPTION);
+  PA_MODULE_USAGE(SOCKET_USAGE);
+#elif defined(USE_PROTOCOL_NATIVE)
+  #include <pulsecore/protocol-native.h>
+  #define protocol_new pa_protocol_native_new
+  #define protocol_free pa_protocol_native_free
+  #define TCPWRAP_SERVICE "pulseaudio-native"
+  #define IPV4_PORT PA_NATIVE_DEFAULT_PORT
+  #define UNIX_SOCKET PA_NATIVE_DEFAULT_UNIX_SOCKET
+  #define MODULE_ARGUMENTS_COMMON "cookie", "auth-anonymous",
+  #ifdef USE_TCP_SOCKETS
+    #include "module-native-protocol-tcp-symdef.h"
+  #else
+    #include "module-native-protocol-unix-symdef.h"
+  #endif
+
+  #if defined(HAVE_CREDS) && !defined(USE_TCP_SOCKETS)
+    #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-group", "auth-group-enable",
+    #define AUTH_USAGE "auth-group=<system group to allow access> auth-group-enable=<enable auth by UNIX group?> "
+  #elif defined(USE_TCP_SOCKETS)
+    #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-ip-acl",
+    #define AUTH_USAGE "auth-ip-acl=<IP address ACL to allow access> "
+  #else
+    #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON
+    #define AUTH_USAGE
+  #endif
+
+  PA_MODULE_DESCRIPTION("Native protocol "SOCKET_DESCRIPTION);
+  PA_MODULE_USAGE("auth-anonymous=<don't check for cookies?> "
+                  "cookie=<path to cookie file> "
+                  AUTH_USAGE
+                  SOCKET_USAGE);
+#elif defined(USE_PROTOCOL_ESOUND)
+  #include <pulsecore/protocol-esound.h>
+  #include <pulsecore/esound.h>
+  #define protocol_new pa_protocol_esound_new
+  #define protocol_free pa_protocol_esound_free
+  #define TCPWRAP_SERVICE "esound"
+  #define IPV4_PORT ESD_DEFAULT_PORT
+  #define MODULE_ARGUMENTS_COMMON "sink", "source", "auth-anonymous", "cookie",
+  #ifdef USE_TCP_SOCKETS
+    #include "module-esound-protocol-tcp-symdef.h"
+  #else
+    #include "module-esound-protocol-unix-symdef.h"
+  #endif
+
+  #if defined(USE_TCP_SOCKETS)
+    #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON "auth-ip-acl",
+    #define AUTH_USAGE "auth-ip-acl=<IP address ACL to allow access> "
+  #else
+    #define MODULE_ARGUMENTS MODULE_ARGUMENTS_COMMON
+    #define AUTH_USAGE
+  #endif
+
+  PA_MODULE_DESCRIPTION("ESOUND protocol "SOCKET_DESCRIPTION);
+  PA_MODULE_USAGE("sink=<sink to connect to> "
+                  "source=<source to connect to> "
+                  "auth-anonymous=<don't verify cookies?> "
+                  "cookie=<path to cookie file> "
+                  AUTH_USAGE
+                  SOCKET_USAGE);
+#else
+  #error "Broken build system"
+#endif
+
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+
+static const char* const valid_modargs[] = {
+    MODULE_ARGUMENTS
+#if defined(USE_TCP_SOCKETS)
+    "port",
+    "listen",
+#else
+    "socket",
+#endif
+    NULL
+};
+
+struct userdata {
+#if defined(USE_TCP_SOCKETS)
+    void *protocol_ipv4;
+    void *protocol_ipv6;
+#else
+    void *protocol_unix;
+    char *socket_path;
+#endif
+};
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    int ret = -1;
+    struct userdata *u = NULL;
+
+#if defined(USE_TCP_SOCKETS)
+    pa_socket_server *s_ipv4 = NULL, *s_ipv6 = NULL;
+    uint32_t port = IPV4_PORT;
+    const char *listen_on;
+#else
+    pa_socket_server *s;
+    int r;
+#endif
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto finish;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+
+#if defined(USE_TCP_SOCKETS)
+    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {
+        pa_log("port= expects a numerical argument between 1 and 65535.");
+        goto fail;
+    }
+
+    listen_on = pa_modargs_get_value(ma, "listen", NULL);
+
+    if (listen_on) {
+        s_ipv6 = pa_socket_server_new_ipv6_string(m->core->mainloop, listen_on, port, TCPWRAP_SERVICE);
+        s_ipv4 = pa_socket_server_new_ipv4_string(m->core->mainloop, listen_on, port, TCPWRAP_SERVICE);
+    } else {
+        s_ipv6 = pa_socket_server_new_ipv6_any(m->core->mainloop, port, TCPWRAP_SERVICE);
+        s_ipv4 = pa_socket_server_new_ipv4_any(m->core->mainloop, port, TCPWRAP_SERVICE);
+    }
+
+    if (!s_ipv4 && !s_ipv6)
+        goto fail;
+
+    if (s_ipv4)
+        u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma);
+    if (s_ipv6)
+        u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma);
+
+    if (!u->protocol_ipv4 && !u->protocol_ipv6)
+        goto fail;
+
+    if (s_ipv6)
+        pa_socket_server_unref(s_ipv6);
+    if (s_ipv6)
+        pa_socket_server_unref(s_ipv4);
+
+#else
+
+#if defined(USE_PROTOCOL_ESOUND)
+
+#if defined(USE_PER_USER_ESOUND_SOCKET)
+    u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
+#else
+    u->socket_path = pa_xstrdup("/tmp/.esd/socket");
+#endif
+
+    /* This socket doesn't reside in our own runtime dir but in
+     * /tmp/.esd/, hence we have to create the dir first */
+
+    if (pa_make_secure_parent_dir(u->socket_path, pa_in_system_mode() ? 0755 : 0700, (uid_t)-1, (gid_t)-1) < 0) {
+        pa_log("Failed to create socket directory '%s': %s\n", u->socket_path, pa_cstrerror(errno));
+        goto fail;
+    }
+
+#else
+    if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
+        pa_log("Failed to generate socket path.");
+        goto fail;
+    }
+#endif
+
+    if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
+        pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno));
+        goto fail;
+    } else if (r > 0)
+        pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path);
+
+    if (!(s = pa_socket_server_new_unix(m->core->mainloop, u->socket_path)))
+        goto fail;
+
+    if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))
+        goto fail;
+
+    pa_socket_server_unref(s);
+
+#endif
+
+    m->userdata = u;
+
+    ret = 0;
+
+finish:
+    if (ma)
+        pa_modargs_free(ma);
+
+    return ret;
+
+fail:
+    if (u) {
+#if defined(USE_TCP_SOCKETS)
+        if (u->protocol_ipv4)
+            protocol_free(u->protocol_ipv4);
+        if (u->protocol_ipv6)
+            protocol_free(u->protocol_ipv6);
+#else
+        if (u->protocol_unix)
+            protocol_free(u->protocol_unix);
+        pa_xfree(u->socket_path);
+#endif
+
+        pa_xfree(u);
+    }
+
+#if defined(USE_TCP_SOCKETS)
+    if (s_ipv4)
+        pa_socket_server_unref(s_ipv4);
+    if (s_ipv6)
+        pa_socket_server_unref(s_ipv6);
+#else
+    if (s)
+        pa_socket_server_unref(s);
+#endif
+
+    goto finish;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    u = m->userdata;
+
+#if defined(USE_TCP_SOCKETS)
+    if (u->protocol_ipv4)
+        protocol_free(u->protocol_ipv4);
+    if (u->protocol_ipv6)
+        protocol_free(u->protocol_ipv6);
+#else
+    if (u->protocol_unix)
+        protocol_free(u->protocol_unix);
+
+#if defined(USE_PROTOCOL_ESOUND) && !defined(USE_PER_USER_ESOUND_SOCKET)
+    if (u->socket_path) {
+        char *p = pa_parent_dir(u->socket_path);
+        rmdir(p);
+        pa_xfree(p);
+    }
+#endif
+
+    pa_xfree(u->socket_path);
+#endif
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
new file mode 100644 (file)
index 0000000..c87b1ec
--- /dev/null
@@ -0,0 +1,429 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+
+#include "module-remap-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Virtual channel remapping sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "sink_name=<name for the sink> "
+        "master=<name of sink to remap> "
+        "master_channel_map=<channel map> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "channel_map=<channel map> "
+        "remix=<remix channels?>");
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_sink *sink, *master;
+    pa_sink_input *sink_input;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "master",
+    "master_channel_map",
+    "rate",
+    "format",
+    "channels",
+    "channel_map",
+    "remix",
+    NULL
+};
+
+/* Called from I/O thread context */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t usec = 0;
+
+            /* Get the latency of the master sink */
+            if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+                usec = 0;
+
+            /* Add the latency internal to our sink input on top */
+            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+
+            *((pa_usec_t*) data) = usec;
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (PA_SINK_IS_LINKED(state) &&
+        u->sink_input &&
+        PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+
+        pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_request_rewind(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_set_requested_latency_within_thread(
+            u->sink_input,
+            pa_sink_get_requested_latency_within_thread(s));
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+        return -1;
+
+    pa_sink_render(u->sink, nbytes, chunk);
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+    pa_assert(nbytes > 0);
+
+    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+        return;
+
+    if (u->sink->thread_info.rewind_nbytes > 0) {
+        size_t amount;
+
+        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes);
+        u->sink->thread_info.rewind_nbytes = 0;
+
+        if (amount > 0)
+            pa_sink_process_rewind(u->sink, amount);
+    }
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_set_max_rewind(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_set_max_request(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_update_latency_range(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_detach_within_thread(u->sink);
+    pa_sink_set_asyncmsgq(u->sink, NULL);
+    pa_sink_set_rtpoll(u->sink, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
+    pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
+    pa_sink_attach_within_thread(u->sink);
+
+    pa_sink_update_latency_range(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_unlink(u->sink);
+    pa_sink_input_unlink(u->sink_input);
+
+    pa_sink_unref(u->sink);
+    u->sink = NULL;
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    pa_module_unload_request(u->module);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT) {
+        pa_log_debug("Requesting rewind due to state change.");
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+    }
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec ss;
+    pa_channel_map sink_map, stream_map;
+    pa_modargs *ma;
+    const char *k;
+    pa_sink *master;
+    pa_sink_input_new_data sink_input_data;
+    pa_sink_new_data sink_data;
+    pa_bool_t remix = TRUE;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
+        pa_log("Master sink not found");
+        goto fail;
+    }
+
+    ss = master->sample_spec;
+    sink_map = master->channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &sink_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    stream_map = sink_map;
+    if (pa_modargs_get_channel_map(ma, "master_channel_map", &stream_map) < 0) {
+        pa_log("Invalid master channel map");
+        goto fail;
+    }
+
+    if (stream_map.channels != ss.channels) {
+        pa_log("Number of channels doesn't match");
+        goto fail;
+    }
+
+    if (pa_channel_map_equal(&stream_map, &master->channel_map))
+        pa_log_warn("No remapping configured, proceeding nonetheless!");
+
+    if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
+        pa_log("Invalid boolean remix parameter");
+        goto fail;
+    }
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->master = master;
+    u->sink = NULL;
+    u->sink_input = NULL;
+
+    /* Create sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+    if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        sink_data.name = pa_sprintf_malloc("%s.remapped", master->name);
+    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &sink_map);
+    k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+    pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+
+    u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY);
+    pa_sink_new_data_done(&sink_data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->set_state = sink_set_state;
+    u->sink->update_requested_latency = sink_update_requested_latency;
+    u->sink->request_rewind = sink_request_rewind;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+    pa_sink_set_rtpoll(u->sink, master->rtpoll);
+
+    /* Create sink input */
+    pa_sink_input_new_data_init(&sink_input_data);
+    sink_input_data.driver = __FILE__;
+    sink_input_data.module = m;
+    sink_input_data.sink = u->master;
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map);
+
+    u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX));
+    pa_sink_input_new_data_done(&sink_input_data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->update_max_request = sink_input_update_max_request_cb;
+    u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    u->sink_input->attach = sink_input_attach_cb;
+    u->sink_input->detach = sink_input_detach_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->userdata = u;
+
+    pa_sink_put(u->sink);
+    pa_sink_input_put(u->sink_input);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink) {
+        pa_sink_unlink(u->sink);
+        pa_sink_unref(u->sink);
+    }
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+    }
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
new file mode 100644 (file)
index 0000000..cc6717c
--- /dev/null
@@ -0,0 +1,162 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+
+#include "module-rescue-streams-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move their streams to the default sink/source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+static const char* const valid_modargs[] = {
+    NULL,
+};
+
+struct userdata {
+    pa_hook_slot *sink_slot, *source_slot;
+};
+
+static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+    pa_sink_input *i;
+    pa_sink *target;
+
+    pa_assert(c);
+    pa_assert(sink);
+
+    if (!pa_idxset_size(sink->inputs)) {
+        pa_log_debug("No sink inputs to move away.");
+        return PA_HOOK_OK;
+    }
+
+    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0)) || target == sink) {
+        uint32_t idx;
+
+        for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx))
+            if (target != sink)
+                break;
+
+        if (!target) {
+            pa_log_info("No evacuation sink found.");
+            return PA_HOOK_OK;
+        }
+    }
+
+    while ((i = pa_idxset_first(sink->inputs, NULL))) {
+        if (pa_sink_input_move_to(i, target) < 0) {
+            pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
+            return PA_HOOK_OK;
+        }
+
+        pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
+    }
+
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void* userdata) {
+    pa_source_output *o;
+    pa_source *target;
+
+    pa_assert(c);
+    pa_assert(source);
+
+    if (!pa_idxset_size(source->outputs)) {
+        pa_log_debug("No source outputs to move away.");
+        return PA_HOOK_OK;
+    }
+
+    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0)) || target == source) {
+        uint32_t idx;
+
+        for (target = pa_idxset_first(c->sources, &idx); target; target = pa_idxset_next(c->sources, &idx))
+            if (target != source && !target->monitor_of == !source->monitor_of)
+                break;
+
+        if (!target) {
+            pa_log_info("No evacuation source found.");
+            return PA_HOOK_OK;
+        }
+    }
+
+    pa_assert(target != source);
+
+    while ((o = pa_idxset_first(source->outputs, NULL))) {
+        if (pa_source_output_move_to(o, target) < 0) {
+            pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
+            return PA_HOOK_OK;
+        }
+
+        pa_log_info("Sucessfully moved source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
+    }
+
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        return -1;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_hook_callback, NULL);
+    u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_hook_callback, NULL);
+
+    pa_modargs_free(ma);
+    return 0;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!m->userdata)
+        return;
+
+    u = m->userdata;
+    if (u->sink_slot)
+        pa_hook_slot_free(u->sink_slot);
+    if (u->source_slot)
+        pa_hook_slot_free(u->source_slot);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
new file mode 100644 (file)
index 0000000..38780f2
--- /dev/null
@@ -0,0 +1,220 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <math.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#include "module-sine-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Sine wave generator");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("sink=<sink to connect to> frequency=<frequency in Hz>");
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink_input *sink_input;
+    pa_memblock *memblock;
+    size_t peek_index;
+};
+
+static const char* const valid_modargs[] = {
+    "sink",
+    "frequency",
+    NULL,
+};
+
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+    pa_assert(chunk);
+
+    chunk->memblock = pa_memblock_ref(u->memblock);
+    chunk->length = pa_memblock_get_length(u->memblock) - u->peek_index;
+    chunk->index = u->peek_index;
+
+    u->peek_index = 0;
+
+    return 0;
+}
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    size_t l;
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    l = pa_memblock_get_length(u->memblock);
+    nbytes %= l;
+
+    if (u->peek_index >= nbytes)
+        u->peek_index -= nbytes;
+    else
+        u->peek_index = l + u->peek_index - nbytes;
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_input_unlink(u->sink_input);
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    pa_module_unload_request(u->module);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT)
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
+static void calc_sine(float *f, size_t l, float freq) {
+    size_t i;
+
+    l /= sizeof(float);
+
+    for (i = 0; i < l; i++)
+        f[i] = (float) sin((double) i/l*M_PI*2*freq)/2;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    pa_sink *sink;
+    pa_sample_spec ss;
+    uint32_t frequency;
+    void *p;
+    pa_sink_input_new_data data;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->sink_input = NULL;
+    u->memblock = NULL;
+    u->peek_index = 0;
+
+    if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK, 1))) {
+        pa_log("No such sink.");
+        goto fail;
+    }
+
+    ss.format = PA_SAMPLE_FLOAT32;
+    ss.rate = sink->sample_spec.rate;
+    ss.channels = 1;
+
+    frequency = 440;
+    if (pa_modargs_get_value_u32(ma, "frequency", &frequency) < 0 || frequency < 1 || frequency > ss.rate/2) {
+        pa_log("Invalid frequency specification");
+        goto fail;
+    }
+
+    u->memblock = pa_memblock_new(m->core->mempool, pa_bytes_per_second(&ss));
+    p = pa_memblock_acquire(u->memblock);
+    calc_sine(p, pa_memblock_get_length(u->memblock), frequency);
+    pa_memblock_release(u->memblock);
+
+    pa_sink_input_new_data_init(&data);
+    data.sink = sink;
+    data.driver = __FILE__;
+    pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency);
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
+    pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
+    pa_sink_input_new_data_set_sample_spec(&data, &ss);
+    data.module = m;
+
+    u->sink_input = pa_sink_input_new(m->core, &data, 0);
+    pa_sink_input_new_data_done(&data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->userdata = u;
+
+    pa_sink_input_put(u->sink_input);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+    }
+
+    if (u->memblock)
+        pa_memblock_unref(u->memblock);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
new file mode 100644 (file)
index 0000000..6f50543
--- /dev/null
@@ -0,0 +1,764 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <signal.h>
+#include <stropts.h>
+#include <sys/conf.h>
+#include <sys/audio.h>
+
+#include <pulse/error.h>
+#include <pulse/mainloop-signal.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/iochannel.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/thread.h>
+
+#include "module-solaris-symdef.h"
+
+PA_MODULE_AUTHOR("Pierre Ossman")
+PA_MODULE_DESCRIPTION("Solaris Sink/Source")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE(
+    "sink_name=<name for the sink> "
+    "source_name=<name for the source> "
+    "device=<OSS device> record=<enable source?> "
+    "playback=<enable sink?> "
+    "format=<sample format> "
+    "channels=<number of channels> "
+    "rate=<sample rate> "
+    "buffer_size=<record buffer size> "
+    "channel_map=<channel map>")
+
+struct userdata {
+    pa_core *core;
+    pa_sink *sink;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+
+    pa_signal_event *sig;
+
+    pa_memchunk memchunk;
+
+    unsigned int page_size;
+
+    uint32_t frame_size;
+    uint32_t buffer_size;
+    unsigned int written_bytes, read_bytes;
+
+    int fd;
+    pa_rtpoll_item *rtpoll_item;
+    pa_module *module;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "source_name",
+    "device",
+    "record",
+    "playback",
+    "buffer_size",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    NULL
+};
+
+#define DEFAULT_SINK_NAME "solaris_output"
+#define DEFAULT_SOURCE_NAME "solaris_input"
+#define DEFAULT_DEVICE "/dev/audio"
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+    int err;
+    audio_info_t info;
+
+    switch (code) {
+    case PA_SINK_MESSAGE_GET_LATENCY: {
+        pa_usec_t r = 0;
+
+        if (u->fd >= 0) {
+
+            err = ioctl(u->fd, AUDIO_GETINFO, &info);
+            pa_assert(err >= 0);
+
+            r += pa_bytes_to_usec(u->written_bytes, &PA_SINK(o)->sample_spec);
+            r -= pa_bytes_to_usec(info.play.samples * u->frame_size, &PA_SINK(o)->sample_spec);
+
+            if (u->memchunk.memblock)
+                r += pa_bytes_to_usec(u->memchunk.length, &PA_SINK(o)->sample_spec);
+        }
+
+        *((pa_usec_t*) data) = r;
+
+        return 0;
+    }
+
+    case PA_SINK_MESSAGE_SET_VOLUME:
+        if (u->fd >= 0) {
+            AUDIO_INITINFO(&info);
+
+            info.play.gain = pa_cvolume_avg((pa_cvolume*)data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+            assert(info.play.gain <= AUDIO_MAX_GAIN);
+
+            if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
+                if (errno == EINVAL)
+                    pa_log("AUDIO_SETINFO: Unsupported volume.");
+                else
+                    pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+            } else {
+                return 0;
+            }
+        }
+        break;
+
+    case PA_SINK_MESSAGE_GET_VOLUME:
+        if (u->fd >= 0) {
+            err = ioctl(u->fd, AUDIO_GETINFO, &info);
+            assert(err >= 0);
+
+            pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
+                info.play.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+
+            return 0;
+        }
+        break;
+
+    case PA_SINK_MESSAGE_SET_MUTE:
+        if (u->fd >= 0) {
+            AUDIO_INITINFO(&info);
+
+            info.output_muted = !!PA_PTR_TO_UINT(data);
+
+            if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+                pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+            else
+                return 0;
+        }
+        break;
+
+    case PA_SINK_MESSAGE_GET_MUTE:
+        if (u->fd >= 0) {
+            err = ioctl(u->fd, AUDIO_GETINFO, &info);
+            pa_assert(err >= 0);
+
+            *(int*)data = !!info.output_muted;
+
+            return 0;
+        }
+        break;
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+    int err;
+    audio_info_t info;
+
+    switch (code) {
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            pa_usec_t r = 0;
+
+            if (u->fd) {
+                err = ioctl(u->fd, AUDIO_GETINFO, &info);
+                pa_assert(err >= 0);
+
+                r += pa_bytes_to_usec(info.record.samples * u->frame_size, &PA_SOURCE(o)->sample_spec);
+                r -= pa_bytes_to_usec(u->read_bytes, &PA_SOURCE(o)->sample_spec);
+            }
+
+            *((pa_usec_t*) data) = r;
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SET_VOLUME:
+            if (u->fd >= 0) {
+                AUDIO_INITINFO(&info);
+
+                info.record.gain = pa_cvolume_avg((pa_cvolume*) data) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;
+                assert(info.record.gain <= AUDIO_MAX_GAIN);
+
+                if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) {
+                    if (errno == EINVAL)
+                        pa_log("AUDIO_SETINFO: Unsupported volume.");
+                    else
+                        pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+                } else {
+                    return 0;
+                }
+            }
+            break;
+
+        case PA_SOURCE_MESSAGE_GET_VOLUME:
+            if (u->fd >= 0) {
+                err = ioctl(u->fd, AUDIO_GETINFO, &info);
+                pa_assert(err >= 0);
+
+                pa_cvolume_set((pa_cvolume*) data, ((pa_cvolume*) data)->channels,
+                    info.record.gain * PA_VOLUME_NORM / AUDIO_MAX_GAIN);
+
+                return 0;
+            }
+            break;
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static void clear_underflow(struct userdata *u)
+{
+    audio_info_t info;
+
+    AUDIO_INITINFO(&info);
+
+    info.play.error = 0;
+
+    if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+        pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+}
+
+static void clear_overflow(struct userdata *u)
+{
+    audio_info_t info;
+
+    AUDIO_INITINFO(&info);
+
+    info.record.error = 0;
+
+    if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
+        pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+    unsigned short revents = 0;
+    int ret;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->high_priority)
+        pa_make_realtime();
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    for (;;) {
+        /* Render some data and write it to the dsp */
+
+        if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) {
+            audio_info_t info;
+            int err;
+            size_t len;
+
+            err = ioctl(u->fd, AUDIO_GETINFO, &info);
+            pa_assert(err >= 0);
+
+            /*
+             * Since we cannot modify the size of the output buffer we fake it
+             * by not filling it more than u->buffer_size.
+             */
+            len = u->buffer_size;
+            len -= u->written_bytes - (info.play.samples * u->frame_size);
+
+            /* The sample counter can sometimes go backwards :( */
+            if (len > u->buffer_size)
+                len = 0;
+
+            if (info.play.error) {
+                pa_log_debug("Solaris buffer underflow!");
+                clear_underflow(u);
+            }
+
+            len -= len % u->frame_size;
+
+            while (len) {
+                void *p;
+                ssize_t r;
+
+                if (!u->memchunk.length)
+                    pa_sink_render(u->sink, len, &u->memchunk);
+
+                pa_assert(u->memchunk.length);
+
+                p = pa_memblock_acquire(u->memchunk.memblock);
+                r = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL);
+                pa_memblock_release(u->memchunk.memblock);
+
+                if (r < 0) {
+                    if (errno == EINTR)
+                        continue;
+                    else if (errno != EAGAIN) {
+                        pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
+                        goto fail;
+                    }
+                } else {
+                    pa_assert(r % u->frame_size == 0);
+
+                    u->memchunk.index += r;
+                    u->memchunk.length -= r;
+
+                    if (u->memchunk.length <= 0) {
+                        pa_memblock_unref(u->memchunk.memblock);
+                        pa_memchunk_reset(&u->memchunk);
+                    }
+
+                    len -= r;
+                    u->written_bytes += r;
+                }
+            }
+        }
+
+        /* Try to read some data and pass it on to the source driver */
+
+        if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN))) {
+            pa_memchunk memchunk;
+            int err;
+            size_t l;
+            void *p;
+            ssize_t r;
+            audio_info_t info;
+
+            err = ioctl(u->fd, AUDIO_GETINFO, &info);
+            pa_assert(err >= 0);
+
+            if (info.record.error) {
+                pa_log_debug("Solaris buffer overflow!");
+                clear_overflow(u);
+            }
+
+            err = ioctl(u->fd, I_NREAD, &l);
+            pa_assert(err >= 0);
+
+            if (l > 0) {
+                /* This is to make sure it fits in the memory pool. Also, a page
+                   should be the most efficient transfer size. */
+                if (l > u->page_size)
+                    l = u->page_size;
+
+                memchunk.memblock = pa_memblock_new(u->core->mempool, l);
+                pa_assert(memchunk.memblock);
+
+                p = pa_memblock_acquire(memchunk.memblock);
+                r = pa_read(u->fd, p, l, NULL);
+                pa_memblock_release(memchunk.memblock);
+
+                if (r < 0) {
+                    pa_memblock_unref(memchunk.memblock);
+                    if (errno != EAGAIN) {
+                        pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
+                        goto fail;
+                    }
+                } else {
+                    memchunk.index = 0;
+                    memchunk.length = r;
+
+                    pa_source_post(u->source, &memchunk);
+                    pa_memblock_unref(memchunk.memblock);
+
+                    u->read_bytes += r;
+
+                    revents &= ~POLLIN;
+                }
+            }
+        }
+
+        if (u->fd >= 0) {
+            struct pollfd *pollfd;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+            pollfd->events =
+                ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0);
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        if (u->fd >= 0) {
+            struct pollfd *pollfd;
+
+            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
+                pa_log("DSP shutdown.");
+                goto fail;
+            }
+
+            revents = pollfd->revents;
+        } else
+            revents = 0;
+    }
+
+fail:
+    /* We have to continue processing messages until we receive the
+     * SHUTDOWN message */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+static void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) {
+    struct userdata *u = userdata;
+
+    assert(u);
+
+    if (u->sink) {
+        pa_sink_get_volume(u->sink);
+        pa_sink_get_mute(u->sink);
+    }
+
+    if (u->source)
+        pa_source_get_volume(u->source);
+}
+
+static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) {
+    audio_info_t info;
+
+    AUDIO_INITINFO(&info);
+
+    if (mode != O_RDONLY) {
+        info.play.sample_rate = ss->rate;
+        info.play.channels = ss->channels;
+        switch (ss->format) {
+        case PA_SAMPLE_U8:
+            info.play.precision = 8;
+            info.play.encoding = AUDIO_ENCODING_LINEAR;
+            break;
+        case PA_SAMPLE_ALAW:
+            info.play.precision = 8;
+            info.play.encoding = AUDIO_ENCODING_ALAW;
+            break;
+        case PA_SAMPLE_ULAW:
+            info.play.precision = 8;
+            info.play.encoding = AUDIO_ENCODING_ULAW;
+            break;
+        case PA_SAMPLE_S16NE:
+            info.play.precision = 16;
+            info.play.encoding = AUDIO_ENCODING_LINEAR;
+            break;
+        default:
+            return -1;
+        }
+    }
+
+    if (mode != O_WRONLY) {
+        info.record.sample_rate = ss->rate;
+        info.record.channels = ss->channels;
+        switch (ss->format) {
+        case PA_SAMPLE_U8:
+            info.record.precision = 8;
+            info.record.encoding = AUDIO_ENCODING_LINEAR;
+            break;
+        case PA_SAMPLE_ALAW:
+            info.record.precision = 8;
+            info.record.encoding = AUDIO_ENCODING_ALAW;
+            break;
+        case PA_SAMPLE_ULAW:
+            info.record.precision = 8;
+            info.record.encoding = AUDIO_ENCODING_ULAW;
+            break;
+        case PA_SAMPLE_S16NE:
+            info.record.precision = 16;
+            info.record.encoding = AUDIO_ENCODING_LINEAR;
+            break;
+        default:
+            return -1;
+        }
+    }
+
+    if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
+        if (errno == EINVAL)
+            pa_log("AUDIO_SETINFO: Unsupported sample format.");
+        else
+            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_solaris_set_buffer(int fd, int buffer_size) {
+    audio_info_t info;
+
+    AUDIO_INITINFO(&info);
+
+    info.play.buffer_size = buffer_size;
+    info.record.buffer_size = buffer_size;
+
+    if (ioctl(fd, AUDIO_SETINFO, &info) < 0) {
+        if (errno == EINVAL)
+            pa_log("AUDIO_SETINFO: Unsupported buffer size.");
+        else
+            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = NULL;
+    const char *p;
+    int fd = -1;
+    int buffer_size;
+    int mode;
+    int record = 1, playback = 1;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    char *t;
+    struct pollfd *pollfd;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
+        pa_log("record= and playback= expect numeric argument.");
+        goto fail;
+    }
+
+    if (!playback && !record) {
+        pa_log("neither playback nor record enabled for device.");
+        goto fail;
+    }
+
+    mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));
+
+    buffer_size = 16384;
+    if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) {
+        pa_log("failed to parse buffer size argument");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("failed to parse sample specification");
+        goto fail;
+    }
+
+    if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0)
+        goto fail;
+
+    pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+
+    if (pa_solaris_auto_format(fd, mode, &ss) < 0)
+        goto fail;
+
+    if (pa_solaris_set_buffer(fd, buffer_size) < 0)
+        goto fail;
+
+    u = pa_xmalloc(sizeof(struct userdata));
+    u->core = m->core;
+
+    u->fd = fd;
+
+    pa_memchunk_reset(&u->memchunk);
+
+    /* We use this to get a reasonable chunk size */
+    u->page_size = PA_PAGE_SIZE;
+
+    u->frame_size = pa_frame_size(&ss);
+    u->buffer_size = buffer_size;
+
+    u->written_bytes = 0;
+    u->read_bytes = 0;
+
+    u->module = m;
+    m->userdata = u;
+
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop);
+
+    u->rtpoll = pa_rtpoll_new();
+    pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq);
+
+    pa_rtpoll_set_timer_periodic(u->rtpoll, pa_bytes_to_usec(u->buffer_size / 10, &ss));
+
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = fd;
+    pollfd->events = 0;
+    pollfd->revents = 0;
+
+    if (mode != O_WRONLY) {
+        u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map);
+        pa_assert(u->source);
+
+        u->source->userdata = u;
+        u->source->parent.process_msg = source_process_msg;
+
+        pa_source_set_module(u->source, m);
+        pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
+        pa_xfree(t);
+        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+        pa_source_set_rtpoll(u->source, u->rtpoll);
+
+        u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL;
+        u->source->refresh_volume = 1;
+    } else
+        u->source = NULL;
+
+    if (mode != O_RDONLY) {
+        u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map);
+        pa_assert(u->sink);
+
+        u->sink->userdata = u;
+        u->sink->parent.process_msg = sink_process_msg;
+
+        pa_sink_set_module(u->sink, m);
+        pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p));
+        pa_xfree(t);
+        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+        pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+        u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL;
+        u->sink->refresh_volume = 1;
+        u->sink->refresh_mute = 1;
+    } else
+        u->sink = NULL;
+
+    pa_assert(u->source || u->sink);
+
+    u->sig = pa_signal_new(SIGPOLL, sig_callback, u);
+    pa_assert(u->sig);
+    ioctl(u->fd, I_SETSIG, S_MSG);
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    /* Read mixer settings */
+    if (u->source)
+        pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_GET_VOLUME, &u->source->volume, 0, NULL);
+    if (u->sink) {
+        pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_VOLUME, &u->sink->volume, 0, NULL);
+        pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_MUTE, &u->sink->muted, 0, NULL);
+    }
+
+    if (u->sink)
+        pa_sink_put(u->sink);
+    if (u->source)
+        pa_source_put(u->source);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (u)
+        pa__done(m);
+    else if (fd >= 0)
+        close(fd);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    ioctl(u->fd, I_SETSIG, 0);
+    pa_signal_free(u->sig);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
+
+    if (u->source)
+        pa_source_unref(u->source);
+
+     if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->fd >= 0)
+        close(u->fd);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
new file mode 100644 (file)
index 0000000..bc7c023
--- /dev/null
@@ -0,0 +1,444 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+
+#include "module-suspend-on-idle-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+static const char* const valid_modargs[] = {
+    "timeout",
+    NULL,
+};
+
+struct userdata {
+    pa_core *core;
+    pa_usec_t timeout;
+    pa_hashmap *device_infos;
+    pa_hook_slot
+        *sink_new_slot,
+        *source_new_slot,
+        *sink_unlink_slot,
+        *source_unlink_slot,
+        *sink_state_changed_slot,
+        *source_state_changed_slot;
+
+    pa_hook_slot
+        *sink_input_new_slot,
+        *source_output_new_slot,
+        *sink_input_unlink_slot,
+        *source_output_unlink_slot,
+        *sink_input_move_slot,
+        *source_output_move_slot,
+        *sink_input_state_changed_slot,
+        *source_output_state_changed_slot;
+};
+
+struct device_info {
+    struct userdata *userdata;
+    pa_sink *sink;
+    pa_source *source;
+    struct timeval last_use;
+    pa_time_event *time_event;
+};
+
+static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+    struct device_info *d = userdata;
+
+    pa_assert(d);
+
+    d->userdata->core->mainloop->time_restart(d->time_event, NULL);
+
+    if (d->sink && pa_sink_used_by(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) {
+        pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
+        pa_sink_suspend(d->sink, TRUE);
+    }
+
+    if (d->source && pa_source_used_by(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) {
+        pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
+        pa_source_suspend(d->source, TRUE);
+    }
+}
+
+static void restart(struct device_info *d) {
+    struct timeval tv;
+    pa_assert(d);
+
+    pa_gettimeofday(&tv);
+    d->last_use = tv;
+    pa_timeval_add(&tv, d->userdata->timeout*1000000);
+    d->userdata->core->mainloop->time_restart(d->time_event, &tv);
+
+    if (d->sink)
+        pa_log_debug("Sink %s becomes idle.", d->sink->name);
+    if (d->source)
+        pa_log_debug("Source %s becomes idle.", d->source->name);
+}
+
+static void resume(struct device_info *d) {
+    pa_assert(d);
+
+    d->userdata->core->mainloop->time_restart(d->time_event, NULL);
+
+    if (d->sink) {
+        pa_sink_suspend(d->sink, FALSE);
+
+        pa_log_debug("Sink %s becomes busy.", d->sink->name);
+    }
+
+    if (d->source) {
+        pa_source_suspend(d->source, FALSE);
+
+        pa_log_debug("Source %s becomes busy.", d->source->name);
+    }
+}
+
+static pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_assert(data);
+    pa_assert(u);
+
+    if ((d = pa_hashmap_get(u->device_infos, data->sink)))
+        resume(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_assert(data);
+    pa_assert(u);
+
+    if ((d = pa_hashmap_get(u->device_infos, data->source)))
+        resume(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
+    pa_assert(c);
+    pa_sink_input_assert_ref(s);
+    pa_assert(u);
+
+    if (pa_sink_used_by(s->sink) <= 0) {
+        struct device_info *d;
+        if ((d = pa_hashmap_get(u->device_infos, s->sink)))
+            restart(d);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+    pa_assert(c);
+    pa_source_output_assert_ref(s);
+    pa_assert(u);
+
+    if (pa_source_used_by(s->source) <= 0) {
+        struct device_info *d;
+        if ((d = pa_hashmap_get(u->device_infos, s->source)))
+            restart(d);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input_move_hook_data *data, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_assert(data);
+    pa_assert(u);
+
+    if ((d = pa_hashmap_get(u->device_infos, data->destination)))
+        resume(d);
+
+    if (pa_sink_used_by(data->sink_input->sink) <= 1)
+        if ((d = pa_hashmap_get(u->device_infos, data->sink_input->sink)))
+            restart(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_move_hook_data *data, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_assert(data);
+    pa_assert(u);
+
+    if ((d = pa_hashmap_get(u->device_infos, data->destination)))
+        resume(d);
+
+    if (pa_source_used_by(data->source_output->source) <= 1)
+        if ((d = pa_hashmap_get(u->device_infos, data->source_output->source)))
+            restart(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
+    struct device_info *d;
+    pa_sink_input_state_t state;
+    pa_assert(c);
+    pa_sink_input_assert_ref(s);
+    pa_assert(u);
+
+    state = pa_sink_input_get_state(s);
+    if (state == PA_SINK_INPUT_RUNNING || state == PA_SINK_INPUT_DRAINED)
+        if ((d = pa_hashmap_get(u->device_infos, s->sink)))
+            resume(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+    struct device_info *d;
+    pa_source_output_state_t state;
+    pa_assert(c);
+    pa_source_output_assert_ref(s);
+    pa_assert(u);
+
+    state = pa_source_output_get_state(s);
+    if (state == PA_SOURCE_OUTPUT_RUNNING)
+        if ((d = pa_hashmap_get(u->device_infos, s->source)))
+            resume(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    struct device_info *d;
+    pa_source *source;
+    pa_sink *sink;
+
+    pa_assert(c);
+    pa_object_assert_ref(o);
+    pa_assert(u);
+
+    source = pa_source_isinstance(o) ? PA_SOURCE(o) : NULL;
+    sink = pa_sink_isinstance(o) ? PA_SINK(o) : NULL;
+
+    pa_assert(source || sink);
+
+    d = pa_xnew(struct device_info, 1);
+    d->userdata = u;
+    d->source = source ? pa_source_ref(source) : NULL;
+    d->sink = sink ? pa_sink_ref(sink) : NULL;
+    d->time_event = c->mainloop->time_new(c->mainloop, NULL, timeout_cb, d);
+    pa_hashmap_put(u->device_infos, o, d);
+
+    if ((d->sink && pa_sink_used_by(d->sink) <= 0) ||
+        (d->source && pa_source_used_by(d->source) <= 0))
+        restart(d);
+
+    return PA_HOOK_OK;
+}
+
+static void device_info_free(struct device_info *d) {
+    pa_assert(d);
+
+    if (d->source)
+        pa_source_unref(d->source);
+    if (d->sink)
+        pa_sink_unref(d->sink);
+
+    d->userdata->core->mainloop->time_free(d->time_event);
+
+    pa_xfree(d);
+}
+
+static pa_hook_result_t device_unlink_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_object_assert_ref(o);
+    pa_assert(u);
+
+    if ((d = pa_hashmap_remove(u->device_infos, o)))
+        device_info_free(d);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_object_assert_ref(o);
+    pa_assert(u);
+
+    if (!(d = pa_hashmap_get(u->device_infos, o)))
+        return PA_HOOK_OK;
+
+    if (pa_sink_isinstance(o)) {
+        pa_sink *s = PA_SINK(o);
+        pa_sink_state_t state = pa_sink_get_state(s);
+
+        if (pa_sink_used_by(s) <= 0) {
+
+            if (PA_SINK_IS_OPENED(state))
+                restart(d);
+
+        }
+
+    } else if (pa_source_isinstance(o)) {
+        pa_source *s = PA_SOURCE(o);
+        pa_source_state_t state = pa_source_get_state(s);
+
+        if (pa_source_used_by(s) <= 0) {
+
+            if (PA_SOURCE_IS_OPENED(state))
+                restart(d);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    uint32_t timeout = 1;
+    uint32_t idx;
+    pa_sink *sink;
+    pa_source *source;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "timeout", &timeout) < 0) {
+        pa_log("Failed to parse timeout value.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->timeout = timeout;
+    u->device_infos = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
+        device_new_hook_cb(m->core, PA_OBJECT(sink), u);
+
+    for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
+        device_new_hook_cb(m->core, PA_OBJECT(source), u);
+
+    u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
+    u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
+    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u);
+    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u);
+    u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u);
+    u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u);
+
+    u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_fixate_hook_cb, u);
+    u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u);
+    u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
+    u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u);
+    u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_hook_cb, u);
+    u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_hook_cb, u);
+    u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
+    u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    struct device_info *d;
+
+    pa_assert(m);
+
+    if (!m->userdata)
+        return;
+
+    u = m->userdata;
+
+    if (u->sink_new_slot)
+        pa_hook_slot_free(u->sink_new_slot);
+    if (u->sink_unlink_slot)
+        pa_hook_slot_free(u->sink_unlink_slot);
+    if (u->sink_state_changed_slot)
+        pa_hook_slot_free(u->sink_state_changed_slot);
+
+    if (u->source_new_slot)
+        pa_hook_slot_free(u->source_new_slot);
+    if (u->source_unlink_slot)
+        pa_hook_slot_free(u->source_unlink_slot);
+    if (u->source_state_changed_slot)
+        pa_hook_slot_free(u->source_state_changed_slot);
+
+    if (u->sink_input_new_slot)
+        pa_hook_slot_free(u->sink_input_new_slot);
+    if (u->sink_input_unlink_slot)
+        pa_hook_slot_free(u->sink_input_unlink_slot);
+    if (u->sink_input_move_slot)
+        pa_hook_slot_free(u->sink_input_move_slot);
+    if (u->sink_input_state_changed_slot)
+        pa_hook_slot_free(u->sink_input_state_changed_slot);
+
+    if (u->source_output_new_slot)
+        pa_hook_slot_free(u->source_output_new_slot);
+    if (u->source_output_unlink_slot)
+        pa_hook_slot_free(u->source_output_unlink_slot);
+    if (u->source_output_move_slot)
+        pa_hook_slot_free(u->source_output_move_slot);
+    if (u->source_output_state_changed_slot)
+        pa_hook_slot_free(u->source_output_state_changed_slot);
+
+    while ((d = pa_hashmap_steal_first(u->device_infos)))
+        device_info_free(d);
+
+    pa_hashmap_free(u->device_infos, NULL, NULL);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
new file mode 100644 (file)
index 0000000..86f3081
--- /dev/null
@@ -0,0 +1,1929 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/version.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/authkey.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/authkey-prop.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/proplist-util.h>
+
+#ifdef TUNNEL_SINK
+#include "module-tunnel-sink-symdef.h"
+#else
+#include "module-tunnel-source-symdef.h"
+#endif
+
+#ifdef TUNNEL_SINK
+PA_MODULE_DESCRIPTION("Tunnel module for sinks");
+PA_MODULE_USAGE(
+        "server=<address> "
+        "sink=<remote sink name> "
+        "cookie=<filename> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "sink_name=<name for the local sink> "
+        "channel_map=<channel map>");
+#else
+PA_MODULE_DESCRIPTION("Tunnel module for sources");
+PA_MODULE_USAGE(
+        "server=<address> "
+        "source=<remote source name> "
+        "cookie=<filename> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "source_name=<name for the local source> "
+        "channel_map=<channel map>");
+#endif
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+
+static const char* const valid_modargs[] = {
+    "server",
+    "cookie",
+    "format",
+    "channels",
+    "rate",
+#ifdef TUNNEL_SINK
+    "sink_name",
+    "sink",
+#else
+    "source_name",
+    "source",
+#endif
+    "channel_map",
+    NULL,
+};
+
+#define DEFAULT_TIMEOUT 5
+
+#define LATENCY_INTERVAL 10
+
+#define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC)
+
+#ifdef TUNNEL_SINK
+
+enum {
+    SINK_MESSAGE_REQUEST = PA_SINK_MESSAGE_MAX,
+    SINK_MESSAGE_REMOTE_SUSPEND,
+    SINK_MESSAGE_UPDATE_LATENCY,
+    SINK_MESSAGE_POST
+};
+
+#define DEFAULT_TLENGTH_MSEC 150
+#define DEFAULT_MINREQ_MSEC 25
+
+#else
+
+enum {
+    SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
+    SOURCE_MESSAGE_REMOTE_SUSPEND,
+    SOURCE_MESSAGE_UPDATE_LATENCY
+};
+
+#define DEFAULT_FRAGSIZE_MSEC 25
+
+#endif
+
+#ifdef TUNNEL_SINK
+static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+#endif
+static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
+#ifdef TUNNEL_SINK
+    [PA_COMMAND_REQUEST] = command_request,
+    [PA_COMMAND_STARTED] = command_started,
+#endif
+    [PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event,
+    [PA_COMMAND_OVERFLOW] = command_overflow_or_underflow,
+    [PA_COMMAND_UNDERFLOW] = command_overflow_or_underflow,
+    [PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed,
+    [PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed,
+    [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,
+    [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,
+    [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved,
+    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved,
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    pa_thread *thread;
+
+    pa_socket_client *client;
+    pa_pstream *pstream;
+    pa_pdispatch *pdispatch;
+
+    char *server_name;
+#ifdef TUNNEL_SINK
+    char *sink_name;
+    pa_sink *sink;
+    int32_t requested_bytes;
+#else
+    char *source_name;
+    pa_source *source;
+#endif
+
+    uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
+
+    uint32_t version;
+    uint32_t ctag;
+    uint32_t device_index;
+    uint32_t channel;
+
+    int64_t counter, counter_delta;
+
+    pa_bool_t remote_corked:1;
+    pa_bool_t remote_suspended:1;
+
+    pa_usec_t transport_usec;
+    pa_bool_t transport_usec_valid;
+
+    uint32_t ignore_latency_before;
+
+    pa_time_event *time_event;
+
+    pa_bool_t auth_cookie_in_property;
+
+    pa_smoother *smoother;
+
+    char *device_description;
+    char *server_fqdn;
+    char *user_name;
+
+    uint32_t maxlength;
+#ifdef TUNNEL_SINK
+    uint32_t tlength;
+    uint32_t minreq;
+    uint32_t prebuf;
+#else
+    uint32_t fragsize;
+#endif
+};
+
+static void request_latency(struct userdata *u);
+
+/* Called from main context */
+static void command_stream_killed(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    pa_log_warn("Stream killed");
+    pa_module_unload_request(u->module);
+}
+
+/* Called from main context */
+static void command_overflow_or_underflow(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    pa_log_info("Server signalled buffer overrun/underrun.");
+    request_latency(u);
+}
+
+/* Called from main context */
+static void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t channel;
+    pa_bool_t suspended;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_get_boolean(t, &suspended) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_log("Invalid packet");
+        pa_module_unload_request(u->module);
+        return;
+    }
+
+#ifdef TUNNEL_SINK
+    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
+#else
+    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
+#endif
+
+    request_latency(u);
+}
+
+/* Called from main context */
+static void command_moved(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    pa_log_debug("Server reports a stream move.");
+    request_latency(u);
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from main context */
+static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+   struct userdata *u = userdata;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    pa_log_debug("Server reports playback started.");
+    request_latency(u);
+}
+
+#endif
+
+/* Called from IO thread context */
+static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) {
+    pa_usec_t x;
+    pa_assert(u);
+
+    if (u->remote_corked == cork)
+        return;
+
+    u->remote_corked = cork;
+    x = pa_rtclock_usec();
+
+    /* Correct by the time this needs to travel to the other side.
+     * This is a valid thread-safe access, because the main thread is
+     * waiting for us */
+    if (u->transport_usec_valid)
+        x += u->transport_usec;
+
+    if (u->remote_suspended || u->remote_corked)
+        pa_smoother_pause(u->smoother, x);
+    else
+        pa_smoother_resume(u->smoother, x);
+}
+
+/* Called from main context */
+static void stream_cork(struct userdata *u, pa_bool_t cork) {
+    pa_tagstruct *t;
+    pa_assert(u);
+
+    if (!u->pstream)
+        return;
+
+    t = pa_tagstruct_new(NULL, 0);
+#ifdef TUNNEL_SINK
+    pa_tagstruct_putu32(t, PA_COMMAND_CORK_PLAYBACK_STREAM);
+#else
+    pa_tagstruct_putu32(t, PA_COMMAND_CORK_RECORD_STREAM);
+#endif
+    pa_tagstruct_putu32(t, u->ctag++);
+    pa_tagstruct_putu32(t, u->channel);
+    pa_tagstruct_put_boolean(t, !!cork);
+    pa_pstream_send_tagstruct(u->pstream, t);
+
+    request_latency(u);
+}
+
+/* Called from IO thread context */
+static void stream_suspend_within_thread(struct userdata *u, pa_bool_t suspend) {
+    pa_usec_t x;
+    pa_assert(u);
+
+    if (u->remote_suspended == suspend)
+        return;
+
+    u->remote_suspended = suspend;
+
+    x = pa_rtclock_usec();
+
+    /* Correct by the time this needed to travel from the other side.
+     * This is a valid thread-safe access, because the main thread is
+     * waiting for us */
+    if (u->transport_usec_valid)
+        x -= u->transport_usec;
+
+    if (u->remote_suspended || u->remote_corked)
+        pa_smoother_pause(u->smoother, x);
+    else
+        pa_smoother_resume(u->smoother, x);
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from IO thread context */
+static void send_data(struct userdata *u) {
+    pa_assert(u);
+
+    while (u->requested_bytes > 0) {
+        pa_memchunk memchunk;
+
+        pa_sink_render(u->sink, u->requested_bytes, &memchunk);
+        pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_POST, NULL, 0, &memchunk, NULL);
+        pa_memblock_unref(memchunk.memblock);
+
+        u->requested_bytes -= memchunk.length;
+
+        u->counter += memchunk.length;
+    }
+}
+
+/* This function is called from IO context -- except when it is not. */
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SINK(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            int r;
+
+            /* First, change the state, because otherwide pa_sink_render() would fail */
+            if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) {
+
+                stream_cork_within_thread(u, u->sink->state == PA_SINK_SUSPENDED);
+
+                if (PA_SINK_IS_OPENED(u->sink->state))
+                    send_data(u);
+            }
+
+            return r;
+        }
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t yl, yr, *usec = data;
+
+            yl = pa_bytes_to_usec(u->counter, &u->sink->sample_spec);
+            yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
+
+            *usec = yl > yr ? yl - yr : 0;
+            return 0;
+        }
+
+        case SINK_MESSAGE_REQUEST:
+
+            pa_assert(offset > 0);
+            u->requested_bytes += (size_t) offset;
+
+            if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+                send_data(u);
+
+            return 0;
+
+
+        case SINK_MESSAGE_REMOTE_SUSPEND:
+
+            stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
+            return 0;
+
+
+        case SINK_MESSAGE_UPDATE_LATENCY: {
+            pa_usec_t y;
+
+            y = pa_bytes_to_usec(u->counter, &u->sink->sample_spec);
+
+            if (y > (pa_usec_t) offset || offset < 0)
+                y -= offset;
+            else
+                y = 0;
+
+            pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
+
+            return 0;
+        }
+
+        case SINK_MESSAGE_POST:
+
+            /* OK, This might be a bit confusing. This message is
+             * delivered to us from the main context -- NOT from the
+             * IO thread context where the rest of the messages are
+             * dispatched. Yeah, ugly, but I am a lazy bastard. */
+
+            pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, chunk);
+
+            u->counter_delta += chunk->length;
+
+            return 0;
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+    pa_sink_assert_ref(s);
+    u = s->userdata;
+
+    switch ((pa_sink_state_t) state) {
+
+        case PA_SINK_SUSPENDED:
+            pa_assert(PA_SINK_IS_OPENED(s->state));
+            stream_cork(u, TRUE);
+            break;
+
+        case PA_SINK_IDLE:
+        case PA_SINK_RUNNING:
+            if (s->state == PA_SINK_SUSPENDED)
+                stream_cork(u, FALSE);
+            break;
+
+        case PA_SINK_UNLINKED:
+        case PA_SINK_INIT:
+            ;
+    }
+
+    return 0;
+}
+
+#else
+
+/* This function is called from IO context -- except when it is not. */
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u = PA_SOURCE(o)->userdata;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_SET_STATE: {
+            int r;
+
+            if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0)
+                stream_cork_within_thread(u, u->source->state == PA_SOURCE_SUSPENDED);
+
+            return r;
+        }
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            pa_usec_t yr, yl, *usec = data;
+
+            yl = pa_bytes_to_usec(u->counter, &PA_SINK(o)->sample_spec);
+            yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
+
+            *usec = yr > yl ? yr - yl : 0;
+            return 0;
+        }
+
+        case SOURCE_MESSAGE_POST:
+
+            if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+                pa_source_post(u->source, chunk);
+
+            u->counter += chunk->length;
+
+            return 0;
+
+        case SOURCE_MESSAGE_REMOTE_SUSPEND:
+
+            stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
+            return 0;
+
+        case SOURCE_MESSAGE_UPDATE_LATENCY: {
+            pa_usec_t y;
+
+            y = pa_bytes_to_usec(u->counter, &u->source->sample_spec);
+
+            if (offset >= 0 || y > (pa_usec_t) -offset)
+                y += offset;
+            else
+                y = 0;
+
+            pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
+
+            return 0;
+        }
+    }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from main context */
+static int source_set_state(pa_source *s, pa_source_state_t state) {
+    struct userdata *u;
+    pa_source_assert_ref(s);
+    u = s->userdata;
+
+    switch ((pa_source_state_t) state) {
+
+        case PA_SOURCE_SUSPENDED:
+            pa_assert(PA_SOURCE_IS_OPENED(s->state));
+            stream_cork(u, TRUE);
+            break;
+
+        case PA_SOURCE_IDLE:
+        case PA_SOURCE_RUNNING:
+            if (s->state == PA_SOURCE_SUSPENDED)
+                stream_cork(u, FALSE);
+            break;
+
+        case PA_SOURCE_UNLINKED:
+        case PA_SOURCE_INIT:
+            ;
+    }
+
+    return 0;
+}
+
+#endif
+
+static void thread_func(void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+    pa_rtpoll_install(u->rtpoll);
+
+    for (;;) {
+        int ret;
+
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
+}
+
+#ifdef TUNNEL_SINK
+/* Called from main context */
+static void command_request(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t bytes, channel;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_REQUEST);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &bytes) < 0) {
+        pa_log("Invalid protocol reply");
+        goto fail;
+    }
+
+    if (channel != u->channel) {
+        pa_log("Recieved data for invalid channel");
+        goto fail;
+    }
+
+    pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
+    return;
+
+fail:
+    pa_module_unload_request(u->module);
+}
+
+#endif
+
+/* Called from main context */
+static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    pa_usec_t sink_usec, source_usec, transport_usec;
+    pa_bool_t playing;
+    int64_t write_index, read_index;
+    struct timeval local, remote, now;
+    pa_sample_spec *ss;
+    int64_t delay;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get latency.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
+        pa_tagstruct_get_usec(t, &source_usec) < 0 ||
+        pa_tagstruct_get_boolean(t, &playing) < 0 ||
+        pa_tagstruct_get_timeval(t, &local) < 0 ||
+        pa_tagstruct_get_timeval(t, &remote) < 0 ||
+        pa_tagstruct_gets64(t, &write_index) < 0 ||
+        pa_tagstruct_gets64(t, &read_index) < 0) {
+        pa_log("Invalid reply.");
+        goto fail;
+    }
+
+#ifdef TUNNEL_SINK
+    if (u->version >= 13) {
+        uint64_t underrun_for = 0, playing_for = 0;
+
+        if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+            pa_tagstruct_getu64(t, &playing_for) < 0) {
+            pa_log("Invalid reply.");
+            goto fail;
+        }
+    }
+#endif
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_log("Invalid reply.");
+        goto fail;
+    }
+
+    if (tag < u->ignore_latency_before) {
+        request_latency(u);
+        return;
+    }
+
+    pa_gettimeofday(&now);
+
+    /* Calculate transport usec */
+    if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
+        /* local and remote seem to have synchronized clocks */
+#ifdef TUNNEL_SINK
+        u->transport_usec = pa_timeval_diff(&remote, &local);
+#else
+        u->transport_usec = pa_timeval_diff(&now, &remote);
+#endif
+    } else
+        u->transport_usec = pa_timeval_diff(&now, &local)/2;
+    u->transport_usec_valid = TRUE;
+
+    /* First, take the device's delay */
+#ifdef TUNNEL_SINK
+    delay = (int64_t) sink_usec;
+    ss = &u->sink->sample_spec;
+#else
+    delay = (int64_t) source_usec;
+    ss = &u->source->sample_spec;
+#endif
+
+    /* Add the length of our server-side buffer */
+    if (write_index >= read_index)
+        delay += (int64_t) pa_bytes_to_usec(write_index-read_index, ss);
+    else
+        delay -= (int64_t) pa_bytes_to_usec(read_index-write_index, ss);
+
+    /* Our measurements are already out of date, hence correct by the     *
+     * transport latency */
+#ifdef TUNNEL_SINK
+    delay -= (int64_t) transport_usec;
+#else
+    delay += (int64_t) transport_usec;
+#endif
+
+    /* Now correct by what we have have read/written since we requested the update */
+#ifdef TUNNEL_SINK
+    delay += (int64_t) pa_bytes_to_usec(u->counter_delta, ss);
+#else
+    delay -= (int64_t) pa_bytes_to_usec(u->counter_delta, ss);
+#endif
+
+#ifdef TUNNEL_SINK
+    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
+#else
+    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
+#endif
+
+    return;
+
+fail:
+
+    pa_module_unload_request(u->module);
+}
+
+/* Called from main context */
+static void request_latency(struct userdata *u) {
+    pa_tagstruct *t;
+    struct timeval now;
+    uint32_t tag;
+    pa_assert(u);
+
+    t = pa_tagstruct_new(NULL, 0);
+#ifdef TUNNEL_SINK
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
+#else
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY);
+#endif
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, u->channel);
+
+    pa_gettimeofday(&now);
+    pa_tagstruct_put_timeval(t, &now);
+
+    pa_pstream_send_tagstruct(u->pstream, t);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL);
+
+    u->ignore_latency_before = tag;
+    u->counter_delta = 0;
+}
+
+/* Called from main context */
+static void timeout_callback(pa_mainloop_api *m, pa_time_event*e,  const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+    struct timeval ntv;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(u);
+
+    request_latency(u);
+
+    pa_gettimeofday(&ntv);
+    ntv.tv_sec += LATENCY_INTERVAL;
+    m->time_restart(e, &ntv);
+}
+
+/* Called from main context */
+static void update_description(struct userdata *u) {
+    char *d;
+    char un[128], hn[128];
+    pa_tagstruct *t;
+
+    pa_assert(u);
+
+    if (!u->server_fqdn || !u->user_name || !u->device_description)
+        return;
+
+    d = pa_sprintf_malloc("%s on %s@%s", u->device_description, u->user_name, u->server_fqdn);
+
+#ifdef TUNNEL_SINK
+    pa_sink_set_description(u->sink, d);
+    pa_proplist_sets(u->sink->proplist, "tunnel.remote.user", u->user_name);
+    pa_proplist_sets(u->sink->proplist, "tunnel.remote.fqdn", u->server_fqdn);
+    pa_proplist_sets(u->sink->proplist, "tunnel.remote.description", u->device_description);
+#else
+    pa_source_set_description(u->source, d);
+    pa_proplist_sets(u->source->proplist, "tunnel.remote.user", u->user_name);
+    pa_proplist_sets(u->source->proplist, "tunnel.remote.fqdn", u->server_fqdn);
+    pa_proplist_sets(u->source->proplist, "tunnel.remote.description", u->device_description);
+#endif
+
+    pa_xfree(d);
+
+    d = pa_sprintf_malloc("%s for %s@%s", u->device_description,
+                          pa_get_user_name(un, sizeof(un)),
+                          pa_get_host_name(hn, sizeof(hn)));
+
+    t = pa_tagstruct_new(NULL, 0);
+#ifdef TUNNEL_SINK
+    pa_tagstruct_putu32(t, PA_COMMAND_SET_PLAYBACK_STREAM_NAME);
+#else
+    pa_tagstruct_putu32(t, PA_COMMAND_SET_RECORD_STREAM_NAME);
+#endif
+    pa_tagstruct_putu32(t, u->ctag++);
+    pa_tagstruct_putu32(t, u->channel);
+    pa_tagstruct_puts(t, d);
+    pa_pstream_send_tagstruct(u->pstream, t);
+
+    pa_xfree(d);
+}
+
+/* Called from main context */
+static void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    pa_sample_spec ss;
+    const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;
+    uint32_t cookie;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_gets(t, &server_name) < 0 ||
+        pa_tagstruct_gets(t, &server_version) < 0 ||
+        pa_tagstruct_gets(t, &user_name) < 0 ||
+        pa_tagstruct_gets(t, &host_name) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_gets(t, &default_sink_name) < 0 ||
+        pa_tagstruct_gets(t, &default_source_name) < 0 ||
+        pa_tagstruct_getu32(t, &cookie) < 0) {
+
+        pa_log("Parse failure");
+        goto fail;
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_log("Packet too long");
+        goto fail;
+    }
+
+    pa_xfree(u->server_fqdn);
+    u->server_fqdn = pa_xstrdup(host_name);
+
+    pa_xfree(u->user_name);
+    u->user_name = pa_xstrdup(user_name);
+
+    update_description(u);
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module);
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from main context */
+static void sink_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t idx, owner_module, monitor_source, flags;
+    const char *name, *description, *monitor_source_name, *driver;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    pa_cvolume volume;
+    pa_bool_t mute;
+    pa_usec_t latency;
+    pa_proplist *pl;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    pl = pa_proplist_new();
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &description) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+        pa_tagstruct_getu32(t, &owner_module) < 0 ||
+        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &mute) < 0 ||
+        pa_tagstruct_getu32(t, &monitor_source) < 0 ||
+        pa_tagstruct_gets(t, &monitor_source_name) < 0 ||
+        pa_tagstruct_get_usec(t, &latency) < 0 ||
+        pa_tagstruct_gets(t, &driver) < 0 ||
+        pa_tagstruct_getu32(t, &flags) < 0) {
+
+        pa_log("Parse failure");
+        goto fail;
+    }
+
+    if (u->version >= 13) {
+        pa_usec_t configured_latency;
+
+        if (pa_tagstruct_get_proplist(t, pl) < 0 ||
+            pa_tagstruct_get_usec(t, &configured_latency) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_log("Packet too long");
+        goto fail;
+    }
+
+    pa_proplist_free(pl);
+
+    if (!u->sink_name || strcmp(name, u->sink_name))
+        return;
+
+    pa_xfree(u->device_description);
+    u->device_description = pa_xstrdup(description);
+
+    update_description(u);
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module);
+    pa_proplist_free(pl);
+}
+
+/* Called from main context */
+static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t idx, owner_module, client, sink;
+    pa_usec_t buffer_usec, sink_usec;
+    const char *name, *driver, *resample_method;
+    pa_bool_t mute;
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    pa_cvolume volume;
+    pa_proplist *pl;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    pl = pa_proplist_new();
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_getu32(t, &owner_module) < 0 ||
+        pa_tagstruct_getu32(t, &client) < 0 ||
+        pa_tagstruct_getu32(t, &sink) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &sample_spec) < 0 ||
+        pa_tagstruct_get_channel_map(t, &channel_map) < 0 ||
+        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+        pa_tagstruct_get_usec(t, &buffer_usec) < 0 ||
+        pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
+        pa_tagstruct_gets(t, &resample_method) < 0 ||
+        pa_tagstruct_gets(t, &driver) < 0) {
+
+        pa_log("Parse failure");
+        goto fail;
+    }
+
+    if (u->version >= 11) {
+        if (pa_tagstruct_get_boolean(t, &mute) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 13) {
+        if (pa_tagstruct_get_proplist(t, pl) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_log("Packet too long");
+        goto fail;
+    }
+
+    pa_proplist_free(pl);
+
+    if (idx != u->device_index)
+        return;
+
+    pa_assert(u->sink);
+
+    if ((u->version < 11 || !!mute == !!u->sink->muted) &&
+        pa_cvolume_equal(&volume, &u->sink->volume))
+        return;
+
+    memcpy(&u->sink->volume, &volume, sizeof(pa_cvolume));
+
+    if (u->version >= 11)
+        u->sink->muted = !!mute;
+
+    pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);
+    return;
+
+fail:
+    pa_module_unload_request(u->module);
+    pa_proplist_free(pl);
+}
+
+#else
+
+/* Called from main context */
+static void source_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t idx, owner_module, monitor_of_sink, flags;
+    const char *name, *description, *monitor_of_sink_name, *driver;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    pa_cvolume volume;
+    pa_bool_t mute;
+    pa_usec_t latency, configured_latency;
+    pa_proplist *pl;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    pl = pa_proplist_new();
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &description) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+        pa_tagstruct_getu32(t, &owner_module) < 0 ||
+        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &mute) < 0 ||
+        pa_tagstruct_getu32(t, &monitor_of_sink) < 0 ||
+        pa_tagstruct_gets(t, &monitor_of_sink_name) < 0 ||
+        pa_tagstruct_get_usec(t, &latency) < 0 ||
+        pa_tagstruct_gets(t, &driver) < 0 ||
+        pa_tagstruct_getu32(t, &flags) < 0) {
+
+        pa_log("Parse failure");
+        goto fail;
+    }
+
+    if (u->version >= 13) {
+        if (pa_tagstruct_get_proplist(t, pl) < 0 ||
+            pa_tagstruct_get_usec(t, &configured_latency) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_log("Packet too long");
+        goto fail;
+    }
+
+    pa_proplist_free(pl);
+
+    if (!u->source_name || strcmp(name, u->source_name))
+        return;
+
+    pa_xfree(u->device_description);
+    u->device_description = pa_xstrdup(description);
+
+    update_description(u);
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module);
+    pa_proplist_free(pl);
+}
+
+#endif
+
+/* Called from main context */
+static void request_info(struct userdata *u) {
+    pa_tagstruct *t;
+    uint32_t tag;
+    pa_assert(u);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_SERVER_INFO);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_pstream_send_tagstruct(u->pstream, t);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, server_info_cb, u, NULL);
+
+#ifdef TUNNEL_SINK
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, u->device_index);
+    pa_pstream_send_tagstruct(u->pstream, t);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_input_info_cb, u, NULL);
+
+    if (u->sink_name) {
+        t = pa_tagstruct_new(NULL, 0);
+        pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
+        pa_tagstruct_putu32(t, tag = u->ctag++);
+        pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+        pa_tagstruct_puts(t, u->sink_name);
+        pa_pstream_send_tagstruct(u->pstream, t);
+        pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_info_cb, u, NULL);
+    }
+#else
+    if (u->source_name) {
+        t = pa_tagstruct_new(NULL, 0);
+        pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
+        pa_tagstruct_putu32(t, tag = u->ctag++);
+        pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+        pa_tagstruct_puts(t, u->source_name);
+        pa_pstream_send_tagstruct(u->pstream, t);
+        pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, source_info_cb, u, NULL);
+    }
+#endif
+}
+
+/* Called from main context */
+static void command_subscribe_event(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    pa_subscription_event_type_t e;
+    uint32_t idx;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
+
+    if (pa_tagstruct_getu32(t, &e) < 0 ||
+        pa_tagstruct_getu32(t, &idx) < 0) {
+        pa_log("Invalid protocol reply");
+        pa_module_unload_request(u->module);
+        return;
+    }
+
+    if (e != (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+#ifdef TUNNEL_SINK
+        e != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        e != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE)
+#else
+        e != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)
+#endif
+        )
+        return;
+
+    request_info(u);
+}
+
+/* Called from main context */
+static void start_subscribe(struct userdata *u) {
+    pa_tagstruct *t;
+    uint32_t tag;
+    pa_assert(u);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER|
+#ifdef TUNNEL_SINK
+                        PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK
+#else
+                        PA_SUBSCRIPTION_MASK_SOURCE
+#endif
+                        );
+
+    pa_pstream_send_tagstruct(u->pstream, t);
+}
+
+/* Called from main context */
+static void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    struct timeval ntv;
+#ifdef TUNNEL_SINK
+    uint32_t bytes;
+#endif
+
+    pa_assert(pd);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to create stream.");
+        else
+            pa_log("Protocol error.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_getu32(t, &u->channel) < 0 ||
+        pa_tagstruct_getu32(t, &u->device_index) < 0
+#ifdef TUNNEL_SINK
+        || pa_tagstruct_getu32(t, &bytes) < 0
+#endif
+        )
+        goto parse_error;
+
+    if (u->version >= 9) {
+#ifdef TUNNEL_SINK
+        if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
+            pa_tagstruct_getu32(t, &u->tlength) < 0 ||
+            pa_tagstruct_getu32(t, &u->prebuf) < 0 ||
+            pa_tagstruct_getu32(t, &u->minreq) < 0)
+            goto parse_error;
+#else
+        if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
+            pa_tagstruct_getu32(t, &u->fragsize) < 0)
+            goto parse_error;
+#endif
+    }
+
+    if (u->version >= 12) {
+        pa_sample_spec ss;
+        pa_channel_map cm;
+        uint32_t device_index;
+        const char *dn;
+        pa_bool_t suspended;
+
+        if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+            pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+            pa_tagstruct_getu32(t, &device_index) < 0 ||
+            pa_tagstruct_gets(t, &dn) < 0 ||
+            pa_tagstruct_get_boolean(t, &suspended) < 0)
+            goto parse_error;
+
+#ifdef TUNNEL_SINK
+        pa_xfree(u->sink_name);
+        u->sink_name = pa_xstrdup(dn);
+#else
+        pa_xfree(u->source_name);
+        u->source_name = pa_xstrdup(dn);
+#endif
+    }
+
+    if (u->version >= 13) {
+        pa_usec_t usec;
+
+        if (pa_tagstruct_get_usec(t, &usec) < 0)
+            goto parse_error;
+
+#ifdef TUNNEL_SINK
+        pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0);
+#else
+        pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0);
+#endif
+    }
+
+    if (!pa_tagstruct_eof(t))
+        goto parse_error;
+
+    start_subscribe(u);
+    request_info(u);
+
+    pa_assert(!u->time_event);
+    pa_gettimeofday(&ntv);
+    ntv.tv_sec += LATENCY_INTERVAL;
+    u->time_event = u->core->mainloop->time_new(u->core->mainloop, &ntv, timeout_callback, u);
+
+    request_latency(u);
+
+    pa_log_debug("Stream created.");
+
+#ifdef TUNNEL_SINK
+    pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
+#endif
+
+    return;
+
+parse_error:
+    pa_log("Invalid reply. (Create stream)");
+
+fail:
+    pa_module_unload_request(u->module);
+
+}
+
+/* Called from main context */
+static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    pa_tagstruct *reply;
+    char name[256], un[128], hn[128];
+#ifdef TUNNEL_SINK
+    pa_cvolume volume;
+#endif
+
+    pa_assert(pd);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (command != PA_COMMAND_REPLY ||
+        pa_tagstruct_getu32(t, &u->version) < 0 ||
+        !pa_tagstruct_eof(t)) {
+
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to authenticate");
+        else
+            pa_log("Protocol error.");
+
+        goto fail;
+    }
+
+    /* Minimum supported protocol version */
+    if (u->version < 8) {
+        pa_log("Incompatible protocol version");
+        goto fail;
+    }
+
+    /* Starting with protocol version 13 the MSB of the version tag
+    reflects if shm is enabled for this connection or not. We don't
+    support SHM here at all, so we just ignore this. */
+
+    if (u->version >= 13)
+        u->version &= 0x7FFFFFFFU;
+
+    pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION);
+
+#ifdef TUNNEL_SINK
+    pa_snprintf(name, sizeof(name), "%s for %s@%s",
+                u->sink_name,
+                pa_get_user_name(un, sizeof(un)),
+                pa_get_host_name(hn, sizeof(hn)));
+#else
+    pa_snprintf(name, sizeof(name), "%s for %s@%s",
+                u->source_name,
+                pa_get_user_name(un, sizeof(un)),
+                pa_get_host_name(hn, sizeof(hn)));
+#endif
+
+    reply = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
+    pa_tagstruct_putu32(reply, tag = u->ctag++);
+
+    if (u->version >= 13) {
+        pa_proplist *pl;
+        pl = pa_proplist_new();
+        pa_init_proplist(pl);
+        pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
+        pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
+        pa_tagstruct_put_proplist(reply, pl);
+        pa_proplist_free(pl);
+    } else
+        pa_tagstruct_puts(reply, "PulseAudio");
+
+    pa_pstream_send_tagstruct(u->pstream, reply);
+    /* We ignore the server's reply here */
+
+    reply = pa_tagstruct_new(NULL, 0);
+
+    if (u->version < 13)
+        /* Only for older PA versions we need to fill in the maxlength */
+        u->maxlength = 4*1024*1024;
+
+#ifdef TUNNEL_SINK
+    u->tlength = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_TLENGTH_MSEC, &u->sink->sample_spec);
+    u->minreq = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_MINREQ_MSEC, &u->sink->sample_spec);
+    u->prebuf = u->tlength;
+#else
+    u->fragsize = pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_FRAGSIZE_MSEC, &u->source->sample_spec);
+#endif
+
+#ifdef TUNNEL_SINK
+    pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM);
+    pa_tagstruct_putu32(reply, tag = u->ctag++);
+
+    if (u->version < 13)
+        pa_tagstruct_puts(reply, name);
+
+    pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec);
+    pa_tagstruct_put_channel_map(reply, &u->sink->channel_map);
+    pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
+    pa_tagstruct_puts(reply, u->sink_name);
+    pa_tagstruct_putu32(reply, u->maxlength);
+    pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
+    pa_tagstruct_putu32(reply, u->tlength);
+    pa_tagstruct_putu32(reply, u->prebuf);
+    pa_tagstruct_putu32(reply, u->minreq);
+    pa_tagstruct_putu32(reply, 0);
+    pa_cvolume_reset(&volume, u->sink->sample_spec.channels);
+    pa_tagstruct_put_cvolume(reply, &volume);
+#else
+    pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM);
+    pa_tagstruct_putu32(reply, tag = u->ctag++);
+
+    if (u->version < 13)
+        pa_tagstruct_puts(reply, name);
+
+    pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec);
+    pa_tagstruct_put_channel_map(reply, &u->source->channel_map);
+    pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
+    pa_tagstruct_puts(reply, u->source_name);
+    pa_tagstruct_putu32(reply, u->maxlength);
+    pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)));
+    pa_tagstruct_putu32(reply, u->fragsize);
+#endif
+
+    if (u->version >= 12) {
+        pa_tagstruct_put_boolean(reply, FALSE); /* no_remap */
+        pa_tagstruct_put_boolean(reply, FALSE); /* no_remix */
+        pa_tagstruct_put_boolean(reply, FALSE); /* fix_format */
+        pa_tagstruct_put_boolean(reply, FALSE); /* fix_rate */
+        pa_tagstruct_put_boolean(reply, FALSE); /* fix_channels */
+        pa_tagstruct_put_boolean(reply, TRUE); /* no_move */
+        pa_tagstruct_put_boolean(reply, FALSE); /* variable_rate */
+    }
+
+    if (u->version >= 13) {
+        pa_proplist *pl;
+
+        pa_tagstruct_put_boolean(reply, FALSE); /* start muted/peak detect*/
+        pa_tagstruct_put_boolean(reply, TRUE); /* adjust_latency */
+
+        pl = pa_proplist_new();
+        pa_proplist_sets(pl, PA_PROP_MEDIA_NAME, name);
+        pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "abstract");
+        pa_tagstruct_put_proplist(reply, pl);
+        pa_proplist_free(pl);
+
+#ifndef TUNNEL_SINK
+        pa_tagstruct_putu32(reply, PA_INVALID_INDEX); /* direct on input */
+#endif
+    }
+
+    pa_pstream_send_tagstruct(u->pstream, reply);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
+
+    pa_log_debug("Connection authenticated, creating stream ...");
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module);
+}
+
+/* Called from main context */
+static void pstream_die_callback(pa_pstream *p, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(p);
+    pa_assert(u);
+
+    pa_log_warn("Stream died.");
+    pa_module_unload_request(u->module);
+}
+
+/* Called from main context */
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(p);
+    pa_assert(packet);
+    pa_assert(u);
+
+    if (pa_pdispatch_run(u->pdispatch, packet, creds, u) < 0) {
+        pa_log("Invalid packet");
+        pa_module_unload_request(u->module);
+        return;
+    }
+}
+
+#ifndef TUNNEL_SINK
+/* Called from main context */
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(p);
+    pa_assert(chunk);
+    pa_assert(u);
+
+    if (channel != u->channel) {
+        pa_log("Recieved memory block on bad channel.");
+        pa_module_unload_request(u->module);
+        return;
+    }
+
+    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, PA_UINT_TO_PTR(seek), offset, chunk);
+
+    u->counter_delta += chunk->length;
+}
+
+#endif
+
+/* Called from main context */
+static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
+    struct userdata *u = userdata;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(sc);
+    pa_assert(u);
+    pa_assert(u->client == sc);
+
+    pa_socket_client_unref(u->client);
+    u->client = NULL;
+
+    if (!io) {
+        pa_log("Connection failed: %s", pa_cstrerror(errno));
+        pa_module_unload_request(u->module);
+        return;
+    }
+
+    u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->mempool);
+    u->pdispatch = pa_pdispatch_new(u->core->mainloop, command_table, PA_COMMAND_MAX);
+
+    pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
+    pa_pstream_set_recieve_packet_callback(u->pstream, pstream_packet_callback, u);
+#ifndef TUNNEL_SINK
+    pa_pstream_set_recieve_memblock_callback(u->pstream, pstream_memblock_callback, u);
+#endif
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
+    pa_tagstruct_put_arbitrary(t, u->auth_cookie, sizeof(u->auth_cookie));
+
+#ifdef HAVE_CREDS
+{
+    pa_creds ucred;
+
+    if (pa_iochannel_creds_supported(io))
+        pa_iochannel_creds_enable(io);
+
+    ucred.uid = getuid();
+    ucred.gid = getgid();
+
+    pa_pstream_send_tagstruct_with_creds(u->pstream, t, &ucred);
+}
+#else
+    pa_pstream_send_tagstruct(u->pstream, t);
+#endif
+
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u, NULL);
+
+    pa_log_debug("Connection established, authenticating ...");
+}
+
+#ifdef TUNNEL_SINK
+
+/* Called from main context */
+static int sink_set_volume(pa_sink *sink) {
+    struct userdata *u;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(sink);
+    u = sink->userdata;
+    pa_assert(u);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, u->device_index);
+    pa_tagstruct_put_cvolume(t, &sink->volume);
+    pa_pstream_send_tagstruct(u->pstream, t);
+
+    return 0;
+}
+
+/* Called from main context */
+static int sink_set_mute(pa_sink *sink) {
+    struct userdata *u;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(sink);
+    u = sink->userdata;
+    pa_assert(u);
+
+    if (u->version < 11)
+        return -1;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, u->device_index);
+    pa_tagstruct_put_boolean(t, !!sink->muted);
+    pa_pstream_send_tagstruct(u->pstream, t);
+
+    return 0;
+}
+
+#endif
+
+/* Called from main context */
+static int load_key(struct userdata *u, const char*fn) {
+    pa_assert(u);
+
+    u->auth_cookie_in_property = FALSE;
+
+    if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
+        pa_log_debug("Using already loaded auth cookie.");
+        pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+        u->auth_cookie_in_property = 1;
+        return 0;
+    }
+
+    if (!fn)
+        fn = PA_NATIVE_COOKIE_FILE;
+
+    if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
+        return -1;
+
+    pa_log_debug("Loading cookie from disk.");
+
+    if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
+        u->auth_cookie_in_property = TRUE;
+
+    return 0;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    char *dn = NULL;
+#ifdef TUNNEL_SINK
+    pa_sink_new_data data;
+#else
+    pa_source_new_data data;
+#endif
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->client = NULL;
+    u->pdispatch = NULL;
+    u->pstream = NULL;
+    u->server_name = NULL;
+#ifdef TUNNEL_SINK
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));;
+    u->sink = NULL;
+    u->requested_bytes = 0;
+#else
+    u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
+    u->source = NULL;
+#endif
+    u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+    u->ctag = 1;
+    u->device_index = u->channel = PA_INVALID_INDEX;
+    u->auth_cookie_in_property = FALSE;
+    u->time_event = NULL;
+    u->ignore_latency_before = 0;
+    u->transport_usec = 0;
+    u->transport_usec_valid = FALSE;
+    u->remote_suspended = u->remote_corked = FALSE;
+    u->counter = u->counter_delta = 0;
+
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+    if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
+        goto fail;
+
+    if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) {
+        pa_log("No server specified.");
+        goto fail;
+    }
+
+    ss = m->core->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification");
+        goto fail;
+    }
+
+    if (!(u->client = pa_socket_client_new_string(m->core->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
+        pa_log("Failed to connect to server '%s'", u->server_name);
+        goto fail;
+    }
+
+    pa_socket_client_set_callback(u->client, on_connection, u);
+
+#ifdef TUNNEL_SINK
+
+    if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
+
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    data.namereg_fail = TRUE;
+    pa_sink_new_data_set_name(&data, dn);
+    pa_sink_new_data_set_sample_spec(&data, &ss);
+    pa_sink_new_data_set_channel_map(&data, &map);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->sink_name), u->sink_name ? " on " : "", u->server_name);
+    pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
+    if (u->sink_name)
+        pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name);
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->userdata = u;
+    u->sink->set_state = sink_set_state;
+    u->sink->set_volume = sink_set_volume;
+    u->sink->set_mute = sink_set_mute;
+
+    u->sink->refresh_volume = u->sink->refresh_muted = FALSE;
+
+    pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0);
+
+    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+    pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+#else
+
+    if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
+        dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
+
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    data.namereg_fail = TRUE;
+    pa_source_new_data_set_name(&data, dn);
+    pa_source_new_data_set_sample_spec(&data, &ss);
+    pa_source_new_data_set_channel_map(&data, &map);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->source_name), u->source_name ? " on " : "", u->server_name);
+    pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
+    if (u->source_name)
+        pa_proplist_sets(data.proplist, "tunnel.remote.source", u->source_name);
+
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY);
+    pa_source_new_data_done(&data);
+
+    if (!u->source) {
+        pa_log("Failed to create source.");
+        goto fail;
+    }
+
+    u->source->parent.process_msg = source_process_msg;
+    u->source->set_state = source_set_state;
+    u->source->userdata = u;
+
+    pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0);
+
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+#endif
+
+    pa_xfree(dn);
+
+    u->time_event = NULL;
+
+    u->maxlength = 0;
+#ifdef TUNNEL_SINK
+    u->tlength = u->minreq = u->prebuf = 0;
+#else
+    u->fragsize = 0;
+#endif
+
+    pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
+    if (!(u->thread = pa_thread_new(thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+#ifdef TUNNEL_SINK
+    pa_sink_put(u->sink);
+#else
+    pa_source_put(u->source);
+#endif
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa_xfree(dn);
+
+    return  -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+#ifdef TUNNEL_SINK
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+#else
+    if (u->source)
+        pa_source_unlink(u->source);
+#endif
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+#ifdef TUNNEL_SINK
+    if (u->sink)
+        pa_sink_unref(u->sink);
+#else
+    if (u->source)
+        pa_source_unref(u->source);
+#endif
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->pstream) {
+        pa_pstream_unlink(u->pstream);
+        pa_pstream_unref(u->pstream);
+    }
+
+    if (u->pdispatch)
+        pa_pdispatch_unref(u->pdispatch);
+
+    if (u->client)
+        pa_socket_client_unref(u->client);
+
+    if (u->auth_cookie_in_property)
+        pa_authkey_prop_unref(m->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+
+    if (u->smoother)
+        pa_smoother_free(u->smoother);
+
+    if (u->time_event)
+        u->core->mainloop->time_free(u->time_event);
+
+#ifdef TUNNEL_SINK
+    pa_xfree(u->sink_name);
+#else
+    pa_xfree(u->source_name);
+#endif
+    pa_xfree(u->server_name);
+
+    pa_xfree(u->device_description);
+    pa_xfree(u->server_fqdn);
+    pa_xfree(u->user_name);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
new file mode 100644 (file)
index 0000000..d862c20
--- /dev/null
@@ -0,0 +1,572 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/namereg.h>
+
+#include "module-volume-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore the volume and the devices of streams");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+        "table=<filename> "
+        "restore_device=<Restore the device for each stream?> "
+        "restore_volume=<Restore the volume for each stream?>"
+);
+
+#define WHITESPACE "\n\r \t"
+#define DEFAULT_VOLUME_TABLE_FILE "volume-restore.table"
+#define SAVE_INTERVAL 10
+
+static const char* const valid_modargs[] = {
+    "table",
+    "restore_device",
+    "restore_volume",
+    NULL,
+};
+
+struct rule {
+    char* name;
+    pa_bool_t volume_is_set;
+    pa_cvolume volume;
+    char *sink, *source;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_hashmap *hashmap;
+    pa_subscription *subscription;
+    pa_hook_slot
+        *sink_input_new_hook_slot,
+        *sink_input_fixate_hook_slot,
+        *source_output_new_hook_slot;
+    pa_bool_t modified;
+    char *table_file;
+    pa_time_event *save_time_event;
+};
+
+static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
+    char *p;
+    long k;
+    unsigned i;
+
+    pa_assert(s);
+    pa_assert(v);
+
+    if (!isdigit(*s))
+        return NULL;
+
+    k = strtol(s, &p, 0);
+    if (k <= 0 || k > (long) PA_CHANNELS_MAX)
+        return NULL;
+
+    v->channels = (unsigned) k;
+
+    for (i = 0; i < v->channels; i++) {
+        p += strspn(p, WHITESPACE);
+
+        if (!isdigit(*p))
+            return NULL;
+
+        k = strtol(p, &p, 0);
+
+        if (k < (long) PA_VOLUME_MUTED)
+            return NULL;
+
+        v->values[i] = (pa_volume_t) k;
+    }
+
+    if (*p != 0)
+        return NULL;
+
+    return v;
+}
+
+static int load_rules(struct userdata *u) {
+    FILE *f;
+    int n = 0;
+    int ret = -1;
+    char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
+    char *ln = buf_name;
+
+    if (!(f = fopen(u->table_file, "r"))) {
+        if (errno == ENOENT) {
+            pa_log_info("Starting with empty ruleset.");
+            ret = 0;
+        } else
+            pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
+
+        goto finish;
+    }
+
+    pa_lock_fd(fileno(f), 1);
+
+    while (!feof(f)) {
+        struct rule *rule;
+        pa_cvolume v;
+        pa_bool_t v_is_set;
+
+        if (!fgets(ln, sizeof(buf_name), f))
+            break;
+
+        n++;
+
+        pa_strip_nl(ln);
+
+        if (ln[0] == '#')
+            continue;
+
+        if (ln == buf_name) {
+            ln = buf_volume;
+            continue;
+        }
+
+        if (ln == buf_volume) {
+            ln = buf_sink;
+            continue;
+        }
+
+        if (ln == buf_sink) {
+            ln = buf_source;
+            continue;
+        }
+
+        pa_assert(ln == buf_source);
+
+        if (buf_volume[0]) {
+            if (!parse_volume(buf_volume, &v)) {
+                pa_log("parse failure in %s:%u, stopping parsing", u->table_file, n);
+                goto finish;
+            }
+
+            v_is_set = TRUE;
+        } else
+            v_is_set = FALSE;
+
+        ln = buf_name;
+
+        if (pa_hashmap_get(u->hashmap, buf_name)) {
+            pa_log("double entry in %s:%u, ignoring", u->table_file, n);
+            continue;
+        }
+
+        rule = pa_xnew(struct rule, 1);
+        rule->name = pa_xstrdup(buf_name);
+        if ((rule->volume_is_set = v_is_set))
+            rule->volume = v;
+        rule->sink = buf_sink[0] ? pa_xstrdup(buf_sink) : NULL;
+        rule->source = buf_source[0] ? pa_xstrdup(buf_source) : NULL;
+
+        pa_hashmap_put(u->hashmap, rule->name, rule);
+    }
+
+    if (ln != buf_name) {
+        pa_log("invalid number of lines in %s.", u->table_file);
+        goto finish;
+    }
+
+    ret = 0;
+
+finish:
+    if (f) {
+        pa_lock_fd(fileno(f), 0);
+        fclose(f);
+    }
+
+    return ret;
+}
+
+static int save_rules(struct userdata *u) {
+    FILE *f;
+    int ret = -1;
+    void *state = NULL;
+    struct rule *rule;
+
+    if (!u->modified)
+        return 0;
+
+    pa_log_info("Saving rules...");
+
+    if (!(f = fopen(u->table_file, "w"))) {
+        pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
+        goto finish;
+    }
+
+    pa_lock_fd(fileno(f), 1);
+
+    while ((rule = pa_hashmap_iterate(u->hashmap, &state, NULL))) {
+        unsigned i;
+
+        fprintf(f, "%s\n", rule->name);
+
+        if (rule->volume_is_set) {
+            fprintf(f, "%u", rule->volume.channels);
+
+            for (i = 0; i < rule->volume.channels; i++)
+                fprintf(f, " %u", rule->volume.values[i]);
+        }
+
+        fprintf(f, "\n%s\n%s\n",
+                rule->sink ? rule->sink : "",
+                rule->source ? rule->source : "");
+    }
+
+    ret = 0;
+    u->modified = FALSE;
+    pa_log_debug("Successfully saved rules...");
+
+finish:
+    if (f) {
+        pa_lock_fd(fileno(f), 0);
+        fclose(f);
+    }
+
+    return ret;
+}
+
+static char* client_name(pa_client *c) {
+    char *t, *e;
+
+    if (!pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME) || !c->driver)
+        return NULL;
+
+    t = pa_sprintf_malloc("%s$%s", c->driver, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
+    t[strcspn(t, "\n\r#")] = 0;
+
+    if (!*t) {
+        pa_xfree(t);
+        return NULL;
+    }
+
+    if ((e = strrchr(t, '('))) {
+        char *k = e + 1 + strspn(e + 1, "0123456789-");
+
+        /* Dirty trick: truncate all trailing parens with numbers in
+         * between, since they are usually used to identify multiple
+         * sessions of the same application, which is something we
+         * explicitly don't want. Besides other stuff this makes xmms
+         * with esound work properly for us. */
+
+        if (*k == ')' && *(k+1) == 0)
+            *e = 0;
+    }
+
+    return t;
+}
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(tv);
+    pa_assert(u);
+
+    pa_assert(e == u->save_time_event);
+    u->core->mainloop->time_free(u->save_time_event);
+    u->save_time_event = NULL;
+
+    save_rules(u);
+}
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    struct userdata *u =  userdata;
+    pa_sink_input *si = NULL;
+    pa_source_output *so = NULL;
+    struct rule *r;
+    char *name;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
+        if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
+            return;
+
+        if (!si->client || !(name = client_name(si->client)))
+            return;
+    } else {
+        pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
+
+        if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
+            return;
+
+        if (!so->client || !(name = client_name(so->client)))
+            return;
+    }
+
+    if ((r = pa_hashmap_get(u->hashmap, name))) {
+        pa_xfree(name);
+
+        if (si) {
+
+            if (!r->volume_is_set || !pa_cvolume_equal(pa_sink_input_get_volume(si), &r->volume)) {
+                pa_log_info("Saving volume for <%s>", r->name);
+                r->volume = *pa_sink_input_get_volume(si);
+                r->volume_is_set = TRUE;
+                u->modified = TRUE;
+            }
+
+            if (!r->sink || strcmp(si->sink->name, r->sink) != 0) {
+                pa_log_info("Saving sink for <%s>", r->name);
+                pa_xfree(r->sink);
+                r->sink = pa_xstrdup(si->sink->name);
+                u->modified = TRUE;
+            }
+        } else {
+            pa_assert(so);
+
+            if (!r->source || strcmp(so->source->name, r->source) != 0) {
+                pa_log_info("Saving source for <%s>", r->name);
+                pa_xfree(r->source);
+                r->source = pa_xstrdup(so->source->name);
+                u->modified = TRUE;
+            }
+        }
+
+    } else {
+        pa_log_info("Creating new entry for <%s>", name);
+
+        r = pa_xnew(struct rule, 1);
+        r->name = name;
+
+        if (si) {
+            r->volume = *pa_sink_input_get_volume(si);
+            r->volume_is_set = TRUE;
+            r->sink = pa_xstrdup(si->sink->name);
+            r->source = NULL;
+        } else {
+            pa_assert(so);
+            r->volume_is_set = FALSE;
+            r->sink = NULL;
+            r->source = pa_xstrdup(so->source->name);
+        }
+
+        pa_hashmap_put(u->hashmap, r->name, r);
+        u->modified = TRUE;
+    }
+
+    if (u->modified && !u->save_time_event) {
+        struct timeval tv;
+        pa_gettimeofday(&tv);
+        tv.tv_sec += SAVE_INTERVAL;
+        u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
+    }
+}
+
+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
+    struct rule *r;
+    char *name;
+
+    pa_assert(data);
+
+    /* In the NEW hook we only adjust the device. Adjusting the volume
+     * is left for the FIXATE hook */
+
+    if (!data->client || !(name = client_name(data->client)))
+        return PA_HOOK_OK;
+
+    if ((r = pa_hashmap_get(u->hashmap, name))) {
+        if (!data->sink && r->sink) {
+            if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK, 1)))
+                pa_log_info("Restoring sink for <%s>", r->name);
+        }
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
+    struct rule *r;
+    char *name;
+
+    pa_assert(data);
+
+    /* In the FIXATE hook we only adjust the volum. Adjusting the device
+     * is left for the NEW hook */
+
+    if (!data->client || !(name = client_name(data->client)))
+        return PA_HOOK_OK;
+
+    if ((r = pa_hashmap_get(u->hashmap, name))) {
+
+        if (r->volume_is_set && data->sample_spec.channels == r->volume.channels) {
+            pa_log_info("Restoring volume for <%s>", r->name);
+            pa_sink_input_new_data_set_volume(data, &r->volume);
+        }
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
+    struct rule *r;
+    char *name;
+
+    pa_assert(data);
+
+    if (!data->client || !(name = client_name(data->client)))
+        return PA_HOOK_OK;
+
+    if ((r = pa_hashmap_get(u->hashmap, name))) {
+        if (!data->source && r->source) {
+            if ((data->source = pa_namereg_get(c, r->source, PA_NAMEREG_SOURCE, 1)))
+                pa_log_info("Restoring source for <%s>", r->name);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    pa_bool_t restore_device = TRUE, restore_volume = TRUE;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    u->modified = FALSE;
+    u->subscription = NULL;
+    u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;
+    u->save_time_event = NULL;
+
+    m->userdata = u;
+
+    if (!(u->table_file = pa_state_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE))))
+        goto fail;
+
+    if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
+        pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0) {
+        pa_log("restore_volume= and restore_device= expect boolean arguments");
+        goto fail;
+    }
+
+    if (!(restore_device || restore_volume)) {
+        pa_log("Both restrong the volume and restoring the device are disabled. There's no point in using this module at all then, failing.");
+        goto fail;
+    }
+
+    if (load_rules(u) < 0)
+        goto fail;
+
+    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
+
+    if (restore_device) {
+        u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
+        u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
+    }
+
+    if (restore_volume)
+        u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    pa__done(m);
+    if (ma)
+        pa_modargs_free(ma);
+
+    return  -1;
+}
+
+static void free_func(void *p, void *userdata) {
+    struct rule *r = p;
+    pa_assert(r);
+
+    pa_xfree(r->name);
+    pa_xfree(r->sink);
+    pa_xfree(r->source);
+    pa_xfree(r);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->subscription)
+        pa_subscription_free(u->subscription);
+
+    if (u->sink_input_new_hook_slot)
+        pa_hook_slot_free(u->sink_input_new_hook_slot);
+    if (u->sink_input_fixate_hook_slot)
+        pa_hook_slot_free(u->sink_input_fixate_hook_slot);
+    if (u->source_output_new_hook_slot)
+        pa_hook_slot_free(u->source_output_new_hook_slot);
+
+    if (u->hashmap) {
+        save_rules(u);
+        pa_hashmap_free(u->hashmap, free_func, NULL);
+    }
+
+    if (u->save_time_event)
+        u->core->mainloop->time_free(u->save_time_event);
+
+    pa_xfree(u->table_file);
+    pa_xfree(u);
+}
diff --git a/src/modules/module-waveout.c b/src/modules/module-waveout.c
new file mode 100644 (file)
index 0000000..b452c3b
--- /dev/null
@@ -0,0 +1,647 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <windows.h>
+#include <mmsystem.h>
+
+#include <pulse/mainloop-api.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "module-waveout-symdef.h"
+
+PA_MODULE_AUTHOR("Pierre Ossman")
+PA_MODULE_DESCRIPTION("Windows waveOut Sink/Source")
+PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_USAGE(
+    "sink_name=<name for the sink> "
+    "source_name=<name for the source> "
+    "device=<device number> "
+    "record=<enable source?> "
+    "playback=<enable sink?> "
+    "format=<sample format> "
+    "channels=<number of channels> "
+    "rate=<sample rate> "
+    "fragments=<number of fragments> "
+    "fragment_size=<fragment size> "
+    "channel_map=<channel map>")
+
+#define DEFAULT_SINK_NAME "wave_output"
+#define DEFAULT_SOURCE_NAME "wave_input"
+
+#define WAVEOUT_MAX_VOLUME 0xFFFF
+
+struct userdata {
+    pa_sink *sink;
+    pa_source *source;
+    pa_core *core;
+    pa_time_event *event;
+    pa_defer_event *defer;
+    pa_usec_t poll_timeout;
+
+    uint32_t fragments, fragment_size;
+
+    uint32_t free_ofrags, free_ifrags;
+
+    DWORD written_bytes;
+    int sink_underflow;
+
+    int cur_ohdr, cur_ihdr;
+    WAVEHDR *ohdrs, *ihdrs;
+
+    HWAVEOUT hwo;
+    HWAVEIN hwi;
+    pa_module *module;
+
+    CRITICAL_SECTION crit;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "source_name",
+    "device",
+    "record",
+    "playback",
+    "fragments",
+    "fragment_size",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    NULL
+};
+
+static void update_usage(struct userdata *u) {
+   pa_module_set_used(u->module,
+                      (u->sink ? pa_sink_used_by(u->sink) : 0) +
+                      (u->source ? pa_source_used_by(u->source) : 0));
+}
+
+static void do_write(struct userdata *u)
+{
+    uint32_t free_frags;
+    pa_memchunk memchunk;
+    WAVEHDR *hdr;
+    MMRESULT res;
+
+    if (!u->sink)
+        return;
+
+    EnterCriticalSection(&u->crit);
+    free_frags = u->free_ofrags;
+    LeaveCriticalSection(&u->crit);
+
+    if (!u->sink_underflow && (free_frags == u->fragments))
+        pa_log_debug("WaveOut underflow!");
+
+    while (free_frags) {
+        hdr = &u->ohdrs[u->cur_ohdr];
+        if (hdr->dwFlags & WHDR_PREPARED)
+            waveOutUnprepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
+
+        hdr->dwBufferLength = 0;
+        while (hdr->dwBufferLength < u->fragment_size) {
+            size_t len;
+
+            len = u->fragment_size - hdr->dwBufferLength;
+
+            if (pa_sink_render(u->sink, len, &memchunk) < 0)
+                break;
+
+            assert(memchunk.memblock);
+            assert(memchunk.memblock->data);
+            assert(memchunk.length);
+
+            if (memchunk.length < len)
+                len = memchunk.length;
+
+            memcpy(hdr->lpData + hdr->dwBufferLength,
+                (char*)memchunk.memblock->data + memchunk.index, len);
+
+            hdr->dwBufferLength += len;
+
+            pa_memblock_unref(memchunk.memblock);
+            memchunk.memblock = NULL;
+        }
+
+        /* Insufficient data in sink buffer? */
+        if (hdr->dwBufferLength == 0) {
+            u->sink_underflow = 1;
+            break;
+        }
+
+        u->sink_underflow = 0;
+
+        res = waveOutPrepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
+        if (res != MMSYSERR_NOERROR) {
+            pa_log_error(__FILE__ ": ERROR: Unable to prepare waveOut block: %d",
+                res);
+        }
+        res = waveOutWrite(u->hwo, hdr, sizeof(WAVEHDR));
+        if (res != MMSYSERR_NOERROR) {
+            pa_log_error(__FILE__ ": ERROR: Unable to write waveOut block: %d",
+                res);
+        }
+
+        u->written_bytes += hdr->dwBufferLength;
+
+        EnterCriticalSection(&u->crit);
+        u->free_ofrags--;
+        LeaveCriticalSection(&u->crit);
+
+        free_frags--;
+        u->cur_ohdr++;
+        u->cur_ohdr %= u->fragments;
+    }
+}
+
+static void do_read(struct userdata *u)
+{
+    uint32_t free_frags;
+    pa_memchunk memchunk;
+    WAVEHDR *hdr;
+    MMRESULT res;
+
+    if (!u->source)
+        return;
+
+    EnterCriticalSection(&u->crit);
+
+    free_frags = u->free_ifrags;
+    u->free_ifrags = 0;
+
+    LeaveCriticalSection(&u->crit);
+
+    if (free_frags == u->fragments)
+        pa_log_debug("WaveIn overflow!");
+
+    while (free_frags) {
+        hdr = &u->ihdrs[u->cur_ihdr];
+        if (hdr->dwFlags & WHDR_PREPARED)
+            waveInUnprepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
+
+        if (hdr->dwBytesRecorded) {
+            memchunk.memblock = pa_memblock_new(u->core->mempool, hdr->dwBytesRecorded);
+            assert(memchunk.memblock);
+
+            memcpy((char*)memchunk.memblock->data, hdr->lpData, hdr->dwBytesRecorded);
+
+            memchunk.length = memchunk.memblock->length = hdr->dwBytesRecorded;
+            memchunk.index = 0;
+
+            pa_source_post(u->source, &memchunk);
+            pa_memblock_unref(memchunk.memblock);
+        }
+
+        res = waveInPrepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
+        if (res != MMSYSERR_NOERROR) {
+            pa_log_error(__FILE__ ": ERROR: Unable to prepare waveIn block: %d",
+                res);
+        }
+        res = waveInAddBuffer(u->hwi, hdr, sizeof(WAVEHDR));
+        if (res != MMSYSERR_NOERROR) {
+            pa_log_error(__FILE__ ": ERROR: Unable to add waveIn block: %d",
+                res);
+        }
+
+        free_frags--;
+        u->cur_ihdr++;
+        u->cur_ihdr %= u->fragments;
+    }
+}
+
+static void poll_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+    struct timeval ntv;
+
+    assert(u);
+
+    update_usage(u);
+
+    do_write(u);
+    do_read(u);
+
+    pa_gettimeofday(&ntv);
+    pa_timeval_add(&ntv, u->poll_timeout);
+
+    a->time_restart(e, &ntv);
+}
+
+static void defer_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
+    struct userdata *u = userdata;
+
+    assert(u);
+
+    a->defer_enable(e, 0);
+
+    do_write(u);
+    do_read(u);
+}
+
+static void CALLBACK chunk_done_cb(HWAVEOUT hwo, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) {
+    struct userdata *u = (struct userdata *)inst;
+
+    if (msg != WOM_DONE)
+        return;
+
+    EnterCriticalSection(&u->crit);
+
+    u->free_ofrags++;
+    assert(u->free_ofrags <= u->fragments);
+
+    LeaveCriticalSection(&u->crit);
+}
+
+static void CALLBACK chunk_ready_cb(HWAVEIN hwi, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) {
+    struct userdata *u = (struct userdata *)inst;
+
+    if (msg != WIM_DATA)
+        return;
+
+    EnterCriticalSection(&u->crit);
+
+    u->free_ifrags++;
+    assert(u->free_ifrags <= u->fragments);
+
+    LeaveCriticalSection(&u->crit);
+}
+
+static pa_usec_t sink_get_latency_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    uint32_t free_frags;
+    MMTIME mmt;
+    assert(s && u && u->sink);
+
+    memset(&mmt, 0, sizeof(mmt));
+    mmt.wType = TIME_BYTES;
+    if (waveOutGetPosition(u->hwo, &mmt, sizeof(mmt)) == MMSYSERR_NOERROR)
+        return pa_bytes_to_usec(u->written_bytes - mmt.u.cb, &s->sample_spec);
+    else {
+        EnterCriticalSection(&u->crit);
+
+        free_frags = u->free_ofrags;
+
+        LeaveCriticalSection(&u->crit);
+
+        return pa_bytes_to_usec((u->fragments - free_frags) * u->fragment_size,
+                              &s->sample_spec);
+    }
+}
+
+static pa_usec_t source_get_latency_cb(pa_source *s) {
+    pa_usec_t r = 0;
+    struct userdata *u = s->userdata;
+    uint32_t free_frags;
+    assert(s && u && u->sink);
+
+    EnterCriticalSection(&u->crit);
+
+    free_frags = u->free_ifrags;
+
+    LeaveCriticalSection(&u->crit);
+
+    r += pa_bytes_to_usec((free_frags + 1) * u->fragment_size, &s->sample_spec);
+
+    return r;
+}
+
+static void notify_sink_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    assert(u);
+
+    u->core->mainloop->defer_enable(u->defer, 1);
+}
+
+static void notify_source_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    assert(u);
+
+    u->core->mainloop->defer_enable(u->defer, 1);
+}
+
+static int sink_get_hw_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    DWORD vol;
+    pa_volume_t left, right;
+
+    if (waveOutGetVolume(u->hwo, &vol) != MMSYSERR_NOERROR)
+        return -1;
+
+    left = (vol & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME;
+    right = ((vol >> 16) & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME;
+
+    /* Windows supports > 2 channels, except for volume control */
+    if (s->hw_volume.channels > 2)
+        pa_cvolume_set(&s->hw_volume, s->hw_volume.channels, (left + right)/2);
+
+    s->hw_volume.values[0] = left;
+    if (s->hw_volume.channels > 1)
+        s->hw_volume.values[1] = right;
+
+    return 0;
+}
+
+static int sink_set_hw_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    DWORD vol;
+
+    vol = s->hw_volume.values[0] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM;
+    if (s->hw_volume.channels > 1)
+        vol |= (s->hw_volume.values[0] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM) << 16;
+
+    if (waveOutSetVolume(u->hwo, vol) != MMSYSERR_NOERROR)
+        return -1;
+
+    return 0;
+}
+
+static int ss_to_waveformat(pa_sample_spec *ss, LPWAVEFORMATEX wf) {
+    wf->wFormatTag = WAVE_FORMAT_PCM;
+
+    if (ss->channels > 2) {
+        pa_log_error("ERROR: More than two channels not supported.");
+        return -1;
+    }
+
+    wf->nChannels = ss->channels;
+
+    switch (ss->rate) {
+    case 8000:
+    case 11025:
+    case 22005:
+    case 44100:
+        break;
+    default:
+        pa_log_error("ERROR: Unsupported sample rate.");
+        return -1;
+    }
+
+    wf->nSamplesPerSec = ss->rate;
+
+    if (ss->format == PA_SAMPLE_U8)
+        wf->wBitsPerSample = 8;
+    else if (ss->format == PA_SAMPLE_S16NE)
+        wf->wBitsPerSample = 16;
+    else {
+        pa_log_error("ERROR: Unsupported sample format.");
+        return -1;
+    }
+
+    wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample/8;
+    wf->nAvgBytesPerSec = wf->nSamplesPerSec * wf->nBlockAlign;
+
+    wf->cbSize = 0;
+
+    return 0;
+}
+
+int pa__init(pa_core *c, pa_module*m) {
+    struct userdata *u = NULL;
+    HWAVEOUT hwo = INVALID_HANDLE_VALUE;
+    HWAVEIN hwi = INVALID_HANDLE_VALUE;
+    WAVEFORMATEX wf;
+    int nfrags, frag_size;
+    int record = 1, playback = 1;
+    unsigned int device;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma = NULL;
+    unsigned int i;
+    struct timeval tv;
+
+    assert(c && m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
+        pa_log("record= and playback= expect boolean argument.");
+        goto fail;
+    }
+
+    if (!playback && !record) {
+        pa_log("neither playback nor record enabled for device.");
+        goto fail;
+    }
+
+    device = WAVE_MAPPER;
+    if (pa_modargs_get_value_u32(ma, "device", &device) < 0) {
+        pa_log("failed to parse device argument");
+        goto fail;
+    }
+
+    nfrags = 5;
+    frag_size = 8192;
+    if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
+        pa_log("failed to parse fragments arguments");
+        goto fail;
+    }
+
+    ss = c->default_sample_spec;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_WAVEEX) < 0) {
+        pa_log("failed to parse sample specification");
+        goto fail;
+    }
+
+    if (ss_to_waveformat(&ss, &wf) < 0)
+        goto fail;
+
+    u = pa_xmalloc(sizeof(struct userdata));
+
+    if (record) {
+        if (waveInOpen(&hwi, device, &wf, (DWORD_PTR)chunk_ready_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+            pa_log("failed to open waveIn");
+            goto fail;
+        }
+        if (waveInStart(hwi) != MMSYSERR_NOERROR) {
+            pa_log("failed to start waveIn");
+            goto fail;
+        }
+        pa_log_debug("Opened waveIn subsystem.");
+    }
+
+    if (playback) {
+        if (waveOutOpen(&hwo, device, &wf, (DWORD_PTR)chunk_done_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+            pa_log("failed to open waveOut");
+            goto fail;
+        }
+        pa_log_debug("Opened waveOut subsystem.");
+    }
+
+    InitializeCriticalSection(&u->crit);
+
+    if (hwi != INVALID_HANDLE_VALUE) {
+        u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map);
+        assert(u->source);
+        u->source->userdata = u;
+        u->source->notify = notify_source_cb;
+        u->source->get_latency = source_get_latency_cb;
+        pa_source_set_owner(u->source, m);
+        pa_source_set_description(u->source, "Windows waveIn PCM");
+        u->source->is_hardware = 1;
+    } else
+        u->source = NULL;
+
+    if (hwo != INVALID_HANDLE_VALUE) {
+        u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map);
+        assert(u->sink);
+        u->sink->notify = notify_sink_cb;
+        u->sink->get_latency = sink_get_latency_cb;
+        u->sink->get_hw_volume = sink_get_hw_volume_cb;
+        u->sink->set_hw_volume = sink_set_hw_volume_cb;
+        u->sink->userdata = u;
+        pa_sink_set_owner(u->sink, m);
+        pa_sink_set_description(u->sink, "Windows waveOut PCM");
+        u->sink->is_hardware = 1;
+    } else
+        u->sink = NULL;
+
+    assert(u->source || u->sink);
+
+    u->core = c;
+    u->hwi = hwi;
+    u->hwo = hwo;
+
+    u->fragments = nfrags;
+    u->free_ifrags = u->fragments;
+    u->free_ofrags = u->fragments;
+    u->fragment_size = frag_size - (frag_size % pa_frame_size(&ss));
+
+    u->written_bytes = 0;
+    u->sink_underflow = 1;
+
+    u->poll_timeout = pa_bytes_to_usec(u->fragments * u->fragment_size / 10, &ss);
+
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, u->poll_timeout);
+
+    u->event = c->mainloop->time_new(c->mainloop, &tv, poll_cb, u);
+    assert(u->event);
+
+    u->defer = c->mainloop->defer_new(c->mainloop, defer_cb, u);
+    assert(u->defer);
+    c->mainloop->defer_enable(u->defer, 0);
+
+    u->cur_ihdr = 0;
+    u->cur_ohdr = 0;
+    u->ihdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
+    assert(u->ihdrs);
+    u->ohdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
+    assert(u->ohdrs);
+    for (i = 0;i < u->fragments;i++) {
+        u->ihdrs[i].dwBufferLength = u->fragment_size;
+        u->ohdrs[i].dwBufferLength = u->fragment_size;
+        u->ihdrs[i].lpData = pa_xmalloc(u->fragment_size);
+        assert(u->ihdrs);
+        u->ohdrs[i].lpData = pa_xmalloc(u->fragment_size);
+        assert(u->ohdrs);
+    }
+
+    u->module = m;
+    m->userdata = u;
+
+    pa_modargs_free(ma);
+
+    /* Read mixer settings */
+    if (u->sink)
+        sink_get_hw_volume_cb(u->sink);
+
+    return 0;
+
+fail:
+   if (hwi != INVALID_HANDLE_VALUE)
+        waveInClose(hwi);
+
+   if (hwo != INVALID_HANDLE_VALUE)
+        waveOutClose(hwo);
+
+    if (u)
+        pa_xfree(u);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_core *c, pa_module*m) {
+    struct userdata *u;
+    unsigned int i;
+
+    assert(c && m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->event)
+        c->mainloop->time_free(u->event);
+
+    if (u->defer)
+        c->mainloop->defer_free(u->defer);
+
+    if (u->sink) {
+        pa_sink_disconnect(u->sink);
+        pa_sink_unref(u->sink);
+    }
+
+    if (u->source) {
+        pa_source_disconnect(u->source);
+        pa_source_unref(u->source);
+    }
+
+    if (u->hwi != INVALID_HANDLE_VALUE) {
+        waveInReset(u->hwi);
+        waveInClose(u->hwi);
+    }
+
+    if (u->hwo != INVALID_HANDLE_VALUE) {
+        waveOutReset(u->hwo);
+        waveOutClose(u->hwo);
+    }
+
+    for (i = 0;i < u->fragments;i++) {
+        pa_xfree(u->ihdrs[i].lpData);
+        pa_xfree(u->ohdrs[i].lpData);
+    }
+
+    pa_xfree(u->ihdrs);
+    pa_xfree(u->ohdrs);
+
+    DeleteCriticalSection(&u->crit);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c
new file mode 100644 (file)
index 0000000..f7be48f
--- /dev/null
@@ -0,0 +1,190 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+
+#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/iochannel.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/x11wrap.h>
+
+#include "module-x11-bell-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 bell interceptor");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("sink=<sink to connect to> sample=<sample name> display=<X11 display>");
+
+static const char* const valid_modargs[] = {
+    "sink",
+    "sample",
+    "display",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    int xkb_event_base;
+
+    char *sink_name;
+    char *scache_item;
+
+    pa_x11_wrapper *x11_wrapper;
+    pa_x11_client *x11_client;
+};
+
+static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) {
+    XkbBellNotifyEvent *bne;
+    struct userdata *u = userdata;
+
+    pa_assert(w);
+    pa_assert(e);
+    pa_assert(u);
+    pa_assert(u->x11_wrapper == w);
+
+    if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify)
+        return 0;
+
+    bne = (XkbBellNotifyEvent*) e;
+
+    if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, TRUE, (bne->percent*PA_VOLUME_NORM)/100, NULL, NULL) < 0) {
+        pa_log_info("Ringing bell failed, reverting to X11 device bell.");
+        XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);
+    }
+
+    return 1;
+}
+
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(w);
+    pa_assert(u);
+    pa_assert(u->x11_wrapper == w);
+
+    if (u->x11_client)
+        pa_x11_client_free(u->x11_client);
+
+    if (u->x11_wrapper)
+        pa_x11_wrapper_unref(u->x11_wrapper);
+
+    u->x11_client = NULL;
+    u->x11_wrapper = NULL;
+
+    pa_module_unload_request(u->module);
+}
+
+int pa__init(pa_module*m) {
+
+    struct userdata *u = NULL;
+    pa_modargs *ma = NULL;
+    int major, minor;
+    unsigned int auto_ctrls, auto_values;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "bell-window-system"));
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+    u->x11_client = NULL;
+
+    if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+        goto fail;
+
+    major = XkbMajorVersion;
+    minor = XkbMinorVersion;
+
+    if (!XkbLibraryVersion(&major, &minor)) {
+        pa_log("XkbLibraryVersion() failed");
+        goto fail;
+    }
+
+    major = XkbMajorVersion;
+    minor = XkbMinorVersion;
+
+    if (!XkbQueryExtension(pa_x11_wrapper_get_display(u->x11_wrapper), NULL, &u->xkb_event_base, NULL, &major, &minor)) {
+        pa_log("XkbQueryExtension() failed");
+        goto fail;
+    }
+
+    XkbSelectEvents(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask);
+    auto_ctrls = auto_values = XkbAudibleBellMask;
+    XkbSetAutoResetControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbAudibleBellMask, &auto_ctrls, &auto_values);
+    XkbChangeEnabledControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbAudibleBellMask, 0);
+
+    u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_cb, x11_kill_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    pa_xfree(u->scache_item);
+    pa_xfree(u->sink_name);
+
+    if (u->x11_client)
+        pa_x11_client_free(u->x11_client);
+
+    if (u->x11_wrapper)
+        pa_x11_wrapper_unref(u->x11_wrapper);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-x11-publish.c b/src/modules/module-x11-publish.c
new file mode 100644 (file)
index 0000000..705d90f
--- /dev/null
@@ -0,0 +1,228 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+#include <unistd.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/x11wrap.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/authkey-prop.h>
+#include <pulsecore/authkey.h>
+#include <pulsecore/x11prop.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/props.h>
+
+#include "module-x11-publish-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 credential publisher");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE("display=<X11 display>");
+
+static const char* const valid_modargs[] = {
+    "display",
+    "sink",
+    "source",
+    "cookie",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    char *id;
+    uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
+    pa_bool_t auth_cookie_in_property;
+
+    pa_x11_wrapper *x11_wrapper;
+    pa_x11_client *x11_client;
+};
+
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(w);
+    pa_assert(u);
+    pa_assert(u->x11_wrapper == w);
+
+    if (u->x11_client)
+        pa_x11_client_free(u->x11_client);
+
+    if (u->x11_wrapper)
+        pa_x11_wrapper_unref(u->x11_wrapper);
+
+    u->x11_client = NULL;
+    u->x11_wrapper = NULL;
+
+    pa_module_unload_request(u->module);
+}
+
+static int load_key(struct userdata *u, const char*fn) {
+    pa_assert(u);
+
+    u->auth_cookie_in_property = FALSE;
+
+    if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
+        pa_log_debug("using already loaded auth cookie.");
+        pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+        u->auth_cookie_in_property = 1;
+        return 0;
+    }
+
+    if (!fn)
+        fn = PA_NATIVE_COOKIE_FILE;
+
+    if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
+        return -1;
+
+    pa_log_debug("Loading cookie from disk.");
+
+    if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
+        u->auth_cookie_in_property = TRUE;
+
+    return 0;
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    char hn[256], un[128];
+    char hx[PA_NATIVE_COOKIE_LENGTH*2+1];
+    const char *t;
+    char *s;
+    pa_strlist *l;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->id = NULL;
+    u->auth_cookie_in_property = FALSE;
+    u->x11_client = NULL;
+    u->x11_wrapper = NULL;
+
+    if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
+        goto fail;
+
+    if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+        goto fail;
+
+    if (!(l = pa_property_get(m->core, PA_NATIVE_SERVER_PROPERTY_NAME)))
+        goto fail;
+
+    l = pa_strlist_reverse(l);
+    s = pa_strlist_tostring(l);
+    l = pa_strlist_reverse(l);
+
+    pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER", s);
+    pa_xfree(s);
+
+    if (!pa_get_fqdn(hn, sizeof(hn)) || !pa_get_user_name(un, sizeof(un)))
+        goto fail;
+
+    u->id = pa_sprintf_malloc("%s@%s/%u", un, hn, (unsigned) getpid());
+    pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID", u->id);
+
+    if ((t = pa_modargs_get_value(ma, "source", NULL)))
+        pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SOURCE", t);
+
+    if ((t = pa_modargs_get_value(ma, "sink", NULL)))
+        pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SINK", t);
+
+    pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE", pa_hexstr(u->auth_cookie, sizeof(u->auth_cookie), hx, sizeof(hx)));
+
+    u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata*u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->x11_client)
+        pa_x11_client_free(u->x11_client);
+
+    if (u->x11_wrapper) {
+        char t[256];
+
+        /* Yes, here is a race condition */
+        if (!pa_x11_get_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID", t, sizeof(t)) || strcmp(t, u->id))
+            pa_log_warn("PulseAudio information vanished from X11!");
+        else {
+            pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID");
+            pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER");
+            pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SINK");
+            pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SOURCE");
+            pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE");
+            XSync(pa_x11_wrapper_get_display(u->x11_wrapper), False);
+        }
+
+        pa_x11_wrapper_unref(u->x11_wrapper);
+    }
+
+    if (u->auth_cookie_in_property)
+        pa_authkey_prop_unref(m->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+
+    pa_xfree(u->id);
+    pa_xfree(u);
+}
diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c
new file mode 100644 (file)
index 0000000..696826d
--- /dev/null
@@ -0,0 +1,247 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+
+#include <X11/Xlib.h>
+#include <X11/SM/SMlib.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/iochannel.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/x11wrap.h>
+
+#include "module-x11-xsmp-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("X11 session management");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("session_manager=<session manager string> display=<X11 display>");
+
+static pa_bool_t ice_in_use = FALSE;
+
+static const char* const valid_modargs[] = {
+    "session_manager",
+    "display",
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_client *client;
+    SmcConn connection;
+    pa_x11_wrapper *x11;
+};
+
+static void die_cb(SmcConn connection, SmPointer client_data){
+    struct userdata *u = client_data;
+    pa_assert(u);
+
+    pa_log_debug("Got die message from XSMP.");
+
+    pa_x11_wrapper_kill(u->x11);
+
+    pa_x11_wrapper_unref(u->x11);
+    u->x11 = NULL;
+
+    pa_module_unload_request(u->module);
+}
+
+static void save_complete_cb(SmcConn connection, SmPointer client_data) {
+}
+
+static void shutdown_cancelled_cb(SmcConn connection, SmPointer client_data) {
+    SmcSaveYourselfDone(connection, True);
+}
+
+static void save_yourself_cb(SmcConn connection, SmPointer client_data, int save_type, Bool _shutdown, int interact_style, Bool fast) {
+    SmcSaveYourselfDone(connection, True);
+}
+
+static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
+    IceConn connection = userdata;
+
+    if (IceProcessMessages(connection, NULL, NULL) == IceProcessMessagesIOError) {
+        IceSetShutdownNegotiation(connection, False);
+        IceCloseConnection(connection);
+    }
+}
+
+static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) {
+    struct pa_core *c = client_data;
+
+    if (opening)
+        *watch_data = c->mainloop->io_new(
+                c->mainloop,
+                IceConnectionNumber(connection),
+                PA_IO_EVENT_INPUT,
+                ice_io_cb,
+                connection);
+    else
+        c->mainloop->io_free(*watch_data);
+}
+
+int pa__init(pa_module*m) {
+
+    pa_modargs *ma = NULL;
+    char t[256], *vendor, *client_id, *k;
+    SmcCallbacks callbacks;
+    SmProp prop_program, prop_user;
+    SmProp *prop_list[2];
+    SmPropValue val_program, val_user;
+    struct userdata *u;
+    const char *e;
+
+    pa_assert(m);
+
+    if (ice_in_use) {
+        pa_log("module-x11-xsmp may no be loaded twice.");
+        return -1;
+    }
+
+    IceAddConnectionWatch(new_ice_connection, m->core);
+    ice_in_use = TRUE;
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->client = NULL;
+    u->connection = NULL;
+    u->x11 = NULL;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (!(u->x11 = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+        goto fail;
+
+    e = pa_modargs_get_value(ma, "session_manager", NULL);
+
+    if (!e && !getenv("SESSION_MANAGER")) {
+        pa_log("X11 session manager not running.");
+        goto fail;
+    }
+
+    memset(&callbacks, 0, sizeof(callbacks));
+    callbacks.die.callback = die_cb;
+    callbacks.die.client_data = u;
+    callbacks.save_yourself.callback = save_yourself_cb;
+    callbacks.save_yourself.client_data = m->core;
+    callbacks.save_complete.callback = save_complete_cb;
+    callbacks.save_complete.client_data = m->core;
+    callbacks.shutdown_cancelled.callback = shutdown_cancelled_cb;
+    callbacks.shutdown_cancelled.client_data = m->core;
+
+    if (!(u->connection = SmcOpenConnection(
+                  (char*) e, m->core,
+                  SmProtoMajor, SmProtoMinor,
+                  SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
+                  &callbacks, NULL, &client_id,
+                  sizeof(t), t))) {
+
+        pa_log("Failed to open connection to session manager: %s", t);
+        goto fail;
+    }
+
+    prop_program.name = (char*) SmProgram;
+    prop_program.type = (char*) SmARRAY8;
+    val_program.value = (char*) PACKAGE_NAME;
+    val_program.length = strlen(val_program.value);
+    prop_program.num_vals = 1;
+    prop_program.vals = &val_program;
+    prop_list[0] = &prop_program;
+
+    prop_user.name = (char*) SmUserID;
+    prop_user.type = (char*) SmARRAY8;
+    pa_get_user_name(t, sizeof(t));
+    val_user.value = t;
+    val_user.length = strlen(val_user.value);
+    prop_user.num_vals = 1;
+    prop_user.vals = &val_user;
+    prop_list[1] = &prop_user;
+
+    SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list);
+
+    pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id);
+    k = pa_sprintf_malloc("XSMP Session on %s as %s", vendor, client_id);
+    u->client = pa_client_new(u->core, __FILE__, k);
+    pa_xfree(k);
+
+    pa_proplist_sets(u->client->proplist, "xsmp.vendor", vendor);
+    pa_proplist_sets(u->client->proplist, "xsmp.client.id", client_id);
+
+    free(vendor);
+    free(client_id);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if ((u = m->userdata)) {
+
+        if (u->connection)
+            SmcCloseConnection(u->connection, 0, NULL);
+
+        if (u->client)
+            pa_client_free(u->client);
+
+        if (u->x11)
+            pa_x11_wrapper_unref(u->x11);
+
+        pa_xfree(u);
+    }
+
+    if (ice_in_use) {
+        IceRemoveConnectionWatch(new_ice_connection, m->core);
+        ice_in_use = FALSE;
+    }
+}
diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c
new file mode 100644 (file)
index 0000000..2fc8137
--- /dev/null
@@ -0,0 +1,438 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <string.h>
+#include <unistd.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/avahi-wrap.h>
+
+#include "module-zeroconf-discover-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SERVICE_TYPE_SINK "_pulse-sink._tcp"
+#define SERVICE_TYPE_SOURCE "_non-monitor._sub._pulse-source._tcp"
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct tunnel {
+    AvahiIfIndex interface;
+    AvahiProtocol protocol;
+    char *name, *type, *domain;
+    uint32_t module_index;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    AvahiPoll *avahi_poll;
+    AvahiClient *client;
+    AvahiServiceBrowser *source_browser, *sink_browser;
+
+    pa_hashmap *tunnels;
+};
+
+static unsigned tunnel_hash(const void *p) {
+    const struct tunnel *t = p;
+
+    return
+        (unsigned) t->interface +
+        (unsigned) t->protocol +
+        pa_idxset_string_hash_func(t->name) +
+        pa_idxset_string_hash_func(t->type) +
+        pa_idxset_string_hash_func(t->domain);
+}
+
+static int tunnel_compare(const void *a, const void *b) {
+    const struct tunnel *ta = a, *tb = b;
+    int r;
+
+    if (ta->interface != tb->interface)
+        return 1;
+    if (ta->protocol != tb->protocol)
+        return 1;
+    if ((r = strcmp(ta->name, tb->name)))
+        return r;
+    if ((r = strcmp(ta->type, tb->type)))
+        return r;
+    if ((r = strcmp(ta->domain, tb->domain)))
+        return r;
+
+    return 0;
+}
+
+static struct tunnel *tunnel_new(
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        const char *name, const char *type, const char *domain) {
+
+    struct tunnel *t;
+    t = pa_xnew(struct tunnel, 1);
+    t->interface = interface;
+    t->protocol = protocol;
+    t->name = pa_xstrdup(name);
+    t->type = pa_xstrdup(type);
+    t->domain = pa_xstrdup(domain);
+    t->module_index = PA_IDXSET_INVALID;
+    return t;
+}
+
+static void tunnel_free(struct tunnel *t) {
+    pa_assert(t);
+    pa_xfree(t->name);
+    pa_xfree(t->type);
+    pa_xfree(t->domain);
+    pa_xfree(t);
+}
+
+static void resolver_cb(
+        AvahiServiceResolver *r,
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        AvahiResolverEvent event,
+        const char *name, const char *type, const char *domain,
+        const char *host_name, const AvahiAddress *a, uint16_t port,
+        AvahiStringList *txt,
+        AvahiLookupResultFlags flags,
+        void *userdata) {
+
+    struct userdata *u = userdata;
+    struct tunnel *tnl;
+
+    pa_assert(u);
+
+    tnl = tunnel_new(interface, protocol, name, type, domain);
+
+    if (event != AVAHI_RESOLVER_FOUND)
+        pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
+    else {
+        char *device = NULL, *dname, *module_name, *args;
+        const char *t;
+        char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
+        pa_sample_spec ss;
+        pa_channel_map cm;
+        AvahiStringList *l;
+        pa_bool_t channel_map_set = FALSE;
+        pa_module *m;
+
+        ss = u->core->default_sample_spec;
+        pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+        for (l = txt; l; l = l->next) {
+            char *key, *value;
+            pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);
+
+            if (strcmp(key, "device") == 0) {
+                pa_xfree(device);
+                device = value;
+                value = NULL;
+            } else if (strcmp(key, "rate") == 0)
+                ss.rate = atoi(value);
+            else if (strcmp(key, "channels") == 0)
+                ss.channels = atoi(value);
+            else if (strcmp(key, "format") == 0)
+                ss.format = pa_parse_sample_format(value);
+            else if (strcmp(key, "channel_map") == 0) {
+                pa_channel_map_parse(&cm, value);
+                channel_map_set = TRUE;
+            }
+
+            avahi_free(key);
+            avahi_free(value);
+        }
+
+        if (!channel_map_set && cm.channels != ss.channels)
+            pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+        if (!pa_sample_spec_valid(&ss)) {
+            pa_log("Service '%s' contains an invalid sample specification.", name);
+            avahi_free(device);
+            goto finish;
+        }
+
+        if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) {
+            pa_log("Service '%s' contains an invalid channel map.", name);
+            avahi_free(device);
+            goto finish;
+        }
+
+        if (device)
+            dname = pa_sprintf_malloc("tunnel.%s.%s", host_name, device);
+        else
+            dname = pa_sprintf_malloc("tunnel.%s", host_name);
+
+        if (!pa_namereg_is_valid_name(dname)) {
+            pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
+            avahi_free(device);
+            pa_xfree(dname);
+            goto finish;
+        }
+
+        t = strstr(type, "sink") ? "sink" : "source";
+
+        module_name = pa_sprintf_malloc("module-tunnel-%s", t);
+        args = pa_sprintf_malloc("server=[%s]:%u "
+                                 "%s=%s "
+                                 "format=%s "
+                                 "channels=%u "
+                                 "rate=%u "
+                                 "%s_name=%s "
+                                 "channel_map=%s",
+                                 avahi_address_snprint(at, sizeof(at), a), port,
+                                 t, device,
+                                 pa_sample_format_to_string(ss.format),
+                                 ss.channels,
+                                 ss.rate,
+                                 t, dname,
+                                 pa_channel_map_snprint(cmt, sizeof(cmt), &cm));
+
+        pa_log_debug("Loading %s with arguments '%s'", module_name, args);
+
+        if ((m = pa_module_load(u->core, module_name, args))) {
+            tnl->module_index = m->index;
+            pa_hashmap_put(u->tunnels, tnl, tnl);
+            tnl = NULL;
+        }
+
+        pa_xfree(module_name);
+        pa_xfree(dname);
+        pa_xfree(args);
+        avahi_free(device);
+    }
+
+finish:
+
+    avahi_service_resolver_free(r);
+
+    if (tnl)
+        tunnel_free(tnl);
+}
+
+static void browser_cb(
+        AvahiServiceBrowser *b,
+        AvahiIfIndex interface, AvahiProtocol protocol,
+        AvahiBrowserEvent event,
+        const char *name, const char *type, const char *domain,
+        AvahiLookupResultFlags flags,
+        void *userdata) {
+
+    struct userdata *u = userdata;
+    struct tunnel *t;
+
+    pa_assert(u);
+
+    if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
+        return;
+
+    t = tunnel_new(interface, protocol, name, type, domain);
+
+    if (event == AVAHI_BROWSER_NEW) {
+
+        if (!pa_hashmap_get(u->tunnels, t))
+            if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u)))
+                pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+
+        /* We ignore the returned resolver object here, since the we don't
+         * need to attach any special data to it, and we can still destory
+         * it from the callback */
+
+    } else if (event == AVAHI_BROWSER_REMOVE) {
+        struct tunnel *t2;
+
+        if ((t2 = pa_hashmap_get(u->tunnels, t))) {
+            pa_module_unload_by_index(u->core, t2->module_index);
+            pa_hashmap_remove(u->tunnels, t2);
+            tunnel_free(t2);
+        }
+    }
+
+    tunnel_free(t);
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    u->client = c;
+
+    switch (state) {
+        case AVAHI_CLIENT_S_REGISTERING:
+        case AVAHI_CLIENT_S_RUNNING:
+        case AVAHI_CLIENT_S_COLLISION:
+
+            if (!u->sink_browser) {
+
+                if (!(u->sink_browser = avahi_service_browser_new(
+                              c,
+                              AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                              SERVICE_TYPE_SINK,
+                              NULL,
+                              0,
+                              browser_cb, u))) {
+
+                    pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+                    pa_module_unload_request(u->module);
+                }
+            }
+
+            if (!u->source_browser) {
+
+                if (!(u->source_browser = avahi_service_browser_new(
+                              c,
+                              AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                              SERVICE_TYPE_SOURCE,
+                              NULL,
+                              0,
+                              browser_cb, u))) {
+
+                    pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+                    pa_module_unload_request(u->module);
+                }
+            }
+
+            break;
+
+        case AVAHI_CLIENT_FAILURE:
+            if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+                int error;
+
+                pa_log_debug("Avahi daemon disconnected.");
+
+                if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+                    pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+                    pa_module_unload_request(u->module);
+                }
+            }
+
+            /* Fall through */
+
+        case AVAHI_CLIENT_CONNECTING:
+
+            if (u->sink_browser) {
+                avahi_service_browser_free(u->sink_browser);
+                u->sink_browser = NULL;
+            }
+
+            if (u->source_browser) {
+                avahi_service_browser_free(u->source_browser);
+                u->source_browser = NULL;
+            }
+
+            break;
+
+        default: ;
+    }
+}
+
+int pa__init(pa_module*m) {
+
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    int error;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->sink_browser = u->source_browser = NULL;
+
+    u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare);
+
+    u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
+
+    if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+        pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
+        goto fail;
+    }
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata*u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->client)
+        avahi_client_free(u->client);
+
+    if (u->avahi_poll)
+        pa_avahi_poll_free(u->avahi_poll);
+
+    if (u->tunnels) {
+        struct tunnel *t;
+
+        while ((t = pa_hashmap_steal_first(u->tunnels))) {
+            pa_module_unload_by_index(u->core, t->module_index);
+            tunnel_free(t);
+        }
+
+        pa_hashmap_free(u->tunnels, NULL, NULL);
+    }
+
+    pa_xfree(u);
+}
diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c
new file mode 100644 (file)
index 0000000..cb9c128
--- /dev/null
@@ -0,0 +1,648 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <string.h>
+#include <unistd.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/dynarray.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/avahi-wrap.h>
+#include <pulsecore/endianmacros.h>
+
+#include "module-zeroconf-publish-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("port=<IP port number>");
+
+#define SERVICE_TYPE_SINK "_pulse-sink._tcp"
+#define SERVICE_TYPE_SOURCE "_pulse-source._tcp"
+#define SERVICE_TYPE_SERVER "_pulse-server._tcp"
+#define SERVICE_SUBTYPE_SINK_HARDWARE "_hardware._sub."SERVICE_TYPE_SINK
+#define SERVICE_SUBTYPE_SINK_VIRTUAL "_virtual._sub."SERVICE_TYPE_SINK
+#define SERVICE_SUBTYPE_SOURCE_HARDWARE "_hardware._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_VIRTUAL "_virtual._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_MONITOR "_monitor._sub."SERVICE_TYPE_SOURCE
+#define SERVICE_SUBTYPE_SOURCE_NON_MONITOR "_non-monitor._sub."SERVICE_TYPE_SOURCE
+
+static const char* const valid_modargs[] = {
+    "port",
+    NULL
+};
+
+enum service_subtype {
+    SUBTYPE_HARDWARE,
+    SUBTYPE_VIRTUAL,
+    SUBTYPE_MONITOR
+};
+
+struct service {
+    struct userdata *userdata;
+    AvahiEntryGroup *entry_group;
+    char *service_name;
+    pa_object *device;
+    enum service_subtype subtype;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    AvahiPoll *avahi_poll;
+    AvahiClient *client;
+
+    pa_hashmap *services;
+    char *service_name;
+
+    AvahiEntryGroup *main_entry_group;
+
+    uint16_t port;
+
+    pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot;
+};
+
+static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, const char **ret_description, enum service_subtype *ret_subtype) {
+    pa_assert(s);
+    pa_assert(ret_ss);
+    pa_assert(ret_description);
+    pa_assert(ret_subtype);
+
+    if (pa_sink_isinstance(s->device)) {
+        pa_sink *sink = PA_SINK(s->device);
+
+        *ret_ss = sink->sample_spec;
+        *ret_map = sink->channel_map;
+        *ret_name = sink->name;
+        *ret_description = pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
+        *ret_subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
+
+    } else if (pa_source_isinstance(s->device)) {
+        pa_source *source = PA_SOURCE(s->device);
+
+        *ret_ss = source->sample_spec;
+        *ret_map = source->channel_map;
+        *ret_name = source->name;
+        *ret_description = pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
+        *ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL);
+
+    } else
+        pa_assert_not_reached();
+}
+
+static AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) {
+    char s[128];
+
+    pa_assert(c);
+
+    l = avahi_string_list_add_pair(l, "server-version", PACKAGE_NAME" "PACKAGE_VERSION);
+    l = avahi_string_list_add_pair(l, "user-name", pa_get_user_name(s, sizeof(s)));
+    l = avahi_string_list_add_pair(l, "fqdn", pa_get_fqdn(s, sizeof(s)));
+    l = avahi_string_list_add_printf(l, "cookie=0x%08x", c->cookie);
+
+    return l;
+}
+
+static int publish_service(struct service *s);
+
+static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
+    struct service *s = userdata;
+
+    pa_assert(s);
+
+    switch (state) {
+
+        case AVAHI_ENTRY_GROUP_ESTABLISHED:
+            pa_log_info("Successfully established service %s.", s->service_name);
+            break;
+
+        case AVAHI_ENTRY_GROUP_COLLISION: {
+            char *t;
+
+            t = avahi_alternative_service_name(s->service_name);
+            pa_log_info("Name collision, renaming %s to %s.", s->service_name, t);
+            pa_xfree(s->service_name);
+            s->service_name = t;
+
+            publish_service(s);
+            break;
+        }
+
+        case AVAHI_ENTRY_GROUP_FAILURE: {
+            pa_log("Failed to register service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
+
+            avahi_entry_group_free(g);
+            s->entry_group = NULL;
+
+            break;
+        }
+
+        case AVAHI_ENTRY_GROUP_UNCOMMITED:
+        case AVAHI_ENTRY_GROUP_REGISTERING:
+            ;
+    }
+}
+
+static void service_free(struct service *s);
+
+static int publish_service(struct service *s) {
+    int r = -1;
+    AvahiStringList *txt = NULL;
+    const char *description = NULL, *name = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    enum service_subtype subtype;
+
+    const char * const subtype_text[] = {
+        [SUBTYPE_HARDWARE] = "hardware",
+        [SUBTYPE_VIRTUAL] = "virtual",
+        [SUBTYPE_MONITOR] = "monitor"
+    };
+
+    pa_assert(s);
+
+    if (!s->userdata->client || avahi_client_get_state(s->userdata->client) != AVAHI_CLIENT_S_RUNNING)
+        return 0;
+
+    if (!s->entry_group) {
+        if (!(s->entry_group = avahi_entry_group_new(s->userdata->client, service_entry_group_callback, s))) {
+            pa_log("avahi_entry_group_new(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+            goto finish;
+        }
+    } else
+        avahi_entry_group_reset(s->entry_group);
+
+    txt = txt_record_server_data(s->userdata->core, txt);
+
+    get_service_data(s, &ss, &map, &name, &description, &subtype);
+    txt = avahi_string_list_add_pair(txt, "device", name);
+    txt = avahi_string_list_add_printf(txt, "rate=%u", ss.rate);
+    txt = avahi_string_list_add_printf(txt, "channels=%u", ss.channels);
+    txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(ss.format));
+    txt = avahi_string_list_add_pair(txt, "channel_map", pa_channel_map_snprint(cm, sizeof(cm), &map));
+    txt = avahi_string_list_add_pair(txt, "subtype", subtype_text[subtype]);
+
+    if (avahi_entry_group_add_service_strlst(
+                s->entry_group,
+                AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                0,
+                s->service_name,
+                pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
+                NULL,
+                NULL,
+                s->userdata->port,
+                txt) < 0) {
+
+        pa_log("avahi_entry_group_add_service_strlst(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+        goto finish;
+    }
+
+    if (avahi_entry_group_add_service_subtype(
+                s->entry_group,
+                AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                0,
+                s->service_name,
+                pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
+                NULL,
+                pa_sink_isinstance(s->device) ? (subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SINK_HARDWARE : SERVICE_SUBTYPE_SINK_VIRTUAL) :
+                (subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SOURCE_HARDWARE : (subtype == SUBTYPE_VIRTUAL ? SERVICE_SUBTYPE_SOURCE_VIRTUAL : SERVICE_SUBTYPE_SOURCE_MONITOR))) < 0) {
+
+        pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+        goto finish;
+    }
+
+    if (pa_source_isinstance(s->device) && subtype != SUBTYPE_MONITOR) {
+        if (avahi_entry_group_add_service_subtype(
+                    s->entry_group,
+                    AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                    0,
+                    s->service_name,
+                    SERVICE_TYPE_SOURCE,
+                    NULL,
+                    SERVICE_SUBTYPE_SOURCE_NON_MONITOR) < 0) {
+
+            pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+            goto finish;
+        }
+    }
+
+    if (avahi_entry_group_commit(s->entry_group) < 0) {
+        pa_log("avahi_entry_group_commit(): %s", avahi_strerror(avahi_client_errno(s->userdata->client)));
+        goto finish;
+    }
+
+    r = 0;
+    pa_log_debug("Successfully created entry group for %s.", s->service_name);
+
+finish:
+
+    /* Remove this service */
+    if (r < 0)
+        service_free(s);
+
+    avahi_string_list_free(txt);
+
+    return r;
+}
+
+static struct service *get_service(struct userdata *u, pa_object *device) {
+    struct service *s;
+    char hn[64], un[64];
+    const char *n;
+
+    pa_assert(u);
+    pa_object_assert_ref(device);
+
+    if ((s = pa_hashmap_get(u->services, device)))
+        return s;
+
+    s = pa_xnew(struct service, 1);
+    s->userdata = u;
+    s->entry_group = NULL;
+    s->device = device;
+
+    if (pa_sink_isinstance(device)) {
+        if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+            n = PA_SINK(device)->name;
+    } else {
+        if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
+            n = PA_SOURCE(device)->name;
+    }
+
+    s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s",
+                                                         pa_get_user_name(un, sizeof(un)),
+                                                         pa_get_host_name(hn, sizeof(hn)),
+                                                         n),
+                                       AVAHI_LABEL_MAX-1);
+
+    pa_hashmap_put(u->services, s->device, s);
+
+    return s;
+}
+
+static void service_free(struct service *s) {
+    pa_assert(s);
+
+    pa_hashmap_remove(s->userdata->services, s->device);
+
+    if (s->entry_group) {
+        pa_log_debug("Removing entry group for %s.", s->service_name);
+        avahi_entry_group_free(s->entry_group);
+    }
+
+    pa_xfree(s->service_name);
+    pa_xfree(s);
+}
+
+static pa_bool_t shall_ignore(pa_object *o) {
+    pa_object_assert_ref(o);
+
+    if (pa_sink_isinstance(o))
+        return !!(PA_SINK(o)->flags & PA_SINK_NETWORK);
+
+    if (pa_source_isinstance(o))
+        return PA_SOURCE(o)->monitor_of || (PA_SOURCE(o)->flags & PA_SOURCE_NETWORK);
+
+    pa_assert_not_reached();
+}
+
+static pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    pa_assert(c);
+    pa_object_assert_ref(o);
+
+    if (!shall_ignore(o))
+        publish_service(get_service(u, o));
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) {
+    struct service *s;
+
+    pa_assert(c);
+    pa_object_assert_ref(o);
+
+    if ((s = pa_hashmap_get(u->services, o)))
+        service_free(s);
+
+    return PA_HOOK_OK;
+}
+
+static int publish_main_service(struct userdata *u);
+
+static void main_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
+    struct userdata *u = userdata;
+    pa_assert(u);
+
+    switch (state) {
+
+        case AVAHI_ENTRY_GROUP_ESTABLISHED:
+            pa_log_info("Successfully established main service.");
+            break;
+
+        case AVAHI_ENTRY_GROUP_COLLISION: {
+            char *t;
+
+            t = avahi_alternative_service_name(u->service_name);
+            pa_log_info("Name collision: renaming main service %s to %s.", u->service_name, t);
+            pa_xfree(u->service_name);
+            u->service_name = t;
+
+            publish_main_service(u);
+            break;
+        }
+
+        case AVAHI_ENTRY_GROUP_FAILURE: {
+            pa_log("Failed to register main service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
+
+            avahi_entry_group_free(g);
+            u->main_entry_group = NULL;
+            break;
+        }
+
+        case AVAHI_ENTRY_GROUP_UNCOMMITED:
+        case AVAHI_ENTRY_GROUP_REGISTERING:
+            break;
+    }
+}
+
+static int publish_main_service(struct userdata *u) {
+    AvahiStringList *txt = NULL;
+    int r = -1;
+
+    pa_assert(u);
+
+    if (!u->main_entry_group) {
+        if (!(u->main_entry_group = avahi_entry_group_new(u->client, main_entry_group_callback, u))) {
+            pa_log("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+            goto fail;
+        }
+    } else
+        avahi_entry_group_reset(u->main_entry_group);
+
+    txt = txt_record_server_data(u->core, txt);
+
+    if (avahi_entry_group_add_service_strlst(
+                u->main_entry_group,
+                AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+                0,
+                u->service_name,
+                SERVICE_TYPE_SERVER,
+                NULL,
+                NULL,
+                u->port,
+                txt) < 0) {
+
+        pa_log("avahi_entry_group_add_service_strlst() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+        goto fail;
+    }
+
+    if (avahi_entry_group_commit(u->main_entry_group) < 0) {
+        pa_log("avahi_entry_group_commit() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+        goto fail;
+    }
+
+    r = 0;
+
+fail:
+    avahi_string_list_free(txt);
+
+    return r;
+}
+
+static int publish_all_services(struct userdata *u) {
+    pa_sink *sink;
+    pa_source *source;
+    int r = -1;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    pa_log_debug("Publishing services in Zeroconf");
+
+    for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx)))
+        if (!shall_ignore(PA_OBJECT(sink)))
+            publish_service(get_service(u, PA_OBJECT(sink)));
+
+    for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx)))
+        if (!shall_ignore(PA_OBJECT(source)))
+            publish_service(get_service(u, PA_OBJECT(source)));
+
+    if (publish_main_service(u) < 0)
+        goto fail;
+
+    r = 0;
+
+fail:
+    return r;
+}
+
+static void unpublish_all_services(struct userdata *u, pa_bool_t rem) {
+    void *state = NULL;
+    struct service *s;
+
+    pa_assert(u);
+
+    pa_log_debug("Unpublishing services in Zeroconf");
+
+    while ((s = pa_hashmap_iterate(u->services, &state, NULL))) {
+        if (s->entry_group) {
+            if (rem) {
+                pa_log_debug("Removing entry group for %s.", s->service_name);
+                avahi_entry_group_free(s->entry_group);
+                s->entry_group = NULL;
+            } else {
+                avahi_entry_group_reset(s->entry_group);
+                pa_log_debug("Resetting entry group for %s.", s->service_name);
+            }
+        }
+    }
+
+    if (u->main_entry_group) {
+        if (rem) {
+            pa_log_debug("Removing main entry group.");
+            avahi_entry_group_free(u->main_entry_group);
+            u->main_entry_group = NULL;
+        } else {
+            avahi_entry_group_reset(u->main_entry_group);
+            pa_log_debug("Resetting main entry group.");
+        }
+    }
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    u->client = c;
+
+    switch (state) {
+        case AVAHI_CLIENT_S_RUNNING:
+            publish_all_services(u);
+            break;
+
+        case AVAHI_CLIENT_S_COLLISION:
+            pa_log_debug("Host name collision");
+            unpublish_all_services(u, FALSE);
+            break;
+
+        case AVAHI_CLIENT_FAILURE:
+            if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+                int error;
+
+                pa_log_debug("Avahi daemon disconnected.");
+
+                unpublish_all_services(u, TRUE);
+                avahi_client_free(u->client);
+
+                if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+                    pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+                    pa_module_unload_request(u->module);
+                }
+            }
+
+            break;
+
+        default: ;
+    }
+}
+
+int pa__init(pa_module*m) {
+
+    struct userdata *u;
+    uint32_t port = PA_NATIVE_DEFAULT_PORT;
+    pa_modargs *ma = NULL;
+    char hn[256], un[256];
+    int error;
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port <= 0 || port > 0xFFFF) {
+        pa_log("Invalid port specified.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->port = (uint16_t) port;
+
+    u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
+
+    u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
+    u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
+    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
+
+    u->main_entry_group = NULL;
+
+    u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", pa_get_user_name(un, sizeof(un)), pa_get_host_name(hn, sizeof(hn))), AVAHI_LABEL_MAX);
+
+    if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+        pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+        goto fail;
+    }
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata*u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->services) {
+        struct service *s;
+
+        while ((s = pa_hashmap_get_first(u->services)))
+            service_free(s);
+
+        pa_hashmap_free(u->services, NULL, NULL);
+    }
+
+    if (u->sink_new_slot)
+        pa_hook_slot_free(u->sink_new_slot);
+    if (u->source_new_slot)
+        pa_hook_slot_free(u->source_new_slot);
+    if (u->sink_changed_slot)
+        pa_hook_slot_free(u->sink_changed_slot);
+    if (u->source_changed_slot)
+        pa_hook_slot_free(u->source_changed_slot);
+    if (u->sink_unlink_slot)
+        pa_hook_slot_free(u->sink_unlink_slot);
+    if (u->source_unlink_slot)
+        pa_hook_slot_free(u->source_unlink_slot);
+
+    if (u->main_entry_group)
+        avahi_entry_group_free(u->main_entry_group);
+
+    if (u->client)
+        avahi_client_free(u->client);
+
+    if (u->avahi_poll)
+        pa_avahi_poll_free(u->avahi_poll);
+
+    pa_xfree(u->service_name);
+    pa_xfree(u);
+}
diff --git a/src/modules/oss-util.c b/src/modules/oss-util.c
new file mode 100644 (file)
index 0000000..2791e16
--- /dev/null
@@ -0,0 +1,417 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "oss-util.h"
+
+int pa_oss_open(const char *device, int *mode, int* pcaps) {
+    int fd = -1;
+    int caps;
+
+    pa_assert(device);
+    pa_assert(mode);
+    pa_assert(*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY);
+
+    if(!pcaps)
+        pcaps = &caps;
+
+    if (*mode == O_RDWR) {
+        if ((fd = open(device, O_RDWR|O_NDELAY|O_NOCTTY)) >= 0) {
+            ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
+
+            if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
+                pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
+                goto fail;
+            }
+
+            if (*pcaps & DSP_CAP_DUPLEX)
+                goto success;
+
+            pa_log_warn("'%s' doesn't support full duplex", device);
+
+            pa_close(fd);
+        }
+
+        if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY|O_NOCTTY)) < 0) {
+            if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY|O_NOCTTY)) < 0) {
+                pa_log("open('%s'): %s", device, pa_cstrerror(errno));
+                goto fail;
+            }
+        }
+    } else {
+        if ((fd = open(device, *mode|O_NDELAY|O_NOCTTY)) < 0) {
+            pa_log("open('%s'): %s", device, pa_cstrerror(errno));
+            goto fail;
+        }
+    }
+
+    *pcaps = 0;
+
+    if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
+        pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+success:
+
+    pa_log_debug("capabilities:%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                 *pcaps & DSP_CAP_BATCH ? " BATCH" : "",
+#ifdef DSP_CAP_BIND
+                 *pcaps & DSP_CAP_BIND ? " BIND" : "",
+#else
+                 "",
+#endif
+                 *pcaps & DSP_CAP_COPROC ? " COPROC" : "",
+                 *pcaps & DSP_CAP_DUPLEX ? " DUPLEX" : "",
+#ifdef DSP_CAP_FREERATE
+                 *pcaps & DSP_CAP_FREERATE ? " FREERATE" : "",
+#else
+                 "",
+#endif
+#ifdef DSP_CAP_INPUT
+                 *pcaps & DSP_CAP_INPUT ? " INPUT" : "",
+#else
+                 "",
+#endif
+                 *pcaps & DSP_CAP_MMAP ? " MMAP" : "",
+#ifdef DSP_CAP_MODEM
+                 *pcaps & DSP_CAP_MODEM ? " MODEM" : "",
+#else
+                 "",
+#endif
+#ifdef DSP_CAP_MULTI
+                 *pcaps & DSP_CAP_MULTI ? " MULTI" : "",
+#else
+                 "",
+#endif
+#ifdef DSP_CAP_OUTPUT
+                 *pcaps & DSP_CAP_OUTPUT ? " OUTPUT" : "",
+#else
+                 "",
+#endif
+                 *pcaps & DSP_CAP_REALTIME ? " REALTIME" : "",
+#ifdef DSP_CAP_SHADOW
+                 *pcaps & DSP_CAP_SHADOW ? " SHADOW" : "",
+#else
+                 "",
+#endif
+#ifdef DSP_CAP_VIRTUAL
+                 *pcaps & DSP_CAP_VIRTUAL ? " VIRTUAL" : "",
+#else
+                 "",
+#endif
+                 *pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : "");
+
+    pa_make_fd_cloexec(fd);
+
+    return fd;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+    return -1;
+}
+
+int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
+    int format, channels, speed, reqformat;
+    pa_sample_format_t orig_format;
+
+    static const int format_trans[PA_SAMPLE_MAX] = {
+        [PA_SAMPLE_U8] = AFMT_U8,
+        [PA_SAMPLE_ALAW] = AFMT_A_LAW,
+        [PA_SAMPLE_ULAW] = AFMT_MU_LAW,
+        [PA_SAMPLE_S16LE] = AFMT_S16_LE,
+        [PA_SAMPLE_S16BE] = AFMT_S16_BE,
+        [PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S32LE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */
+    };
+
+    pa_assert(fd >= 0);
+    pa_assert(ss);
+
+    orig_format = ss->format;
+
+    reqformat = format = format_trans[ss->format];
+    if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) {
+        format = AFMT_S16_NE;
+        if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) {
+            int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE;
+            format = f;
+            if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) {
+                format = AFMT_U8;
+                if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) {
+                    pa_log("SNDCTL_DSP_SETFMT: %s", format != AFMT_U8 ? "No supported sample format" : pa_cstrerror(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;
+    }
+
+    if (orig_format != ss->format)
+        pa_log_warn("device doesn't support sample format %s, changed to %s.",
+               pa_sample_format_to_string(orig_format),
+               pa_sample_format_to_string(ss->format));
+
+    channels = ss->channels;
+    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
+        pa_log("SNDCTL_DSP_CHANNELS: %s", pa_cstrerror(errno));
+        return -1;
+    }
+    pa_assert(channels > 0);
+
+    if (ss->channels != channels) {
+        pa_log_warn("device doesn't support %i channels, using %i channels.", ss->channels, channels);
+        ss->channels = channels;
+    }
+
+    speed = ss->rate;
+    if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
+        pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno));
+        return -1;
+    }
+    pa_assert(speed > 0);
+
+    if (ss->rate != (unsigned) speed) {
+        pa_log_warn("device doesn't support %i Hz, changed to %i Hz.", ss->rate, speed);
+
+        /* If the sample rate deviates too much, we need to resample */
+        if (speed < ss->rate*.95 || speed > ss->rate*1.05)
+            ss->rate = speed;
+    }
+
+    return 0;
+}
+
+static int simple_log2(int v) {
+    int k = 0;
+
+    for (;;) {
+        v >>= 1;
+        if (!v) break;
+        k++;
+    }
+
+    return k;
+}
+
+int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
+    int arg;
+    arg = ((int) nfrags << 16) | simple_log2(frag_size);
+
+    if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
+        pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
+    char cv[PA_CVOLUME_SNPRINT_MAX];
+    unsigned vol;
+
+    pa_assert(fd >= 0);
+    pa_assert(ss);
+    pa_assert(volume);
+
+    if (ioctl(fd, mixer, &vol) < 0)
+        return -1;
+
+    pa_cvolume_reset(volume, ss->channels);
+
+    volume->values[0] = ((vol & 0xFF) * PA_VOLUME_NORM) / 100;
+
+    if (volume->channels >= 2)
+        volume->values[1] = (((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100;
+
+    pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume));
+    return 0;
+}
+
+int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
+    char cv[PA_CVOLUME_SNPRINT_MAX];
+    unsigned vol;
+    pa_volume_t l, r;
+
+    l = volume->values[0] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[0];
+
+    vol = (l*100)/PA_VOLUME_NORM;
+
+    if (ss->channels >= 2) {
+        r = volume->values[1] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[1];
+        vol |= ((r*100)/PA_VOLUME_NORM) << 8;
+    }
+
+    if (ioctl(fd, mixer, &vol) < 0)
+        return -1;
+
+    pa_log_debug("Wrote mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume));
+    return 0;
+}
+
+static int get_device_number(const char *dev) {
+    const char *p, *e;
+    char *rp = NULL;
+    int r;
+
+    if (!(p = rp = pa_readlink(dev))) {
+        if (errno != EINVAL && errno != ENOLINK) {
+            r = -1;
+            goto finish;
+        }
+
+        p = dev;
+    }
+
+    if ((e = strrchr(p, '/')))
+        p = e+1;
+
+    if (p == 0) {
+        r = 0;
+        goto finish;
+    }
+
+    p = strchr(p, 0) -1;
+
+    if (*p >= '0' && *p <= '9') {
+        r = *p - '0';
+        goto finish;
+    }
+
+    r = -1;
+
+finish:
+    pa_xfree(rp);
+    return r;
+}
+
+int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
+    FILE *f;
+    int n, r = -1;
+    int b = 0;
+
+    if ((n = get_device_number(dev)) < 0)
+        return -1;
+
+    if (!(f = fopen("/dev/sndstat", "r")) &&
+        !(f = fopen("/proc/sndstat", "r")) &&
+        !(f = fopen("/proc/asound/oss/sndstat", "r"))) {
+
+        if (errno != ENOENT)
+            pa_log_warn("failed to open OSS sndstat device: %s", pa_cstrerror(errno));
+
+        return -1;
+    }
+
+    while (!feof(f)) {
+        char line[64];
+        int device;
+
+        if (!fgets(line, sizeof(line), f))
+            break;
+
+        line[strcspn(line, "\r\n")] = 0;
+
+        if (!b) {
+            b = strcmp(line, "Audio devices:") == 0;
+            continue;
+        }
+
+        if (line[0] == 0)
+            break;
+
+        if (sscanf(line, "%i: ", &device) != 1)
+            continue;
+
+        if (device == n) {
+            char *k = strchr(line, ':');
+            pa_assert(k);
+            k++;
+            k += strspn(k, " ");
+
+            if (pa_endswith(k, " (DUPLEX)"))
+                k[strlen(k)-9] = 0;
+
+            pa_strlcpy(name, k, l);
+            r = 0;
+            break;
+        }
+    }
+
+    fclose(f);
+    return r;
+}
+
+static int open_mixer(const char *mixer) {
+    int fd;
+
+    if ((fd = open(mixer, O_RDWR|O_NDELAY|O_NOCTTY)) >= 0)
+        return fd;
+
+    return -1;
+}
+
+int pa_oss_open_mixer_for_device(const char *device) {
+    int n;
+    char *fn;
+    int fd;
+
+    if ((n = get_device_number(device)) < 0)
+        return -1;
+
+    if (n == 0)
+        if ((fd = open_mixer("/dev/mixer")) >= 0)
+            return fd;
+
+    fn = pa_sprintf_malloc("/dev/mixer%i", n);
+    fd = open_mixer(fn);
+    pa_xfree(fn);
+
+    if (fd < 0)
+        pa_log_warn("Failed to open mixer '%s': %s", device, pa_cstrerror(errno));
+
+    return fd;
+}
diff --git a/src/modules/oss-util.h b/src/modules/oss-util.h
new file mode 100644 (file)
index 0000000..654f7bb
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef fooossutilhfoo
+#define fooossutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+
+int pa_oss_open(const char *device, int *mode, int* pcaps);
+int pa_oss_auto_format(int fd, pa_sample_spec *ss);
+
+int pa_oss_set_fragments(int fd, int frags, int frag_size);
+
+int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume);
+int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume);
+
+int pa_oss_get_hw_description(const char *dev, char *name, size_t l);
+
+int pa_oss_open_mixer_for_device(const char *device);
+
+#endif
diff --git a/src/modules/rtp/Makefile b/src/modules/rtp/Makefile
new file mode 100644 (file)
index 0000000..316beb7
--- /dev/null
@@ -0,0 +1,13 @@
+# This is a dirty trick just to ease compilation with emacs
+#
+# This file is not intended to be distributed or anything
+#
+# So: don't touch it, even better ignore it!
+
+all:
+       $(MAKE) -C ../..
+
+clean:
+       $(MAKE) -C ../.. clean
+
+.PHONY: all clean
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
new file mode 100644 (file)
index 0000000..cff5cf8
--- /dev/null
@@ -0,0 +1,701 @@
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/time-smoother.h>
+
+#include "module-rtp-recv-symdef.h"
+
+#include "rtp.h"
+#include "sdp.h"
+#include "sap.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Recieve data from a network via RTP/SAP/SDP");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "sink=<name of the sink> "
+        "sap_address=<multicast address to listen on> "
+);
+
+#define SAP_PORT 9875
+#define DEFAULT_SAP_ADDRESS "224.0.0.56"
+#define MEMBLOCKQ_MAXLENGTH (1024*1024*40)
+#define MAX_SESSIONS 16
+#define DEATH_TIMEOUT 20
+#define RATE_UPDATE_INTERVAL (5*PA_USEC_PER_SEC)
+#define LATENCY_USEC (500*PA_USEC_PER_MSEC)
+
+static const char* const valid_modargs[] = {
+    "sink",
+    "sap_address",
+    NULL
+};
+
+struct session {
+    struct userdata *userdata;
+    PA_LLIST_FIELDS(struct session);
+
+    pa_sink_input *sink_input;
+    pa_memblockq *memblockq;
+
+    pa_bool_t first_packet;
+    uint32_t ssrc;
+    uint32_t offset;
+
+    struct pa_sdp_info sdp_info;
+
+    pa_rtp_context rtp_context;
+
+    pa_rtpoll_item *rtpoll_item;
+
+    pa_atomic_t timestamp;
+
+    pa_smoother *smoother;
+    pa_usec_t intended_latency;
+    pa_usec_t sink_latency;
+
+    pa_usec_t last_rate_update;
+};
+
+struct userdata {
+    pa_module *module;
+
+    pa_sap_context sap_context;
+    pa_io_event* sap_event;
+
+    pa_time_event *check_death_event;
+
+    char *sink_name;
+
+    PA_LLIST_HEAD(struct session, sessions);
+    pa_hashmap *by_origin;
+    int n_sessions;
+};
+
+static void session_free(struct session *s);
+
+/* Called from I/O thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct session *s = PA_SINK_INPUT(o)->userdata;
+
+    switch (code) {
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
+            *((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+            break;
+    }
+
+    return pa_sink_input_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+    struct session *s;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    if (pa_memblockq_peek(s->memblockq, chunk) < 0)
+        return -1;
+
+    pa_memblockq_drop(s->memblockq, chunk->length);
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct session *s;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    pa_memblockq_rewind(s->memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct session *s;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    pa_memblockq_set_maxrewind(s->memblockq, nbytes);
+}
+
+/* Called from main context */
+static void sink_input_kill(pa_sink_input* i) {
+    struct session *s;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    session_free(s);
+}
+
+/* Called from I/O thread context */
+static int rtpoll_work_cb(pa_rtpoll_item *i) {
+    pa_memchunk chunk;
+    int64_t k, j, delta;
+    struct timeval now;
+    struct session *s;
+    struct pollfd *p;
+
+    pa_assert_se(s = pa_rtpoll_item_get_userdata(i));
+
+    p = pa_rtpoll_item_get_pollfd(i, NULL);
+
+    if (p->revents & (POLLERR|POLLNVAL|POLLHUP|POLLOUT)) {
+        pa_log("poll() signalled bad revents.");
+        return -1;
+    }
+
+    if ((p->revents & POLLIN) == 0)
+        return 0;
+
+    p->revents = 0;
+
+    if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->module->core->mempool) < 0)
+        return 0;
+
+    if (s->sdp_info.payload != s->rtp_context.payload) {
+        pa_memblock_unref(chunk.memblock);
+        return 0;
+    }
+
+    if (!s->first_packet) {
+        s->first_packet = TRUE;
+
+        s->ssrc = s->rtp_context.ssrc;
+        s->offset = s->rtp_context.timestamp;
+
+        if (s->ssrc == s->userdata->module->core->cookie)
+            pa_log_warn("Detected RTP packet loop!");
+    } else {
+        if (s->ssrc != s->rtp_context.ssrc) {
+            pa_memblock_unref(chunk.memblock);
+            return 0;
+        }
+    }
+
+    /* Check wheter there was a timestamp overflow */
+    k = (int64_t) s->rtp_context.timestamp - (int64_t) s->offset;
+    j = (int64_t) 0x100000000LL - (int64_t) s->offset + (int64_t) s->rtp_context.timestamp;
+
+    if ((k < 0 ? -k : k) < (j < 0 ? -j : j))
+        delta = k;
+    else
+        delta = j;
+
+    pa_memblockq_seek(s->memblockq, delta * s->rtp_context.frame_size, PA_SEEK_RELATIVE);
+
+    pa_rtclock_get(&now);
+
+    pa_smoother_put(s->smoother, pa_timeval_load(&now), pa_bytes_to_usec(pa_memblockq_get_write_index(s->memblockq), &s->sink_input->sample_spec));
+
+    if (pa_memblockq_push(s->memblockq, &chunk) < 0) {
+        pa_log_warn("Queue overrun");
+        pa_memblockq_seek(s->memblockq, chunk.length, PA_SEEK_RELATIVE);
+    }
+
+    pa_log("blocks in q: %u", pa_memblockq_get_nblocks(s->memblockq));
+
+    pa_memblock_unref(chunk.memblock);
+
+    /* The next timestamp we expect */
+    s->offset = s->rtp_context.timestamp + (chunk.length / s->rtp_context.frame_size);
+
+    pa_atomic_store(&s->timestamp, now.tv_sec);
+
+    if (s->last_rate_update + RATE_UPDATE_INTERVAL < pa_timeval_load(&now)) {
+        pa_usec_t wi, ri, render_delay, sink_delay = 0, latency, fix;
+        unsigned fix_samples;
+
+        pa_log("Updating sample rate");
+
+        wi = pa_smoother_get(s->smoother, pa_timeval_load(&now));
+        ri = pa_bytes_to_usec(pa_memblockq_get_read_index(s->memblockq), &s->sink_input->sample_spec);
+
+        if (PA_MSGOBJECT(s->sink_input->sink)->process_msg(PA_MSGOBJECT(s->sink_input->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_delay, 0, NULL) < 0)
+            sink_delay = 0;
+
+        render_delay = pa_bytes_to_usec(pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq), &s->sink_input->sink->sample_spec);
+
+        if (ri > render_delay+sink_delay)
+            ri -= render_delay+sink_delay;
+        else
+            ri = 0;
+
+        if (wi < ri)
+            latency = 0;
+        else
+            latency = wi - ri;
+
+        pa_log_debug("Write index deviates by %0.2f ms, expected %0.2f ms", (double) latency/PA_USEC_PER_MSEC, (double)  s->intended_latency/PA_USEC_PER_MSEC);
+
+        /* Calculate deviation */
+        if (latency < s->intended_latency)
+            fix = s->intended_latency - latency;
+        else
+            fix = latency - s->intended_latency;
+
+        /* How many samples is this per second? */
+        fix_samples = fix * s->sink_input->thread_info.sample_spec.rate / RATE_UPDATE_INTERVAL;
+
+        /* Check if deviation is in bounds */
+        if (fix_samples > s->sink_input->sample_spec.rate*.20)
+            pa_log_debug("Hmmm, rate fix is too large (%lu Hz), not applying.", (unsigned long) fix_samples);
+
+        /* Fix up rate */
+        if (latency < s->intended_latency)
+            s->sink_input->sample_spec.rate -= fix_samples;
+        else
+            s->sink_input->sample_spec.rate += fix_samples;
+
+        pa_resampler_set_input_rate(s->sink_input->thread_info.resampler, s->sink_input->sample_spec.rate);
+
+        pa_log_debug("Updated sampling rate to %lu Hz.", (unsigned long) s->sink_input->sample_spec.rate);
+
+        s->last_rate_update = pa_timeval_load(&now);
+    }
+
+    if (pa_memblockq_is_readable(s->memblockq) &&
+        s->sink_input->thread_info.underrun_for > 0) {
+        pa_log_debug("Requesting rewind due to end of underrun");
+        pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE);
+    }
+
+    return 1;
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach(pa_sink_input *i) {
+    struct session *s;
+    struct pollfd *p;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    pa_assert(!s->rtpoll_item);
+    s->rtpoll_item = pa_rtpoll_item_new(i->sink->rtpoll, PA_RTPOLL_LATE, 1);
+
+    p = pa_rtpoll_item_get_pollfd(s->rtpoll_item, NULL);
+    p->fd = s->rtp_context.fd;
+    p->events = POLLIN;
+    p->revents = 0;
+
+    pa_rtpoll_item_set_work_callback(s->rtpoll_item, rtpoll_work_cb);
+    pa_rtpoll_item_set_userdata(s->rtpoll_item, s);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach(pa_sink_input *i) {
+    struct session *s;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    pa_assert(s->rtpoll_item);
+    pa_rtpoll_item_free(s->rtpoll_item);
+    s->rtpoll_item = NULL;
+}
+
+static int mcast_socket(const struct sockaddr* sa, socklen_t salen) {
+    int af, fd = -1, r, one;
+
+    pa_assert(sa);
+    pa_assert(salen > 0);
+
+    af = sa->sa_family;
+    if ((fd = socket(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("Failed to create socket: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    one = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
+        pa_log("SO_REUSEADDR failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (af == AF_INET) {
+        struct ip_mreq mr4;
+        memset(&mr4, 0, sizeof(mr4));
+        mr4.imr_multiaddr = ((const struct sockaddr_in*) sa)->sin_addr;
+        r = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr4, sizeof(mr4));
+    } else {
+        struct ipv6_mreq mr6;
+        memset(&mr6, 0, sizeof(mr6));
+        mr6.ipv6mr_multiaddr = ((const struct sockaddr_in6*) sa)->sin6_addr;
+        r = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mr6, sizeof(mr6));
+    }
+
+    if (r < 0) {
+        pa_log_info("Joining mcast group failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (bind(fd, sa, salen) < 0) {
+        pa_log("bind() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    return fd;
+
+fail:
+    if (fd >= 0)
+        close(fd);
+
+    return -1;
+}
+
+static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_info) {
+    struct session *s = NULL;
+    pa_sink *sink;
+    int fd = -1;
+    pa_memchunk silence;
+    pa_sink_input_new_data data;
+    struct timeval now;
+
+    pa_assert(u);
+    pa_assert(sdp_info);
+
+    if (u->n_sessions >= MAX_SESSIONS) {
+        pa_log("Session limit reached.");
+        goto fail;
+    }
+
+    if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, TRUE))) {
+        pa_log("Sink does not exist.");
+        goto fail;
+    }
+
+    pa_rtclock_get(&now);
+
+    s = pa_xnew0(struct session, 1);
+    s->userdata = u;
+    s->first_packet = FALSE;
+    s->sdp_info = *sdp_info;
+    s->rtpoll_item = NULL;
+    s->intended_latency = LATENCY_USEC;
+    s->smoother = pa_smoother_new(PA_USEC_PER_SEC*5, PA_USEC_PER_SEC*2, TRUE, 10);
+    pa_smoother_set_time_offset(s->smoother, pa_timeval_load(&now));
+    s->last_rate_update = pa_timeval_load(&now);
+    pa_atomic_store(&s->timestamp, now.tv_sec);
+
+    if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0)
+        goto fail;
+
+    pa_sink_input_new_data_init(&data);
+    data.sink = sink;
+    data.driver = __FILE__;
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream");
+    pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME,
+                     "RTP Stream%s%s%s",
+                     sdp_info->session_name ? " (" : "",
+                     sdp_info->session_name ? sdp_info->session_name : "",
+                     sdp_info->session_name ? ")" : "");
+
+    if (sdp_info->session_name)
+        pa_proplist_sets(data.proplist, "rtp.session", sdp_info->session_name);
+    pa_proplist_sets(data.proplist, "rtp.origin", sdp_info->origin);
+    pa_proplist_setf(data.proplist, "rtp.payload", "%u", (unsigned) sdp_info->payload);
+    data.module = u->module;
+    pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec);
+
+    s->sink_input = pa_sink_input_new(u->module->core, &data, 0);
+    pa_sink_input_new_data_done(&data);
+
+    if (!s->sink_input) {
+        pa_log("Failed to create sink input.");
+        goto fail;
+    }
+
+    s->sink_input->userdata = s;
+
+    s->sink_input->parent.process_msg = sink_input_process_msg;
+    s->sink_input->pop = sink_input_pop_cb;
+    s->sink_input->process_rewind = sink_input_process_rewind_cb;
+    s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    s->sink_input->kill = sink_input_kill;
+    s->sink_input->attach = sink_input_attach;
+    s->sink_input->detach = sink_input_detach;
+
+    pa_sink_input_get_silence(s->sink_input, &silence);
+
+    s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, s->intended_latency/2);
+
+    if (s->intended_latency < s->sink_latency*2)
+        s->intended_latency = s->sink_latency*2;
+
+    s->memblockq = pa_memblockq_new(
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            MEMBLOCKQ_MAXLENGTH,
+            pa_frame_size(&s->sink_input->sample_spec),
+            pa_usec_to_bytes(s->intended_latency - s->sink_latency, &s->sink_input->sample_spec),
+            0,
+            0,
+            &silence);
+
+    pa_memblock_unref(silence.memblock);
+
+    pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec));
+
+    pa_hashmap_put(s->userdata->by_origin, s->sdp_info.origin, s);
+    u->n_sessions++;
+    PA_LLIST_PREPEND(struct session, s->userdata->sessions, s);
+
+    pa_sink_input_put(s->sink_input);
+
+    pa_log_info("New session '%s'", s->sdp_info.session_name);
+
+    return s;
+
+fail:
+    pa_xfree(s);
+
+    if (fd >= 0)
+        pa_close(fd);
+
+    return NULL;
+}
+
+static void session_free(struct session *s) {
+    pa_assert(s);
+
+    pa_log_info("Freeing session '%s'", s->sdp_info.session_name);
+
+    pa_sink_input_unlink(s->sink_input);
+    pa_sink_input_unref(s->sink_input);
+
+    PA_LLIST_REMOVE(struct session, s->userdata->sessions, s);
+    pa_assert(s->userdata->n_sessions >= 1);
+    s->userdata->n_sessions--;
+    pa_hashmap_remove(s->userdata->by_origin, s->sdp_info.origin);
+
+    pa_memblockq_free(s->memblockq);
+    pa_sdp_info_destroy(&s->sdp_info);
+    pa_rtp_context_destroy(&s->rtp_context);
+
+    pa_smoother_free(s->smoother);
+
+    pa_xfree(s);
+}
+
+static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
+    struct userdata *u = userdata;
+    pa_bool_t goodbye = FALSE;
+    pa_sdp_info info;
+    struct session *s;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(u);
+    pa_assert(fd == u->sap_context.fd);
+    pa_assert(flags == PA_IO_EVENT_INPUT);
+
+    if (pa_sap_recv(&u->sap_context, &goodbye) < 0)
+        return;
+
+    if (!pa_sdp_parse(u->sap_context.sdp_data, &info, goodbye))
+        return;
+
+    if (goodbye) {
+
+        if ((s = pa_hashmap_get(u->by_origin, info.origin)))
+            session_free(s);
+
+        pa_sdp_info_destroy(&info);
+    } else {
+
+        if (!(s = pa_hashmap_get(u->by_origin, info.origin))) {
+            if (!(s = session_new(u, &info)))
+                pa_sdp_info_destroy(&info);
+
+        } else {
+            struct timeval now;
+            pa_rtclock_get(&now);
+            pa_atomic_store(&s->timestamp, now.tv_sec);
+
+            pa_sdp_info_destroy(&info);
+        }
+    }
+}
+
+static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *ptv, void *userdata) {
+    struct session *s, *n;
+    struct userdata *u = userdata;
+    struct timeval now;
+    struct timeval tv;
+
+    pa_assert(m);
+    pa_assert(t);
+    pa_assert(ptv);
+    pa_assert(u);
+
+    pa_rtclock_get(&now);
+
+    pa_log_debug("Checking for dead streams ...");
+
+    for (s = u->sessions; s; s = n) {
+        int k;
+        n = s->next;
+
+        k = pa_atomic_load(&s->timestamp);
+
+        if (k + DEATH_TIMEOUT < now.tv_sec)
+            session_free(s);
+    }
+
+    /* Restart timer */
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, DEATH_TIMEOUT*PA_USEC_PER_SEC);
+    m->time_restart(t, &tv);
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    struct sockaddr_in sa4;
+    struct sockaddr_in6 sa6;
+    struct sockaddr *sa;
+    socklen_t salen;
+    const char *sap_address;
+    int fd = -1;
+    struct timeval tv;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("failed to parse module arguments");
+        goto fail;
+    }
+
+    sap_address = pa_modargs_get_value(ma, "sap_address", DEFAULT_SAP_ADDRESS);
+
+    if (inet_pton(AF_INET6, sap_address, &sa6.sin6_addr) > 0) {
+        sa6.sin6_family = AF_INET6;
+        sa6.sin6_port = htons(SAP_PORT);
+        sa = (struct sockaddr*) &sa6;
+        salen = sizeof(sa6);
+    } else if (inet_pton(AF_INET, sap_address, &sa4.sin_addr) > 0) {
+        sa4.sin_family = AF_INET;
+        sa4.sin_port = htons(SAP_PORT);
+        sa = (struct sockaddr*) &sa4;
+        salen = sizeof(sa4);
+    } else {
+        pa_log("Invalid SAP address '%s'", sap_address);
+        goto fail;
+    }
+
+    if ((fd = mcast_socket(sa, salen)) < 0)
+        goto fail;
+
+    u = pa_xnew(struct userdata, 1);
+    m->userdata = u;
+    u->module = m;
+    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+
+    u->sap_event = m->core->mainloop->io_new(m->core->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u);
+    pa_sap_context_init_recv(&u->sap_context, fd);
+
+    PA_LLIST_HEAD_INIT(struct session, u->sessions);
+    u->n_sessions = 0;
+    u->by_origin = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, DEATH_TIMEOUT * PA_USEC_PER_SEC);
+    u->check_death_event = m->core->mainloop->time_new(m->core->mainloop, &tv, check_death_event_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    struct session *s;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sap_event)
+        m->core->mainloop->io_free(u->sap_event);
+
+    if (u->check_death_event)
+        m->core->mainloop->time_free(u->check_death_event);
+
+    pa_sap_context_destroy(&u->sap_context);
+
+    if (u->by_origin) {
+        while ((s = pa_hashmap_get_first(u->by_origin)))
+            session_free(s);
+
+        pa_hashmap_free(u->by_origin, NULL, NULL);
+    }
+
+    pa_xfree(u->sink_name);
+    pa_xfree(u);
+}
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
new file mode 100644 (file)
index 0000000..d0d06c4
--- /dev/null
@@ -0,0 +1,402 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/source.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/socket-util.h>
+
+#include "module-rtp-send-symdef.h"
+
+#include "rtp.h"
+#include "sdp.h"
+#include "sap.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Read data from source and send it to the network via RTP/SAP/SDP");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "source=<name of the source> "
+        "format=<sample format> "
+        "channels=<number of channels> "
+        "rate=<sample rate> "
+        "destination=<destination IP address> "
+        "port=<port number> "
+        "mtu=<maximum transfer unit> "
+        "loop=<loopback to local host?>"
+);
+
+#define DEFAULT_PORT 46000
+#define SAP_PORT 9875
+#define DEFAULT_DESTINATION "224.0.0.56"
+#define MEMBLOCKQ_MAXLENGTH (1024*170)
+#define DEFAULT_MTU 1280
+#define SAP_INTERVAL 5
+
+static const char* const valid_modargs[] = {
+    "source",
+    "format",
+    "channels",
+    "rate",
+    "destination",
+    "port",
+    "mtu" ,
+    "loop",
+    NULL
+};
+
+struct userdata {
+    pa_module *module;
+
+    pa_source_output *source_output;
+    pa_memblockq *memblockq;
+
+    pa_rtp_context rtp_context;
+    pa_sap_context sap_context;
+    size_t mtu;
+
+    pa_time_event *sap_event;
+};
+
+/* Called from I/O thread context */
+static int source_output_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct userdata *u;
+    pa_assert_se(u = PA_SOURCE_OUTPUT(o)->userdata);
+
+    switch (code) {
+        case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY:
+            *((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->source_output->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+            break;
+    }
+
+    return pa_source_output_process_msg(o, code, data, offset, chunk);
+}
+
+/* Called from I/O thread context */
+static void source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
+    struct userdata *u;
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    if (pa_memblockq_push(u->memblockq, chunk) < 0) {
+        pa_log_warn("Failed to push chunk into memblockq.");
+        return;
+    }
+
+    pa_rtp_send(&u->rtp_context, u->mtu, u->memblockq);
+}
+
+/* Called from main context */
+static void source_output_kill(pa_source_output* o) {
+    struct userdata *u;
+    pa_source_output_assert_ref(o);
+    pa_assert_se(u = o->userdata);
+
+    pa_module_unload_request(u->module);
+
+    pa_source_output_unlink(u->source_output);
+    pa_source_output_unref(u->source_output);
+    u->source_output = NULL;
+}
+
+static void sap_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+    struct timeval next;
+
+    pa_assert(m);
+    pa_assert(t);
+    pa_assert(tv);
+    pa_assert(u);
+
+    pa_sap_send(&u->sap_context, 0);
+
+    pa_gettimeofday(&next);
+    pa_timeval_add(&next, SAP_INTERVAL * PA_USEC_PER_SEC);
+    m->time_restart(t, &next);
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_modargs *ma = NULL;
+    const char *dest;
+    uint32_t port = DEFAULT_PORT, mtu;
+    int af, fd = -1, sap_fd = -1;
+    pa_source *s;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    struct sockaddr_in sa4, sap_sa4;
+    struct sockaddr_in6 sa6, sap_sa6;
+    struct sockaddr_storage sa_dst;
+    pa_source_output *o = NULL;
+    uint8_t payload;
+    char *p;
+    int r, j;
+    socklen_t k;
+    struct timeval tv;
+    char hn[128], *n;
+    pa_bool_t loop = FALSE;
+    pa_source_output_new_data data;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE, 1))) {
+        pa_log("Source does not exist.");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "loop", &loop) < 0) {
+        pa_log("Failed to parse \"loop\" parameter.");
+        goto fail;
+    }
+
+    ss = s->sample_spec;
+    pa_rtp_sample_spec_fixup(&ss);
+    cm = s->channel_map;
+    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+        pa_log("Failed to parse sample specification");
+        goto fail;
+    }
+
+    if (!pa_rtp_sample_spec_valid(&ss)) {
+        pa_log("Specified sample type not compatible with RTP");
+        goto fail;
+    }
+
+    if (ss.channels != cm.channels)
+        pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_AIFF);
+
+    payload = pa_rtp_payload_from_sample_spec(&ss);
+
+    mtu = pa_frame_align(DEFAULT_MTU, &ss);
+
+    if (pa_modargs_get_value_u32(ma, "mtu", &mtu) < 0 || mtu < 1 || mtu % pa_frame_size(&ss) != 0) {
+        pa_log("Invalid MTU.");
+        goto fail;
+    }
+
+    port = DEFAULT_PORT + ((rand() % 512) << 1);
+    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {
+        pa_log("port= expects a numerical argument between 1 and 65535.");
+        goto fail;
+    }
+
+    if (port & 1)
+        pa_log_warn("Port number not even as suggested in RFC3550!");
+
+    dest = pa_modargs_get_value(ma, "destination", DEFAULT_DESTINATION);
+
+    if (inet_pton(AF_INET6, dest, &sa6.sin6_addr) > 0) {
+        sa6.sin6_family = af = AF_INET6;
+        sa6.sin6_port = htons(port);
+        sap_sa6 = sa6;
+        sap_sa6.sin6_port = htons(SAP_PORT);
+    } else if (inet_pton(AF_INET, dest, &sa4.sin_addr) > 0) {
+        sa4.sin_family = af = AF_INET;
+        sa4.sin_port = htons(port);
+        sap_sa4 = sa4;
+        sap_sa4.sin_port = htons(SAP_PORT);
+    } else {
+        pa_log("Invalid destination '%s'", dest);
+        goto fail;
+    }
+
+    if ((fd = socket(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("socket() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (connect(fd, af == AF_INET ? (struct sockaddr*) &sa4 : (struct sockaddr*) &sa6, af == AF_INET ? sizeof(sa4) : sizeof(sa6)) < 0) {
+        pa_log("connect() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if ((sap_fd = socket(af, SOCK_DGRAM, 0)) < 0) {
+        pa_log("socket() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (connect(sap_fd, af == AF_INET ? (struct sockaddr*) &sap_sa4 : (struct sockaddr*) &sap_sa6, af == AF_INET ? sizeof(sap_sa4) : sizeof(sap_sa6)) < 0) {
+        pa_log("connect() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    j = !!loop;
+    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0 ||
+        setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0) {
+        pa_log("IP_MULTICAST_LOOP failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    /* If the socket queue is full, let's drop packets */
+    pa_make_fd_nonblock(fd);
+    pa_make_udp_socket_low_delay(fd);
+    pa_make_fd_cloexec(fd);
+    pa_make_fd_cloexec(sap_fd);
+
+    pa_source_output_new_data_init(&data);
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "RTP Monitor Stream");
+    pa_proplist_sets(data.proplist, "rtp.destination", dest);
+    pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu);
+    pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port);
+    data.driver = __FILE__;
+    data.module = m;
+    data.source = s;
+    pa_source_output_new_data_set_sample_spec(&data, &ss);
+    pa_source_output_new_data_set_channel_map(&data, &cm);
+
+    o = pa_source_output_new(m->core, &data, 0);
+    pa_source_output_new_data_done(&data);
+
+    if (!o) {
+        pa_log("failed to create source output.");
+        goto fail;
+    }
+
+    o->parent.process_msg = source_output_process_msg;
+    o->push = source_output_push;
+    o->kill = source_output_kill;
+
+    u = pa_xnew(struct userdata, 1);
+    m->userdata = u;
+    o->userdata = u;
+
+    u->module = m;
+    u->source_output = o;
+
+    u->memblockq = pa_memblockq_new(
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            MEMBLOCKQ_MAXLENGTH,
+            pa_frame_size(&ss),
+            1,
+            0,
+            0,
+            NULL);
+
+    u->mtu = mtu;
+
+    k = sizeof(sa_dst);
+    pa_assert_se((r = getsockname(fd, (struct sockaddr*) &sa_dst, &k)) >= 0);
+
+    n = pa_sprintf_malloc("PulseAudio RTP Stream on %s", pa_get_fqdn(hn, sizeof(hn)));
+
+    p = pa_sdp_build(af,
+                     af == AF_INET ? (void*) &((struct sockaddr_in*) &sa_dst)->sin_addr : (void*) &((struct sockaddr_in6*) &sa_dst)->sin6_addr,
+                     af == AF_INET ? (void*) &sa4.sin_addr : (void*) &sa6.sin6_addr,
+                     n, port, payload, &ss);
+
+    pa_xfree(n);
+
+    pa_rtp_context_init_send(&u->rtp_context, fd, m->core->cookie, payload, pa_frame_size(&ss));
+    pa_sap_context_init_send(&u->sap_context, sap_fd, p);
+
+    pa_log_info("RTP stream initialized with mtu %u on %s:%u, SSRC=0x%08x, payload=%u, initial sequence #%u", mtu, dest, port, u->rtp_context.ssrc, payload, u->rtp_context.sequence);
+    pa_log_info("SDP-Data:\n%s\nEOF", p);
+
+    pa_sap_send(&u->sap_context, 0);
+
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, SAP_INTERVAL * PA_USEC_PER_SEC);
+    u->sap_event = m->core->mainloop->time_new(m->core->mainloop, &tv, sap_event_cb, u);
+
+    pa_source_output_put(u->source_output);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    if (fd >= 0)
+        pa_close(fd);
+
+    if (sap_fd >= 0)
+        pa_close(sap_fd);
+
+    if (o) {
+        pa_source_output_unlink(o);
+        pa_source_output_unref(o);
+    }
+
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sap_event)
+        m->core->mainloop->time_free(u->sap_event);
+
+    if (u->source_output) {
+        pa_source_output_unlink(u->source_output);
+        pa_source_output_unref(u->source_output);
+    }
+
+    pa_rtp_context_destroy(&u->rtp_context);
+
+    pa_sap_send(&u->sap_context, 1);
+    pa_sap_context_destroy(&u->sap_context);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    pa_xfree(u);
+}
diff --git a/src/modules/rtp/rfc2327.txt b/src/modules/rtp/rfc2327.txt
new file mode 100644 (file)
index 0000000..ce77de6
--- /dev/null
@@ -0,0 +1,2355 @@
+
+
+
+
+
+
+Network Working Group                                           M. Handley
+Request for Comments: 2327                                     V. Jacobson
+Category: Standards Track                                         ISI/LBNL
+                                                                April 1998
+
+
+                   SDP: Session Description Protocol
+
+Status of this Memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (1998).  All Rights Reserved.
+
+Abstract
+
+   This document defines the Session Description Protocol, SDP.  SDP is
+   intended for describing multimedia sessions for the purposes of
+   session announcement, session invitation, and other forms of
+   multimedia session initiation.
+
+   This document is a product of the Multiparty Multimedia Session
+   Control (MMUSIC) working group of the Internet Engineering Task
+   Force. Comments are solicited and should be addressed to the working
+   group's mailing list at confctrl@isi.edu and/or the authors.
+
+1.  Introduction
+
+   On the Internet multicast backbone (Mbone), a session directory tool
+   is used to advertise multimedia conferences and communicate the
+   conference addresses and conference tool-specific information
+   necessary for participation.  This document defines a session
+   description protocol for this purpose, and for general real-time
+   multimedia session description purposes. This memo does not describe
+   multicast address allocation or the distribution of SDP messages in
+   detail.  These are described in accompanying memos.  SDP is not
+   intended for negotiation of media encodings.
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                     [Page 1]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+2.  Background
+
+   The Mbone is the part of the internet that supports IP multicast, and
+   thus permits efficient many-to-many communication.  It is used
+   extensively for multimedia conferencing.  Such conferences usually
+   have the property that tight coordination of conference membership is
+   not necessary; to receive a conference, a user at an Mbone site only
+   has to know the conference's multicast group address and the UDP
+   ports for the conference data streams.
+
+   Session directories assist the advertisement of conference sessions
+   and communicate the relevant conference setup information to
+   prospective participants.  SDP is designed to convey such information
+   to recipients.  SDP is purely a format for session description - it
+   does not incorporate a transport protocol, and is intended to use
+   different transport protocols as appropriate including the Session
+   Announcement Protocol [4], Session Initiation Protocol [11], Real-
+   Time Streaming Protocol [12], electronic mail using the MIME
+   extensions, and the Hypertext Transport Protocol.
+
+   SDP is intended to be general purpose so that it can be used for a
+   wider range of network environments and applications than just
+   multicast session directories.  However, it is not intended to
+   support negotiation of session content or media encodings - this is
+   viewed as outside the scope of session description.
+
+3.  Glossary of Terms
+
+   The following terms are used in this document, and have specific
+   meaning within the context of this document.
+
+   Conference
+     A multimedia conference is a set of two or more communicating users
+     along with the software they are using to communicate.
+
+   Session
+     A multimedia session is a set of multimedia senders and receivers
+     and the data streams flowing from senders to receivers.  A
+     multimedia conference is an example of a multimedia session.
+
+   Session Advertisement
+     See session announcement.
+
+   Session Announcement
+     A session announcement is a mechanism by which a session
+     description is conveyed to users in a proactive fashion, i.e., the
+     session description was not explicitly requested by the user.
+
+
+
+
+Handley & Jacobson          Standards Track                     [Page 2]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Session Description
+     A well defined format for conveying sufficient information to
+     discover and participate in a multimedia session.
+
+3.1.  Terminology
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in RFC 2119.
+
+4.  SDP Usage
+
+4.1.  Multicast Announcements
+
+   SDP is a session description protocol for multimedia sessions. A
+   common mode of usage is for a client to announce a conference session
+   by periodically multicasting an announcement packet to a well known
+   multicast address and port using the Session Announcement Protocol
+   (SAP).
+
+   SAP packets are UDP packets with the following format:
+
+         |--------------------|
+         | SAP header         |
+         |--------------------|
+         | text payload       |
+         |//////////
+
+
+   The header is the Session Announcement Protocol header.  SAP is
+   described in more detail in a companion memo [4]
+
+   The text payload is an SDP session description, as described in this
+   memo.  The text payload should be no greater than 1 Kbyte in length.
+   If announced by SAP, only one session announcement is permitted in a
+   single packet.
+
+4.2.  Email and WWW Announcements
+
+   Alternative means of conveying session descriptions include
+   electronic mail and the World Wide Web. For both email and WWW
+   distribution, the use of the MIME content type "application/sdp"
+   should be used.  This enables the automatic launching of applications
+   for participation in the session from the WWW client or mail reader
+   in a standard manner.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                     [Page 3]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Note that announcements of multicast sessions made only via email or
+   the World Wide Web (WWW) do not have the property that the receiver
+   of a session announcement can necessarily receive the session because
+   the multicast sessions may be restricted in scope, and access to the
+   WWW server or reception of email is possible outside this scope.  SAP
+   announcements do not suffer from this mismatch.
+
+5.  Requirements and Recommendations
+
+   The purpose of SDP is to convey information about media streams in
+   multimedia sessions to allow the recipients of a session description
+   to participate in the session.  SDP is primarily intended for use in
+   an internetwork, although it is sufficiently general that it can
+   describe conferences in other network environments.
+
+   A multimedia session, for these purposes, is defined as a set of
+   media streams that exist for some duration of time.  Media streams
+   can be many-to-many.  The times during which the session is active
+   need not be continuous.
+
+   Thus far, multicast based sessions on the Internet have differed from
+   many other forms of conferencing in that anyone receiving the traffic
+   can join the session (unless the session traffic is encrypted).  In
+   such an environment, SDP serves two primary purposes.  It is a means
+   to communicate the existence of a session, and is a means to convey
+   sufficient information to enable joining and participating in the
+   session.  In a unicast environment, only the latter purpose is likely
+   to be relevant.
+
+   Thus SDP includes:
+
+   o Session name and purpose
+
+   o Time(s) the session is active
+
+   o The media comprising the session
+
+   o Information to receive those media (addresses, ports, formats and
+     so on)
+
+   As resources necessary to participate in a session may be limited,
+   some additional information may also be desirable:
+
+   o Information about the bandwidth to be used by the conference
+
+   o Contact information for the person responsible for the session
+
+
+
+
+
+Handley & Jacobson          Standards Track                     [Page 4]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   In general, SDP must convey sufficient information to be able to join
+   a session (with the possible exception of encryption keys) and to
+   announce the resources to be used to non-participants that may need
+   to know.
+
+5.1.  Media Information
+
+   SDP includes:
+
+   o The type of media (video, audio, etc)
+
+   o The transport protocol (RTP/UDP/IP, H.320, etc)
+
+   o The format of the media (H.261 video, MPEG video, etc)
+
+   For an IP multicast session, the following are also conveyed:
+
+   o Multicast address for media
+
+   o Transport Port for media
+
+   This address and port are the destination address and destination
+   port of the multicast stream, whether being sent, received, or both.
+
+   For an IP unicast session, the following are conveyed:
+
+   o Remote address for media
+
+   o Transport port for contact address
+
+   The semantics of this address and port depend on the media and
+   transport protocol defined.  By default, this is the remote address
+   and remote port to which data is sent, and the remote address and
+   local port on which to receive data.  However, some media may define
+   to use these to establish a control channel for the actual media
+   flow.
+
+5.2.  Timing Information
+
+   Sessions may either be bounded or unbounded in time. Whether or not
+   they are bounded, they may be only active at specific times.
+
+   SDP can convey:
+
+   o An arbitrary list of start and stop times bounding the session
+
+   o For each bound, repeat times such as "every Wednesday at 10am for
+     one hour"
+
+
+
+Handley & Jacobson          Standards Track                     [Page 5]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   This timing information is globally consistent, irrespective of local
+   time zone or daylight saving time.
+
+5.3.  Private Sessions
+
+   It is possible to create both public sessions and private sessions.
+   Private sessions will typically be conveyed by encrypting the session
+   description to distribute it.  The details of how encryption is
+   performed are dependent on the mechanism used to convey SDP - see [4]
+   for how this is done for session announcements.
+
+   If a session announcement is private it is possible to use that
+   private announcement to convey encryption keys necessary to decode
+   each of the media in a conference, including enough information to
+   know which encryption scheme is used for each media.
+
+5.4.  Obtaining Further Information about a Session
+
+   A session description should convey enough information to decide
+   whether or not to participate in a session.  SDP may include
+   additional pointers in the form of Universal Resources Identifiers
+   (URIs) for more information about the session.
+
+5.5.  Categorisation
+
+   When many session descriptions are being distributed by SAP or any
+   other advertisement mechanism, it may be desirable to filter
+   announcements that are of interest from those that are not.  SDP
+   supports a categorisation mechanism for sessions that is capable of
+   being automated.
+
+5.6.  Internationalization
+
+   The SDP specification recommends the use of the ISO 10646 character
+   sets in the UTF-8 encoding (RFC 2044) to allow many different
+   languages to be represented.  However, to assist in compact
+   representations, SDP also allows other character sets such as ISO
+   8859-1 to be used when desired.  Internationalization only applies to
+   free-text fields (session name and background information), and not
+   to SDP as a whole.
+
+6.  SDP Specification
+
+   SDP session descriptions are entirely textual using the ISO 10646
+   character set in UTF-8 encoding. SDP field names and attributes names
+   use only the US-ASCII subset of UTF-8, but textual fields and
+   attribute values may use the full ISO 10646 character set.  The
+   textual form, as opposed to a binary encoding such as ASN/1 or XDR,
+
+
+
+Handley & Jacobson          Standards Track                     [Page 6]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   was chosen to enhance portability, to enable a variety of transports
+   to be used (e.g, session description in a MIME email message) and to
+   allow flexible, text-based toolkits (e.g., Tcl/Tk ) to be used to
+   generate and to process session descriptions.  However, since the
+   total bandwidth allocated to all SAP announcements is strictly
+   limited, the encoding is deliberately compact.  Also, since
+   announcements may be transported via very unreliable means (e.g.,
+   email) or damaged by an intermediate caching server, the encoding was
+   designed with strict order and formatting rules so that most errors
+   would result in malformed announcements which could be detected
+   easily and discarded. This also allows rapid discarding of encrypted
+   announcements for which a receiver does not have the correct key.
+
+   An SDP session description consists of a number of lines of text of
+   the form <type>=<value> <type> is always exactly one character and is
+   case-significant.  <value> is a structured text string whose format
+   depends on <type>.  It also will be case-significant unless a
+   specific field defines otherwise.  Whitespace is not permitted either
+   side of the `=' sign. In general <value> is either a number of fields
+   delimited by a single space character or a free format string.
+
+   A session description consists of a session-level description
+   (details that apply to the whole session and all media streams) and
+   optionally several media-level descriptions (details that apply onto
+   to a single media stream).
+
+   An announcement consists of a session-level section followed by zero
+   or more media-level sections.  The session-level part starts with a
+   `v=' line and continues to the first media-level section.  The media
+   description starts with an `m=' line and continues to the next media
+   description or end of the whole session description.  In general,
+   session-level values are the default for all media unless overridden
+   by an equivalent media-level value.
+
+   When SDP is conveyed by SAP, only one session description is allowed
+   per packet.  When SDP is conveyed by other means, many SDP session
+   descriptions may be concatenated together (the `v=' line indicating
+   the start of a session description terminates the previous
+   description).  Some lines in each description are required and some
+   are optional but all must appear in exactly the order given here (the
+   fixed order greatly enhances error detection and allows for a simple
+   parser). Optional items are marked with a `*'.
+
+Session description
+        v=  (protocol version)
+        o=  (owner/creator and session identifier).
+        s=  (session name)
+        i=* (session information)
+
+
+
+Handley & Jacobson          Standards Track                     [Page 7]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+        u=* (URI of description)
+        e=* (email address)
+        p=* (phone number)
+        c=* (connection information - not required if included in all media)
+        b=* (bandwidth information)
+        One or more time descriptions (see below)
+        z=* (time zone adjustments)
+        k=* (encryption key)
+        a=* (zero or more session attribute lines)
+        Zero or more media descriptions (see below)
+
+Time description
+        t=  (time the session is active)
+        r=* (zero or more repeat times)
+
+Media description
+        m=  (media name and transport address)
+        i=* (media title)
+        c=* (connection information - optional if included at session-level)
+        b=* (bandwidth information)
+        k=* (encryption key)
+        a=* (zero or more media attribute lines)
+
+   The set of `type' letters is deliberately small and not intended to
+   be extensible -- SDP parsers must completely ignore any announcement
+   that contains a `type' letter that it does not understand. The
+   `attribute' mechanism ("a=" described below) is the primary means for
+   extending SDP and tailoring it to particular applications or media.
+   Some attributes (the ones listed in this document) have a defined
+   meaning but others may be added on an application-, media- or
+   session-specific basis.  A session directory must ignore any
+   attribute it doesn't understand.
+
+   The connection (`c=') and attribute (`a=') information in the
+   session-level section applies to all the media of that session unless
+   overridden by connection information or an attribute of the same name
+   in the media description.  For instance, in the example below, each
+   media behaves as if it were given a `recvonly' attribute.
+
+   An example SDP description is:
+
+        v=0
+        o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
+        s=SDP Seminar
+        i=A Seminar on the session description protocol
+        u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps
+        e=mjh@isi.edu (Mark Handley)
+        c=IN IP4 224.2.17.12/127
+
+
+
+Handley & Jacobson          Standards Track                     [Page 8]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+        t=2873397496 2873404696
+        a=recvonly
+        m=audio 49170 RTP/AVP 0
+        m=video 51372 RTP/AVP 31
+        m=application 32416 udp wb
+        a=orient:portrait
+
+   Text records such as the session name and information are bytes
+   strings which may contain any byte with the exceptions of 0x00 (Nul),
+   0x0a (ASCII newline) and 0x0d (ASCII carriage return).  The sequence
+   CRLF (0x0d0a) is used to end a record, although parsers should be
+   tolerant and also accept records terminated with a single newline
+   character.  By default these byte strings contain ISO-10646
+   characters in UTF-8 encoding, but this default may be changed using
+   the `charset' attribute.
+
+   Protocol Version
+
+   v=0
+
+   The "v=" field gives the version of the Session Description Protocol.
+   There is no minor version number.
+
+   Origin
+
+   o=<username> <session id> <version> <network type> <address type>
+   <address>
+
+   The "o=" field gives the originator of the session (their username
+   and the address of the user's host) plus a session id and session
+   version number.
+
+   <username> is the user's login on the originating host, or it is "-"
+   if the originating host does not support the concept of user ids.
+   <username> must not contain spaces.  <session id> is a numeric string
+   such that the tuple of <username>, <session id>, <network type>,
+   <address type> and <address> form a globally unique identifier for
+   the session.
+
+   The method of <session id> allocation is up to the creating tool, but
+   it has been suggested that a Network Time Protocol (NTP) timestamp be
+   used to ensure uniqueness [1].
+
+   <version> is a version number for this announcement.  It is needed
+   for proxy announcements to detect which of several announcements for
+   the same session is the most recent.  Again its usage is up to the
+
+
+
+
+
+Handley & Jacobson          Standards Track                     [Page 9]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   creating tool, so long as <version> is increased when a modification
+   is made to the session data.  Again, it is recommended (but not
+   mandatory) that an NTP timestamp is used.
+
+   <network type> is a text string giving the type of network.
+   Initially "IN" is defined to have the meaning "Internet".  <address
+   type> is a text string giving the type of the address that follows.
+   Initially "IP4" and "IP6" are defined.  <address> is the globally
+   unique address of the machine from which the session was created.
+   For an address type of IP4, this is either the fully-qualified domain
+   name of the machine, or the dotted-decimal representation of the IP
+   version 4 address of the machine.  For an address type of IP6, this
+   is either the fully-qualified domain name of the machine, or the
+   compressed textual representation of the IP version 6 address of the
+   machine.  For both IP4 and IP6, the fully-qualified domain name is
+   the form that SHOULD be given unless this is unavailable, in which
+   case the globally unique address may be substituted.  A local IP
+   address MUST NOT be used in any context where the SDP description
+   might leave the scope in which the address is meaningful.
+
+   In general, the "o=" field serves as a globally unique identifier for
+   this version of this session description, and the subfields excepting
+   the version taken together identify the session irrespective of any
+   modifications.
+
+   Session Name
+
+   s=<session name>
+
+   The "s=" field is the session name.  There must be one and only one
+   "s=" field per session description, and it must contain ISO 10646
+   characters (but see also the `charset' attribute below).
+
+   Session and Media Information
+
+   i=<session description>
+
+   The "i=" field is information about the session.  There may be at
+   most one session-level "i=" field per session description, and at
+   most one "i=" field per media. Although it may be omitted, this is
+   discouraged for session announcements, and user interfaces for
+   composing sessions should require text to be entered.  If it is
+   present it must contain ISO 10646 characters (but see also the
+   `charset' attribute below).
+
+   A single "i=" field can also be used for each media definition.  In
+   media definitions, "i=" fields are primarily intended for labeling
+   media streams. As such, they are most likely to be useful when a
+
+
+
+Handley & Jacobson          Standards Track                    [Page 10]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   single session has more than one distinct media stream of the same
+   media type.  An example would be two different whiteboards, one for
+   slides and one for feedback and questions.
+
+   URI
+
+   u=<URI>
+
+   o A URI is a Universal Resource Identifier as used by WWW clients
+
+   o The URI should be a pointer to additional information about the
+     conference
+
+   o This field is optional, but if it is present it should be specified
+     before the first media field
+
+   o No more than one URI field is allowed per session description
+
+
+   Email Address and Phone Number
+
+   e=<email address>
+   p=<phone number>
+
+   o These specify contact information for the person responsible for
+     the conference.  This is not necessarily the same person that
+     created the conference announcement.
+
+   o Either an email field or a phone field must be specified.
+     Additional email and phone fields are allowed.
+
+   o If these are present, they should be specified before the first
+     media field.
+
+   o More than one email or phone field can be given for a session
+     description.
+
+   o Phone numbers should be given in the conventional international
+
+     format - preceded by a "+ and the international country code.
+     There must be a space or a hyphen ("-") between the country code
+     and the rest of the phone number.  Spaces and hyphens may be used
+     to split up a phone field to aid readability if desired. For
+     example:
+
+                   p=+44-171-380-7777 or p=+1 617 253 6011
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 11]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   o Both email addresses and phone numbers can have an optional free
+     text string associated with them, normally giving the name of the
+     person who may be contacted.  This should be enclosed in
+     parenthesis if it is present.  For example:
+
+                        e=mjh@isi.edu (Mark Handley)
+
+     The alternative RFC822 name quoting convention is also allowed for
+     both email addresses and phone numbers.  For example,
+
+                        e=Mark Handley <mjh@isi.edu>
+
+     The free text string should be in the ISO-10646 character set with
+     UTF-8 encoding, or alternatively in ISO-8859-1 or other encodings
+     if the appropriate charset session-level attribute is set.
+
+   Connection Data
+
+   c=<network type> <address type> <connection address>
+
+   The "c=" field contains connection data.
+
+   A session announcement must contain one "c=" field in each media
+   description (see below) or a "c=" field at the session-level.  It may
+   contain a session-level "c=" field and one additional "c=" field per
+   media description, in which case the per-media values override the
+   session-level settings for the relevant media.
+
+   The first sub-field is the network type, which is a text string
+   giving the type of network.  Initially "IN" is defined to have the
+   meaning "Internet".
+
+   The second sub-field is the address type.  This allows SDP to be used
+   for sessions that are not IP based.  Currently only IP4 is defined.
+
+   The third sub-field is the connection address.  Optional extra
+   subfields may be added after the connection address depending on the
+   value of the <address type> field.
+
+   For IP4 addresses, the connection address is defined as follows:
+
+   o Typically the connection address will be a class-D IP multicast
+
+     group address.  If the session is not multicast, then the
+     connection address contains the fully-qualified domain name or the
+     unicast IP address of the expected data source or data relay or
+     data sink as determined by additional attribute fields. It is not
+     expected that fully-qualified domain names or unicast addresses
+
+
+
+Handley & Jacobson          Standards Track                    [Page 12]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     will be given in a session description that is communicated by a
+     multicast announcement, though this is not prohibited.  If a
+     unicast data stream is to pass through a network address
+     translator, the use of a fully-qualified domain name rather than an
+     unicast IP address is RECOMMENDED.  In other cases, the use of an
+     IP address to specify a particular interface on a multi-homed host
+     might be required.  Thus this specification leaves the decision as
+     to which to use up to the individual application, but all
+     applications MUST be able to cope with receiving both formats.
+
+   o Conferences using an IP multicast connection address must also have
+     a time to live (TTL) value present in addition to the multicast
+     address.  The TTL and the address together define the scope with
+     which multicast packets sent in this conference will be sent. TTL
+     values must be in the range 0-255.
+
+     The TTL for the session is appended to the address using a slash as
+     a separator.  An example is:
+
+                           c=IN IP4 224.2.1.1/127
+
+     Hierarchical or layered encoding schemes are data streams where the
+     encoding from a single media source is split into a number of
+     layers.  The receiver can choose the desired quality (and hence
+     bandwidth) by only subscribing to a subset of these layers.  Such
+     layered encodings are normally transmitted in multiple multicast
+     groups to allow multicast pruning.  This technique keeps unwanted
+     traffic from sites only requiring certain levels of the hierarchy.
+     For applications requiring multiple multicast groups, we allow the
+     following notation to be used for the connection address:
+
+            <base multicast address>/<ttl>/<number of addresses>
+
+     If the number of addresses is not given it is assumed to be one.
+     Multicast addresses so assigned are contiguously allocated above
+     the base address, so that, for example:
+
+                          c=IN IP4 224.2.1.1/127/3
+
+     would state that addresses 224.2.1.1, 224.2.1.2 and 224.2.1.3 are
+     to be used at a ttl of 127.  This is semantically identical to
+     including multiple "c=" lines in a media description:
+
+                           c=IN IP4 224.2.1.1/127
+                           c=IN IP4 224.2.1.2/127
+                           c=IN IP4 224.2.1.3/127
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 13]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     Multiple addresses or "c=" lines can only be specified on a per-
+     media basis, and not for a session-level "c=" field.
+
+     It is illegal for the slash notation described above to be used for
+     IP unicast addresses.
+
+   Bandwidth
+
+   b=<modifier>:<bandwidth-value>
+
+   o This specifies the proposed bandwidth to be used by the session or
+     media, and is optional.
+
+   o <bandwidth-value> is in kilobits per second
+
+   o <modifier> is a single alphanumeric word giving the meaning of the
+     bandwidth figure.
+
+   o Two modifiers are initially defined:
+
+   CT Conference Total: An implicit maximum bandwidth is associated with
+     each TTL on the Mbone or within a particular multicast
+     administrative scope region (the Mbone bandwidth vs. TTL limits are
+     given in the MBone FAQ). If the bandwidth of a session or media in
+     a session is different from the bandwidth implicit from the scope,
+     a `b=CT:...' line should be supplied for the session giving the
+     proposed upper limit to the bandwidth used. The primary purpose of
+     this is to give an approximate idea as to whether two or more
+     conferences can co-exist simultaneously.
+
+   AS Application-Specific Maximum: The bandwidth is interpreted to be
+     application-specific, i.e., will be the application's concept of
+     maximum bandwidth.  Normally this will coincide with what is set on
+     the application's "maximum bandwidth" control if applicable.
+
+     Note that CT gives a total bandwidth figure for all the media at
+     all sites.  AS gives a bandwidth figure for a single media at a
+     single site, although there may be many sites sending
+     simultaneously.
+
+   o Extension Mechanism: Tool writers can define experimental bandwidth
+     modifiers by prefixing their modifier with "X-". For example:
+
+                                 b=X-YZ:128
+
+     SDP parsers should ignore bandwidth fields with unknown modifiers.
+     Modifiers should be alpha-numeric and, although no length limit is
+     given, they are recommended to be short.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 14]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Times, Repeat Times and Time Zones
+
+   t=<start time>  <stop time>
+
+   o "t=" fields specify the start and stop times for a conference
+     session.  Multiple "t=" fields may be used if a session is active
+     at multiple irregularly spaced times; each additional "t=" field
+     specifies an additional period of time for which the session will
+     be active.  If the session is active at regular times, an "r="
+     field (see below) should be used in addition to and following a
+     "t=" field - in which case the "t=" field specifies the start and
+     stop times of the repeat sequence.
+
+   o The first and second sub-fields give the start and stop times for
+     the conference respectively.  These values are the decimal
+     representation of Network Time Protocol (NTP) time values in
+     seconds [1].  To convert these values to UNIX time, subtract
+     decimal 2208988800.
+
+   o If the stop-time is set to zero, then the session is not bounded,
+     though it will not become active until after the start-time.  If
+     the start-time is also zero, the session is regarded as permanent.
+
+     User interfaces should strongly discourage the creation of
+     unbounded and permanent sessions as they give no information about
+     when the session is actually going to terminate, and so make
+     scheduling difficult.
+
+     The general assumption may be made, when displaying unbounded
+     sessions that have not timed out to the user, that an unbounded
+     session will only be active until half an hour from the current
+     time or the session start time, whichever is the later.  If
+     behaviour other than this is required, an end-time should be given
+     and modified as appropriate when new information becomes available
+     about when the session should really end.
+
+     Permanent sessions may be shown to the user as never being active
+     unless there are associated repeat times which state precisely when
+     the session will be active.  In general, permanent sessions should
+     not be created for any session expected to have a duration of less
+     than 2 months, and should be discouraged for sessions expected to
+     have a duration of less than 6 months.
+
+     r=<repeat interval> <active duration> <list of offsets from start-
+     time>
+
+   o "r=" fields specify repeat times for a session.  For example, if
+     a session is active at 10am on Monday and 11am on Tuesday for one
+
+
+
+Handley & Jacobson          Standards Track                    [Page 15]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     hour each week for three months, then the <start time> in the
+     corresponding "t=" field would be the NTP representation of 10am on
+     the first Monday, the <repeat interval> would be 1 week, the
+     <active duration> would be 1 hour, and the offsets would be zero
+     and 25 hours. The corresponding "t=" field stop time would be the
+     NTP representation of the end of the last session three months
+     later. By default all fields are in seconds, so the "r=" and "t="
+     fields might be:
+
+                           t=3034423619 3042462419
+                            r=604800 3600 0 90000
+
+    To make announcements more compact, times may also be given in units
+    of days, hours or minutes. The syntax for these is a number
+    immediately followed by a single case-sensitive character.
+    Fractional units are not allowed - a smaller unit should be used
+    instead.  The following unit specification characters are allowed:
+
+                         d - days (86400 seconds)
+                        h - minutes (3600 seconds)
+                         m - minutes (60 seconds)
+         s - seconds (allowed for completeness but not recommended)
+
+   Thus, the above announcement could also have been written:
+
+                               r=7d 1h 0 25h
+
+     Monthly and yearly repeats cannot currently be directly specified
+     with a single SDP repeat time - instead separate "t" fields should
+     be used to explicitly list the session times.
+
+        z=<adjustment time> <offset> <adjustment time> <offset> ....
+
+   o To schedule a repeated session which spans a change from daylight-
+     saving time to standard time or vice-versa, it is necessary to
+     specify offsets from the base repeat times. This is required
+     because different time zones change time at different times of day,
+     different countries change to or from daylight time on different
+     dates, and some countries do not have daylight saving time at all.
+
+     Thus in order to schedule a session that is at the same time winter
+     and summer, it must be possible to specify unambiguously by whose
+     time zone a session is scheduled.  To simplify this task for
+     receivers, we allow the sender to specify the NTP time that a time
+     zone adjustment happens and the offset from the time when the
+     session was first scheduled.  The "z" field allows the sender to
+     specify a list of these adjustment times and offsets from the base
+     time.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 16]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     An example might be:
+
+                        z=2882844526 -1h 2898848070 0
+
+     This specifies that at time 2882844526 the time base by which the
+     session's repeat times are calculated is shifted back by 1 hour,
+     and that at time 2898848070 the session's original time base is
+     restored. Adjustments are always relative to the specified start
+     time - they are not cumulative.
+
+   o    If a session is likely to last several years, it is  expected
+   that
+     the session announcement will be modified periodically rather than
+     transmit several years worth of adjustments in one announcement.
+
+   Encryption Keys
+
+   k=<method>
+   k=<method>:<encryption key>
+
+   o The session description protocol may be used to convey encryption
+     keys.  A key field is permitted before the first media entry (in
+     which case it applies to all media in the session), or for each
+     media entry as required.
+
+   o The format of keys and their usage is outside the scope of this
+     document, but see [3].
+
+   o The method indicates the mechanism to be used to obtain a usable
+     key by external means, or from the encoded encryption key given.
+
+     The following methods are defined:
+
+      k=clear:<encryption key>
+        The encryption key (as described in [3] for  RTP  media  streams
+        under  the  AV  profile)  is  included untransformed in this key
+        field.
+
+      k=base64:<encoded encryption key>
+        The encryption key (as described in [3] for RTP media streams
+        under the AV profile) is included in this key field but has been
+        base64 encoded because it includes characters that are
+        prohibited in SDP.
+
+      k=uri:<URI to obtain key>
+        A Universal Resource Identifier as used by WWW clients is
+        included in this key field.  The URI refers to the data
+        containing the key, and may require additional authentication
+
+
+
+Handley & Jacobson          Standards Track                    [Page 17]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+        before the key can be returned.  When a request is made to the
+        given URI, the MIME content-type of the reply specifies the
+        encoding for the key in the reply.  The key should not be
+        obtained until the user wishes to join the session to reduce
+        synchronisation of requests to the WWW server(s).
+
+      k=prompt
+        No key is included in this SDP description, but the session or
+        media stream referred to by this key field is encrypted.  The
+        user should be prompted for the key when attempting to join the
+        session, and this user-supplied key should then be used to
+        decrypt the media streams.
+
+   Attributes
+
+   a=<attribute>
+   a=<attribute>:<value>
+
+   Attributes are the primary means for extending SDP.  Attributes may
+   be defined to be used as "session-level" attributes, "media-level"
+   attributes, or both.
+
+   A media description may have any number of attributes ("a=" fields)
+   which are media specific.  These are referred to as "media-level"
+   attributes and add information about the media stream.  Attribute
+   fields can also be added before the first media field; these
+   "session-level" attributes convey additional information that applies
+   to the conference as a whole rather than to individual media; an
+   example might be the conference's floor control policy.
+
+   Attribute fields may be of two forms:
+
+   o property attributes.  A property attribute is simply of the form
+     "a=<flag>".  These are binary attributes, and the presence of the
+     attribute conveys that the attribute is a property of the session.
+     An example might be "a=recvonly".
+
+   o value attributes.  A value attribute is of the form
+     "a=<attribute>:<value>".  An example might be that a whiteboard
+     could have the value attribute "a=orient:landscape"
+
+   Attribute interpretation depends on the media tool being invoked.
+   Thus receivers of session descriptions should be configurable in
+   their interpretation of announcements in general and of attributes in
+   particular.
+
+   Attribute names must be in the US-ASCII subset of ISO-10646/UTF-8.
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 18]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Attribute values are byte strings, and MAY use any byte value except
+   0x00 (Nul), 0x0A (LF), and 0x0D (CR). By default, attribute values
+   are to be interpreted as in ISO-10646 character set with UTF-8
+   encoding.  Unlike other text fields, attribute values are NOT
+   normally affected by the `charset' attribute as this would make
+   comparisons against known values problematic.  However, when an
+   attribute is defined, it can be defined to be charset-dependent, in
+   which case it's value should be interpreted in the session charset
+   rather than in ISO-10646.
+
+   Attributes that will be commonly used can be registered with IANA
+   (see Appendix B).  Unregistered attributes should begin with "X-" to
+   prevent inadvertent collision with registered attributes.  In either
+   case, if an attribute is received that is not understood, it should
+   simply be ignored by the receiver.
+
+   Media Announcements
+
+   m=<media> <port> <transport> <fmt list>
+
+   A session description may contain a number of media descriptions.
+   Each media description starts with an "m=" field, and is terminated
+   by either the next "m=" field or by the end of the session
+   description.  A media field also has several sub-fields:
+
+   o The first sub-field is the media type.  Currently defined media are
+     "audio", "video", "application", "data" and "control", though this
+     list may be extended as new communication modalities emerge (e.g.,
+     telepresense).  The difference between "application" and "data" is
+     that the former is a media flow such as whiteboard information, and
+     the latter is bulk-data transfer such as multicasting of program
+     executables which will not typically be displayed to the user.
+     "control" is used to specify an additional conference control
+     channel for the session.
+
+   o The second sub-field is the transport port to which the media
+     stream will be sent.  The meaning of the transport port depends on
+     the network being used as specified in the relevant "c" field and
+     on the transport protocol defined in the third sub-field.  Other
+     ports used by the media application (such as the RTCP port, see
+     [2]) should be derived algorithmically from the base media port.
+
+     Note: For transports based on UDP, the value should be in the range
+     1024 to 65535 inclusive.  For RTP compliance it should be an even
+     number.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 19]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     For applications where hierarchically encoded streams are being
+     sent to a unicast address, it may be necessary to specify multiple
+     transport ports.  This is done using a similar notation to that
+     used for IP multicast addresses in the "c=" field:
+
+          m=<media> <port>/<number of ports> <transport> <fmt list>
+
+     In such a case, the ports used depend on the transport protocol.
+     For RTP, only the even ports are used for data and the
+     corresponding one-higher odd port is used for RTCP.  For example:
+
+                         m=video 49170/2 RTP/AVP 31
+
+     would specify that ports 49170 and 49171 form one RTP/RTCP pair and
+     49172 and 49173 form the second RTP/RTCP pair.  RTP/AVP is the
+     transport protocol and 31 is the format (see below).
+
+     It is illegal for both multiple addresses to be specified in the
+     "c=" field and for multiple ports to be specified in the "m=" field
+     in the same session description.
+
+   o The third sub-field is the transport protocol.  The transport
+     protocol values are dependent on the address-type field in the "c="
+     fields.  Thus a "c=" field of IP4 defines that the transport
+     protocol runs over IP4.  For IP4, it is normally expected that most
+     media traffic will be carried as RTP over UDP.  The following
+     transport protocols are preliminarily defined, but may be extended
+     through registration of new protocols with IANA:
+
+     - RTP/AVP - the IETF's Realtime Transport Protocol using the
+       Audio/Video profile carried over UDP.
+
+     - udp - User Datagram Protocol
+
+     If an application uses a single combined proprietary media format
+     and transport protocol over UDP, then simply specifying the
+     transport protocol as udp and using the format field to distinguish
+     the combined protocol is recommended.  If a transport protocol is
+     used over UDP to carry several distinct media types that need to be
+     distinguished by a session directory, then specifying the transport
+     protocol and media format separately is necessary. RTP is an
+     example of a transport-protocol that carries multiple payload
+     formats that must be distinguished by the session directory for it
+     to know how to start appropriate tools, relays, mixers or
+     recorders.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 20]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     The main reason to specify the transport-protocol in addition to
+     the media format is that the same standard media formats may be
+     carried over different transport protocols even when the network
+     protocol is the same - a historical example is vat PCM audio and
+     RTP PCM audio.  In addition, relays and monitoring tools that are
+     transport-protocol-specific but format-independent are possible.
+
+     For RTP media streams operating under the RTP Audio/Video Profile
+     [3], the protocol field is "RTP/AVP".  Should other RTP profiles be
+     defined in the future, their profiles will be specified in the same
+     way.  For example, the protocol field "RTP/XYZ" would specify RTP
+     operating under a profile whose short name is "XYZ".
+
+   o The fourth and subsequent sub-fields are media formats.  For audio
+     and video, these will normally be a media payload type as defined
+     in the RTP Audio/Video Profile.
+
+     When a list of payload formats is given, this implies that all of
+     these formats may be used in the session, but the first of these
+     formats is the default format for the session.
+
+     For media whose transport protocol is not RTP or UDP the format
+     field is protocol specific.  Such formats should be defined in an
+     additional specification document.
+
+     For media whose transport protocol is RTP, SDP can be used to
+     provide a dynamic binding of media encoding to RTP payload type.
+     The encoding names in the RTP AV Profile do not specify unique
+     audio encodings (in terms of clock rate and number of audio
+     channels), and so they are not used directly in SDP format fields.
+     Instead, the payload type number should be used to specify the
+     format for static payload types and the payload type number along
+     with additional encoding information should be used for dynamically
+     allocated payload types.
+
+     An example of a static payload type is u-law PCM coded single
+     channel audio sampled at 8KHz.  This is completely defined in the
+     RTP Audio/Video profile as payload type 0, so the media field for
+     such a stream sent to UDP port 49232 is:
+
+                           m=video 49232 RTP/AVP 0
+
+     An example of a dynamic payload type is 16 bit linear encoded
+     stereo audio sampled at 16KHz.  If we wish to use dynamic RTP/AVP
+     payload type 98 for such a stream, additional information is
+     required to decode it:
+
+                          m=video 49232 RTP/AVP 98
+
+
+
+Handley & Jacobson          Standards Track                    [Page 21]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+                           a=rtpmap:98 L16/16000/2
+
+     The general form of an rtpmap attribute is:
+
+     a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding
+     parameters>]
+
+     For audio streams, <encoding parameters> may specify the number of
+     audio channels.  This parameter may be omitted if the number of
+     channels is one provided no additional parameters are needed.  For
+     video streams, no encoding parameters are currently specified.
+
+     Additional parameters may be defined in the future, but
+     codecspecific parameters should not be added.  Parameters added to
+     an rtpmap attribute should only be those required for a session
+     directory to make the choice of appropriate media too to
+     participate in a session.  Codec-specific parameters should be
+     added in other attributes.
+
+     Up to one rtpmap attribute can be defined for each media format
+     specified. Thus we might have:
+
+                       m=audio 49230 RTP/AVP 96 97 98
+                             a=rtpmap:96 L8/8000
+                            a=rtpmap:97 L16/8000
+                           a=rtpmap:98 L16/11025/2
+
+     RTP profiles that specify the use of dynamic payload types must
+     define the set of valid encoding names and/or a means to register
+     encoding names if that profile is to be used with SDP.
+
+     Experimental encoding formats can also be specified using rtpmap.
+     RTP formats that are not registered as standard format names must
+     be preceded by "X-".  Thus a new experimental redundant audio
+     stream called GSMLPC using dynamic payload type 99 could be
+     specified as:
+
+                          m=video 49232 RTP/AVP 99
+                          a=rtpmap:99 X-GSMLPC/8000
+
+     Such an experimental encoding requires that any site wishing to
+     receive the media stream has relevant configured state in its
+     session directory to know which tools are appropriate.
+
+     Note that RTP audio formats typically do not include information
+     about the number of samples per packet.  If a non-default (as
+     defined in the RTP Audio/Video Profile) packetisation is required,
+     the "ptime" attribute is used as given below.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 22]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+     For more details on RTP audio and video formats, see [3].
+
+   o Formats for non-RTP media should be registered as MIME content
+     types as described in Appendix B.  For example, the LBL whiteboard
+     application might be registered as MIME content-type application/wb
+     with encoding considerations specifying that it operates over UDP,
+     with no appropriate file format.  In SDP this would then be
+     expressed using a combination of the "media" field and the "fmt"
+     field, as follows:
+
+                         m=application 32416 udp wb
+
+   Suggested Attributes
+
+   The following attributes are suggested.  Since application writers
+   may add new attributes as they are required, this list is not
+   exhaustive.
+
+   a=cat:<category>
+       This attribute gives the dot-separated hierarchical category of
+       the session.  This is to enable a receiver to filter unwanted
+       sessions by category.  It would probably have been a compulsory
+       separate field, except for its experimental nature at this time.
+       It is a session-level attribute, and is not dependent on charset.
+
+   a=keywds:<keywords>
+       Like the cat attribute, this is to assist identifying wanted
+       sessions at the receiver.  This allows a receiver to select
+       interesting session based on keywords describing the purpose of
+       the session.  It is a session-level attribute. It is a charset
+       dependent attribute, meaning that its value should be interpreted
+       in the charset specified for the session description if one is
+       specified, or by default in ISO 10646/UTF-8.
+
+   a=tool:<name and version of tool>
+       This gives the name and version number of the tool used to create
+       the session description.  It is a session-level attribute, and is
+       not dependent on charset.
+
+   a=ptime:<packet time>
+       This gives the length of time in milliseconds represented by the
+       media in a packet. This is probably only meaningful for audio
+       data.  It should not be necessary to know ptime to decode RTP or
+       vat audio, and it is intended as a recommendation for the
+       encoding/packetisation of audio.  It is a media attribute, and is
+       not dependent on charset.
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 23]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   a=recvonly
+       This specifies that the tools should be started in receive-only
+       mode where applicable. It can be either a session or media
+       attribute, and is not dependent on charset.
+
+   a=sendrecv
+       This specifies that the tools should be started in send and
+       receive mode.  This is necessary for interactive conferences with
+       tools such as wb which defaults to receive only mode. It can be
+       either a session or media attribute, and is not dependent on
+       charset.
+
+   a=sendonly
+       This specifies that the tools should be started in send-only
+       mode.  An example may be where a different unicast address is to
+       be used for a traffic destination than for a traffic source. In
+       such a case, two media descriptions may be use, one sendonly and
+       one recvonly. It can be either a session or media attribute, but
+       would normally only be used as a media attribute, and is not
+       dependent on charset.
+
+   a=orient:<whiteboard orientation>
+       Normally this is only used in a whiteboard media specification.
+       It specifies the orientation of a the whiteboard on the screen.
+       It is a media attribute. Permitted values are `portrait',
+       `landscape' and `seascape' (upside down landscape). It is not
+       dependent on charset
+
+   a=type:<conference type>
+       This specifies the type of the conference.  Suggested values are
+       `broadcast', `meeting', `moderated', `test' and `H332'.
+       `recvonly' should be the default for `type:broadcast' sessions,
+       `type:meeting' should imply `sendrecv' and `type:moderated'
+       should indicate the use of a floor control tool and that the
+       media tools are started so as to "mute" new sites joining the
+       conference.
+
+       Specifying the attribute type:H332 indicates that this loosely
+       coupled session is part of a H.332 session as defined in the ITU
+       H.332 specification [10].  Media tools should be started
+       `recvonly'.
+
+       Specifying the attribute type:test is suggested as a hint that,
+       unless explicitly requested otherwise, receivers can safely avoid
+       displaying this session description to users.
+
+       The type attribute is a session-level attribute, and is not
+       dependent on charset.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 24]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   a=charset:<character set>
+       This specifies the character set to be used to display the
+       session name and information data.  By default, the ISO-10646
+       character set in UTF-8 encoding is used. If a more compact
+       representation is required, other character sets may be used such
+       as ISO-8859-1 for Northern European languages.  In particular,
+       the ISO 8859-1 is specified with the following SDP attribute:
+
+                             a=charset:ISO-8859-1
+
+       This is a session-level attribute; if this attribute is present,
+       it must be before the first media field.  The charset specified
+       MUST be one of those registered with IANA, such as ISO-8859-1.
+       The character set identifier is a US-ASCII string and MUST be
+       compared against the IANA identifiers using a case-insensitive
+       comparison.  If the identifier is not recognised or not
+       supported, all strings that are affected by it SHOULD be regarded
+       as byte strings.
+
+       Note that a character set specified MUST still prohibit the use
+       of bytes 0x00 (Nul), 0x0A (LF) and 0x0d (CR). Character sets
+       requiring the use of these characters MUST define a quoting
+       mechanism that prevents these bytes appearing within text fields.
+
+   a=sdplang:<language tag>
+       This can be a session level attribute or a media level attribute.
+       As a session level attribute, it specifies the language for the
+       session description.  As a media level attribute, it specifies
+       the language for any media-level SDP information field associated
+       with that media.  Multiple sdplang attributes can be provided
+       either at session or media level if multiple languages in the
+       session description or media use multiple languages, in which
+       case the order of the attributes indicates the order of
+       importance of the various languages in the session or media from
+       most important to least important.
+
+       In general, sending session descriptions consisting of multiple
+       languages should be discouraged.  Instead, multiple descriptions
+       should be sent describing the session, one in each language.
+       However this is not possible with all transport mechanisms, and
+       so multiple sdplang attributes are allowed although not
+       recommended.
+
+       The sdplang attribute value must be a single RFC 1766 language
+       tag in US-ASCII.  It is not dependent on the charset attribute.
+       An sdplang attribute SHOULD be specified when a session is of
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 25]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+       sufficient scope to cross geographic boundaries where the
+       language of recipients cannot be assumed, or where the session is
+       in a different language from the locally assumed norm.
+
+   a=lang:<language tag>
+       This can be a session level attribute or a media level attribute.
+       As a session level attribute, it specifies the default language
+       for the session being described.  As a media level attribute, it
+       specifies the language for that media, overriding any session-
+       level language specified.  Multiple lang attributes can be
+       provided either at session or media level if multiple languages
+       if the session description or media use multiple languages, in
+       which case the order of the attributes indicates the order of
+       importance of the various languages in the session or media from
+       most important to least important.
+
+       The lang attribute value must be a single RFC 1766 language tag
+       in US-ASCII. It is not dependent on the charset attribute.  A
+       lang attribute SHOULD be specified when a session is of
+       sufficient scope to cross geographic boundaries where the
+       language of recipients cannot be assumed, or where the session is
+       in a different language from the locally assumed norm.
+
+   a=framerate:<frame rate>
+       This gives the maximum video frame rate in frames/sec.  It is
+       intended as a recommendation for the encoding of video data.
+       Decimal representations of fractional values using the notation
+       "<integer>.<fraction>" are allowed.  It is a media attribute, is
+       only defined for video media, and is not dependent on charset.
+
+   a=quality:<quality>
+       This gives a suggestion for the quality of the encoding as an
+       integer value.
+
+       The intention of the quality attribute for video is to specify a
+       non-default trade-off between frame-rate and still-image quality.
+       For video, the value in the range 0 to 10, with the following
+       suggested meaning:
+
+       10 - the best still-image quality the compression scheme can
+       give.
+
+       5 - the default behaviour given no quality suggestion.
+
+       0 - the worst still-image quality the codec designer thinks is
+           still usable.
+
+       It is a media attribute, and is not dependent on charset.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 26]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   a=fmtp:<format> <format specific parameters>
+       This attribute allows parameters that are specific to a
+       particular format to be conveyed in a way that SDP doesn't have
+       to understand them.  The format must be one of the formats
+       specified for the media.  Format-specific parameters may be any
+       set of parameters required to be conveyed by SDP and given
+       unchanged to the media tool that will use this format.
+
+       It is a media attribute, and is not dependent on charset.
+
+6.1.  Communicating Conference Control Policy
+
+   There is some debate over the way conference control policy should be
+   communicated.  In general, the authors believe that an implicit
+   declarative style of specifying conference control is desirable where
+   possible.
+
+   A simple declarative style uses a single conference attribute field
+   before the first media field, possibly supplemented by properties
+   such as `recvonly' for some of the media tools.  This conference
+   attribute conveys the conference control policy. An example might be:
+
+                             a=type:moderated
+
+   In some cases, however, it is possible that this may be insufficient
+   to communicate the details of an unusual conference control policy.
+   If this is the case, then a conference attribute specifying external
+   control might be set, and then one or more "media" fields might be
+   used to specify the conference control tools and configuration data
+   for those tools. An example is an ITU H.332 session:
+
+                c=IN IP4 224.5.6.7
+                a=type:H332
+                m=audio 49230 RTP/AVP 0
+                m=video 49232 RTP/AVP 31
+                m=application 12349 udp wb
+                m=control 49234 H323 mc
+                c=IN IP4 134.134.157.81
+
+   In this example, a general conference attribute (type:H332) is
+   specified stating that conference control will be provided by an
+   external H.332 tool, and a contact addresses for the H.323 session
+   multipoint controller is given.
+
+   In this document, only the declarative style of conference control
+   declaration is specified.  Other forms of conference control should
+   specify an appropriate type attribute, and should define the
+   implications this has for control media.
+
+
+
+Handley & Jacobson          Standards Track                    [Page 27]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+7.  Security Considerations
+
+   SDP is a session description format that describes multimedia
+   sessions.  A session description should not be trusted unless it has
+   been obtained by an authenticated transport protocol from a trusted
+   source.  Many different transport protocols may be used to distribute
+   session description, and the nature of the authentication will differ
+   from transport to transport.
+
+   One transport that will frequently be used to distribute session
+   descriptions is the Session Announcement Protocol (SAP).  SAP
+   provides both encryption and authentication mechanisms but due to the
+   nature of session announcements it is likely that there are many
+   occasions where the originator of a session announcement cannot be
+   authenticated because they are previously unknown to the receiver of
+   the announcement and because no common public key infrastructure is
+   available.
+
+   On receiving a session description over an unauthenticated transport
+   mechanism or from an untrusted party, software parsing the session
+   should take a few precautions. Session description contain
+   information required to start software on the receivers system.
+   Software that parses a session description MUST not be able to start
+   other software except that which is specifically configured as
+   appropriate software to participate in multimedia sessions.  It is
+   normally considered INAPPROPRIATE for software parsing a session
+   description to start, on a user's system, software that is
+   appropriate to participate in multimedia sessions, without the user
+   first being informed that such software will be started and giving
+   their consent.  Thus a session description arriving by session
+   announcement, email, session invitation, or WWW page SHOULD not
+   deliver the user into an {it interactive} multimedia session without
+   the user being aware that this will happen.  As it is not always
+   simple to tell whether a session is interactive or not, applications
+   that are unsure should assume sessions are interactive.
+
+   In this specification, there are no attributes which would allow the
+   recipient of a session description to be informed to start multimedia
+   tools in a mode where they default to transmitting.  Under some
+   circumstances it might be appropriate to define such attributes.  If
+   this is done an application parsing a session description containing
+   such attributes SHOULD either ignore them, or inform the user that
+   joining this session will result in the automatic transmission of
+   multimedia data.  The default behaviour for an unknown attribute is
+   to ignore it.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 28]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Session descriptions may be parsed at intermediate systems such as
+   firewalls for the purposes of opening a hole in the firewall to allow
+   the participation in multimedia sessions.  It is considered
+   INAPPROPRIATE for a firewall to open such holes for unicast data
+   streams unless the session description comes in a request from inside
+   the firewall.
+
+   For multicast sessions, it is likely that local administrators will
+   apply their own policies, but the exclusive use of "local" or "site-
+   local" administrative scope within the firewall and the refusal of
+   the firewall to open a hole for such scopes will provide separation
+   of global multicast sessions from local ones.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 29]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+Appendix A: SDP Grammar
+
+   This appendix provides an Augmented BNF grammar for SDP. ABNF is
+   defined in RFC 2234.
+
+
+   announcement =        proto-version
+                         origin-field
+                         session-name-field
+                         information-field
+                         uri-field
+                         email-fields
+                         phone-fields
+                         connection-field
+                         bandwidth-fields
+                         time-fields
+                         key-field
+                         attribute-fields
+                         media-descriptions
+
+   proto-version =       "v=" 1*DIGIT CRLF
+                         ;this memo describes version 0
+
+   origin-field =        "o=" username space
+                         sess-id space sess-version space
+                         nettype space addrtype space
+                         addr CRLF
+
+   session-name-field =  "s=" text CRLF
+
+   information-field =   ["i=" text CRLF]
+
+   uri-field =           ["u=" uri CRLF]
+
+   email-fields =        *("e=" email-address CRLF)
+
+   phone-fields =        *("p=" phone-number CRLF)
+
+
+   connection-field =    ["c=" nettype space addrtype space
+                         connection-address CRLF]
+                         ;a connection field must be present
+                         ;in every media description or at the
+                         ;session-level
+
+
+   bandwidth-fields =    *("b=" bwtype ":" bandwidth CRLF)
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 30]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   time-fields =         1*( "t=" start-time space stop-time
+                         *(CRLF repeat-fields) CRLF)
+                         [zone-adjustments CRLF]
+
+
+   repeat-fields =       "r=" repeat-interval space typed-time
+                         1*(space typed-time)
+
+
+   zone-adjustments =    time space ["-"] typed-time
+                         *(space time space ["-"] typed-time)
+
+
+   key-field =           ["k=" key-type CRLF]
+
+
+   key-type =            "prompt" |
+                         "clear:" key-data |
+                         "base64:" key-data |
+                         "uri:" uri
+
+
+   key-data =            email-safe | "~" | "
+
+
+   attribute-fields =    *("a=" attribute CRLF)
+
+
+   media-descriptions =  *( media-field
+                         information-field
+                         *(connection-field)
+                         bandwidth-fields
+                         key-field
+                         attribute-fields )
+
+
+   media-field =         "m=" media space port ["/" integer]
+                         space proto 1*(space fmt) CRLF
+
+
+   media =               1*(alpha-numeric)
+                         ;typically "audio", "video", "application"
+                         ;or "data"
+
+   fmt =                 1*(alpha-numeric)
+                         ;typically an RTP payload type for audio
+                         ;and video media
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 31]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   proto =               1*(alpha-numeric)
+                         ;typically "RTP/AVP" or "udp" for IP4
+
+
+   port =                1*(DIGIT)
+                         ;should in the range "1024" to "65535" inclusive
+                         ;for UDP based media
+
+
+   attribute =           (att-field ":" att-value) | att-field
+
+
+   att-field =           1*(alpha-numeric)
+
+
+   att-value =           byte-string
+
+
+   sess-id =             1*(DIGIT)
+                         ;should be unique for this originating username/host
+
+
+   sess-version =        1*(DIGIT)
+                         ;0 is a new session
+
+
+   connection-address =  multicast-address
+                         | addr
+
+
+   multicast-address =   3*(decimal-uchar ".") decimal-uchar "/" ttl
+                         [ "/" integer ]
+                         ;multicast addresses may be in the range
+                         ;224.0.0.0 to 239.255.255.255
+
+   ttl =                 decimal-uchar
+
+   start-time =          time | "0"
+
+   stop-time =           time | "0"
+
+   time =                POS-DIGIT 9*(DIGIT)
+                         ;sufficient for 2 more centuries
+
+
+   repeat-interval =     typed-time
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 32]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   typed-time =          1*(DIGIT) [fixed-len-time-unit]
+
+
+   fixed-len-time-unit = "d" | "h" | "m" | "s"
+
+
+   bwtype =              1*(alpha-numeric)
+
+   bandwidth =           1*(DIGIT)
+
+
+   username =            safe
+                         ;pretty wide definition, but doesn't include space
+
+
+   email-address =       email | email "(" email-safe ")" |
+                         email-safe "<" email ">"
+
+
+   email =               ;defined in RFC822
+
+
+   uri=                  ;defined in RFC1630
+
+
+   phone-number =        phone | phone "(" email-safe ")" |
+                         email-safe "<" phone ">"
+
+
+   phone =               "+" POS-DIGIT 1*(space | "-" | DIGIT)
+                         ;there must be a space or hyphen between the
+                         ;international code and the rest of the number.
+
+
+   nettype =             "IN"
+                         ;list to be extended
+
+
+   addrtype =            "IP4" | "IP6"
+                         ;list to be extended
+
+
+   addr =                FQDN | unicast-address
+
+
+   FQDN =                4*(alpha-numeric|"-"|".")
+                         ;fully qualified domain name as specified in RFC1035
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 33]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   unicast-address =     IP4-address | IP6-address
+
+
+   IP4-address =         b1 "." decimal-uchar "." decimal-uchar "." b4
+   b1 =                  decimal-uchar
+                         ;less than "224"; not "0" or "127"
+   b4 =                  decimal-uchar
+                         ;not "0"
+
+   IP6-address =         ;to be defined
+
+
+   text =                byte-string
+                         ;default is to interpret this as IS0-10646 UTF8
+                         ;ISO 8859-1 requires a "a=charset:ISO-8859-1"
+                         ;session-level attribute to be used
+
+
+   byte-string =         1*(0x01..0x09|0x0b|0x0c|0x0e..0xff)
+                         ;any byte except NUL, CR or LF
+
+
+   decimal-uchar =       DIGIT
+                         | POS-DIGIT DIGIT
+                         | ("1" 2*(DIGIT))
+                         | ("2" ("0"|"1"|"2"|"3"|"4") DIGIT)
+                         | ("2" "5" ("0"|"1"|"2"|"3"|"4"|"5"))
+
+
+   integer =             POS-DIGIT *(DIGIT)
+
+
+   alpha-numeric =       ALPHA | DIGIT
+
+
+   DIGIT =               "0" | POS-DIGIT
+
+
+   POS-DIGIT =           "1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
+
+
+   ALPHA =               "a"|"b"|"c"|"d"|"e"|"f"|"g"|"h"|"i"|"j"|"k"|
+                         "l"|"m"|"n"|"o "|"p"|"q"|"r"|"s"|"t"|"u"|"v"|
+                         "w"|"x"|"y"|"z"|"A"|"B"|"C "|"D"|"E"|"F"|"G"|
+                         "H"|"I"|"J"|"K"|"L"|"M"|"N"|"O"|"P"|" Q"|"R"|
+                         "S"|"T"|"U"|"V"|"W"|"X"|"Y"|"Z"
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 34]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   email-safe =          safe | space | tab
+
+
+   safe =                alpha-numeric |
+                         "'" | "'" | "-" | "." | "/" | ":" | "?" | """ |
+                         "#" | "$" | "&" | "*" | ";" | "=" | "@" | "[" |
+                         "]" | "^" | "_" | "`" | "{" | "|" | "}" | "+" |
+                         "~" | "
+
+
+   space =               %d32
+   tab =                 %d9
+   CRLF =                %d13.10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 35]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+Appendix B: Guidelines for registering SDP names with IANA
+
+   There are seven field names that may be registered with IANA. Using
+   the terminology in the SDP specification BNF, they are "media",
+   "proto", "fmt", "att-field", "bwtype", "nettype" and "addrtype".
+
+   "media" (eg, audio, video, application, data).
+
+       Packetized media types, such as those used by RTP, share the
+       namespace used by media types registry [RFC 2048] (i.e. "MIME
+       types").  The list of valid media names is the set of top-level
+       MIME content types.  The set of media is intended to be small and
+       not to be extended except under rare circumstances.  (The MIME
+       subtype corresponds to the "fmt" parameter below).
+
+   "proto"
+
+       In general this should be an IETF standards-track transport
+       protocol identifier such as RTP/AVP (rfc 1889 under the rfc 1890
+       profile).
+
+       However, people will want to invent their own proprietary
+       transport protocols.  Some of these should be registered as a
+       "fmt" using "udp" as the protocol and some of which probably
+       can't be.
+
+       Where the protocol and the application are intimately linked,
+       such as with the LBL whiteboard wb which used a proprietary and
+       special purpose protocol over UDP, the protocol name should be
+       "udp" and the format name that should be registered is "wb".  The
+       rules for formats (see below) apply to such registrations.
+
+       Where the proprietary transport protocol really carries many
+       different data formats, it is possible to register a new protocol
+       name with IANA. In such a case, an RFC MUST be produced
+       describing the protocol and referenced in the registration.  Such
+       an RFC MAY be informational, although it is preferable if it is
+       standards-track.
+
+   "fmt"
+
+       The format namespace is dependent on the context of the "proto"
+       field, so a format cannot be registered without specifying one or
+       more transport protocols that it applies to.
+
+       Formats cover all the possible encodings that might want to be
+       transported in a multimedia session.
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 36]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+       For RTP formats that have been assigned static payload types, the
+       payload type number is used.  For RTP formats using a dynamic
+       payload type number, the dynamic payload type number is given as
+       the format and an additional "rtpmap" attribute specifies the
+       format and parameters.
+
+       For non-RTP formats, any unregistered format name may be
+       registered through the MIME-type registration process [RFC 2048].
+       The type given here is the MIME subtype only (the top-level MIME
+       content type is specified by the media parameter).  The MIME type
+       registration SHOULD reference a standards-track RFC which
+       describes the transport protocol for this media type.  If there
+       is an existing MIME type for this format, the MIME registration
+       should be augmented to reference the transport specification for
+       this media type.  If there is not an existing MIME type for this
+       format, and there exists no appropriate file format, this should
+       be noted in the encoding considerations as "no appropriate file
+       format".
+
+   "att-field" (Attribute names)
+
+       Attribute field names MAY be registered with IANA, although this
+       is not compulsory, and unknown attributes are simply ignored.
+
+       When an attribute is registered, it must be accompanied by a
+       brief specification stating the following:
+
+       o contact name, email address and telephone number
+
+       o attribute-name (as it will appear in SDP)
+
+       o long-form attribute name in English
+
+       o type of attribute (session level, media level, or both)
+
+       o whether the attribute value is subject to the charset
+       attribute.
+
+       o a one paragraph explanation of the purpose of the attribute.
+
+       o a specification of appropriate attribute values for this
+         attribute.
+
+       IANA will not sanity check such attribute registrations except to
+       ensure that they do not clash with existing registrations.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 37]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+       Although the above is the minimum that IANA will accept, if the
+       attribute is expected to see widespread use and interoperability
+       is an issue, authors are encouraged to produce a standards-track
+       RFC that specifies the attribute more precisely.
+
+       Submitters of registrations should ensure that the specification
+       is in the spirit of SDP attributes, most notably that the
+       attribute is platform independent in the sense that it makes no
+       implicit assumptions about operating systems and does not name
+       specific pieces of software in a manner that might inhibit
+       interoperability.
+
+   "bwtype" (bandwidth specifiers)
+
+       A proliferation of bandwidth specifiers is strongly discouraged.
+
+       New bandwidth specifiers may be registered with IANA.  The
+       submission MUST reference a standards-track RFC specifying the
+       semantics of the bandwidth specifier precisely, and indicating
+       when it should be used, and why the existing registered bandwidth
+       specifiers do not suffice.
+
+   "nettype" (Network Type)
+
+       New network types may be registered with IANA if SDP needs to be
+       used in the context of non-internet environments. Whilst these
+       are not normally the preserve of IANA, there may be circumstances
+       when an Internet application needs to interoperate with a non-
+       internet application, such as when gatewaying an internet
+       telephony call into the PSTN.  The number of network types should
+       be small and should be rarely extended.  A new network type
+       cannot be registered without registering at least one address
+       type to be used with that network type.  A new network type
+       registration MUST reference an RFC which gives details of the
+       network type and address type and specifies how and when they
+       would be used.  Such an RFC MAY be Informational.
+
+   "addrtype" (Address Type)
+
+       New address types may be registered with IANA.  An address type
+       is only meaningful in the context of a network type, and any
+       registration of an address type MUST specify a registered network
+       type, or be submitted along with a network type registration.  A
+       new address type registration MUST reference an RFC giving
+       details of the syntax of the address type.  Such an RFC MAY be
+       Informational.  Address types are not expected to be registered
+       frequently.
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 38]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   Registration Procedure
+
+   To register a name the above guidelines should be followed regarding
+   the required  level  of  documentation  that  is required.  The
+   registration itself should be sent to IANA.  Attribute registrations
+   should  include the  information  given  above.   Other registrations
+   should include the following additional information:
+
+   o contact name, email address and telephone number
+
+   o name being registered (as it will appear in SDP)
+
+   o long-form name in English
+
+   o type of name ("media", "proto", "fmt", "bwtype", "nettype", or
+     "addrtype")
+
+   o a one paragraph explanation of the purpose of the registered name.
+
+   o a reference to the specification (eg RFC number) of the registered
+     name.
+
+   IANA may refer any registration to the IESG or to any appropriate
+   IETF working group for review, and may request revisions to be made
+   before a registration will be made.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 39]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+Appendix C: Authors' Addresses
+
+   Mark Handley
+   Information Sciences Institute
+   c/o MIT Laboratory for Computer Science
+   545 Technology Square
+   Cambridge, MA 02139
+   United States
+   electronic mail: mjh@isi.edu
+
+   Van Jacobson
+   MS 46a-1121
+   Lawrence Berkeley Laboratory
+   Berkeley, CA 94720
+   United States
+   electronic mail: van@ee.lbl.gov
+
+Acknowledgments
+
+   Many people in the IETF MMUSIC working group have made comments and
+   suggestions contributing to this document.  In particular, we would
+   like to thank Eve Schooler, Steve Casner, Bill Fenner, Allison
+   Mankin, Ross Finlayson, Peter Parnes, Joerg Ott, Carsten Bormann, Rob
+   Lanphier and Steve Hanna.
+
+References
+
+   [1] Mills, D., "Network Time Protocol (version 3) specification and
+   implementation", RFC 1305, March 1992.
+
+   [2] Schulzrinne, H., Casner, S., Frederick, R. and V. Jacobson, "RTP:
+   A Transport Protocol for Real-Time Applications", RFC 1889, January
+   1996.
+
+   [3] Schulzrinne, H., "RTP Profile for Audio and Video Conferences
+   with Minimal Control", RFC 1890, January 1996
+
+   [4] Handley, M., "SAP - Session Announcement Protocol", Work in
+   Progress.
+
+   [5] V. Jacobson, S. McCanne, "vat - X11-based audio teleconferencing
+   tool" vat manual page, Lawrence Berkeley Laboratory, 1994.
+
+   [6] The Unicode Consortium, "The Unicode Standard -- Version 2.0",
+   Addison-Wesley, 1996.
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 40]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+   [7] ISO/IEC 10646-1:1993. International Standard -- Information
+   technol- ogy -- Universal Multiple-Octet Coded Character Set (UCS) --
+   Part 1: Architecture and Basic Multilingual Plane.  Five amendments
+   and a techn- ical  corrigendum  have been published up to now.  UTF-8
+   is described in Annex R, published as Amendment 2.
+
+   [8] Goldsmith, D., and M. Davis, "Using Unicode with MIME", RFC 1641,
+   July 1994.
+
+   [9] Yergeau, F., "UTF-8, a transformation format of Unicode and ISO
+   10646", RFC 2044, October 1996.
+
+   [10] ITU-T Recommendation H.332 (1998): "Multimedia Terminal for
+   Receiving Internet-based H.323 Conferences", ITU, Geneva.
+
+   [11] Handley, M., Schooler, E., and H. Schulzrinne, "Session
+   Initiation Protocol (SIP)", Work in Progress.
+
+   [12] Schulzrinne, H., Rao, A., and R. Lanphier, "Real Time Streaming
+   Protocol (RTSP)", RFC 2326, April 1998.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 41]
+\f
+RFC 2327                          SDP                         April 1998
+
+
+Full Copyright Statement
+
+   Copyright (C) The Internet Society (1998).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley & Jacobson          Standards Track                    [Page 42]
+\f
diff --git a/src/modules/rtp/rfc2974.txt b/src/modules/rtp/rfc2974.txt
new file mode 100644 (file)
index 0000000..4a5aa62
--- /dev/null
@@ -0,0 +1,1011 @@
+
+
+
+
+
+
+Network Working Group                                         M. Handley
+Request for Comments: 2974                                         ACIRI
+Category: Experimental                                        C. Perkins
+                                                                 USC/ISI
+                                                               E. Whelan
+                                                                     UCL
+                                                            October 2000
+
+
+                     Session Announcement Protocol
+
+Status of this Memo
+
+   This memo defines an Experimental Protocol for the Internet
+   community.  It does not specify an Internet standard of any kind.
+   Discussion and suggestions for improvement are requested.
+   Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (2000).  All Rights Reserved.
+
+Abstract
+
+   This document describes version 2 of the multicast session directory
+   announcement protocol, Session Announcement Protocol (SAP), and the
+   related issues affecting security and scalability that should be
+   taken into account by implementors.
+
+1  Introduction
+
+   In order to assist the advertisement of multicast multimedia
+   conferences and other multicast sessions, and to communicate the
+   relevant session setup information to prospective participants, a
+   distributed session directory may be used.  An instance of such a
+   session directory periodically multicasts packets containing a
+   description of the session, and these advertisements are received by
+   other session directories such that potential remote participants can
+   use the session description to start the tools required to
+   participate in the session.
+
+   This memo describes the issues involved in the multicast announcement
+   of session description information and defines an announcement
+   protocol to be used.  Sessions are described using the session
+   description protocol which is described in a companion memo [4].
+
+
+
+
+
+
+Handley, et al.               Experimental                      [Page 1]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+2  Terminology
+
+   A SAP announcer periodically multicasts an announcement packet to a
+   well known multicast address and port.  The announcement is multicast
+   with the same scope as the session it is announcing, ensuring that
+   the recipients of the announcement are within the scope of the
+   session the announcement describes (bandwidth and other such
+   constraints permitting).  This is also important for the scalability
+   of the protocol, as it keeps local session announcements local.
+
+   A SAP listener learns of the multicast scopes it is within (for
+   example, using the Multicast-Scope Zone Announcement Protocol [5])
+   and listens on the well known SAP address and port for those scopes.
+   In this manner, it will eventually learn of all the sessions being
+   announced, allowing those sessions to be joined.
+
+   The key words `MUST', `MUST NOT', `REQUIRED', `SHALL', `SHALL NOT',
+   `SHOULD', `SHOULD NOT', `RECOMMENDED', `MAY', and `OPTIONAL' in this
+   document are to be interpreted as described in [1].
+
+3  Session Announcement
+
+   As noted previously, a SAP announcer periodically sends an
+   announcement packet to a well known multicast address and port.
+   There is no rendezvous mechanism - the SAP announcer is not aware of
+   the presence or absence of any SAP listeners - and no additional
+   reliability is provided over the standard best-effort UDP/IP
+   semantics.
+
+   That announcement contains a session description and SHOULD contain
+   an authentication header.  The session description MAY be encrypted
+   although this is NOT RECOMMENDED (see section 7).
+
+   A SAP announcement is multicast with the same scope as the session it
+   is announcing, ensuring that the recipients of the announcement are
+   within the scope of the session the announcement describes. There are
+   a number of possibilities:
+
+   IPv4 global scope sessions use multicast addresses in the range
+      224.2.128.0 - 224.2.255.255 with SAP announcements being sent to
+      224.2.127.254 (note that 224.2.127.255 is used by the obsolete
+      SAPv0 and MUST NOT be used).
+
+
+
+
+
+
+
+
+
+Handley, et al.               Experimental                      [Page 2]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   IPv4 administrative scope sessions using administratively scoped IP
+      multicast as defined in [7].  The multicast address to be used for
+      announcements is the highest multicast address in the relevant
+      administrative scope zone.  For example, if the scope range is
+      239.16.32.0 - 239.16.33.255, then 239.16.33.255 is used for SAP
+      announcements.
+
+   IPv6 sessions are announced on the address FF0X:0:0:0:0:0:2:7FFE
+      where X is the 4-bit scope value.  For example, an announcement
+      for a link-local session assigned the address
+      FF02:0:0:0:0:0:1234:5678, should be advertised on SAP address
+      FF02:0:0:0:0:0:2:7FFE.
+
+   Ensuring that a description is not used by a potential participant
+   outside the session scope is not addressed in this memo.
+
+   SAP announcements MUST be sent on port 9875 and SHOULD be sent with
+   an IP time-to-live of 255 (the use of TTL scoping for multicast is
+   discouraged [7]).
+
+   If a session uses addresses in multiple administrative scope ranges,
+   it is necessary for the announcer to send identical copies of the
+   announcement to each administrative scope range.  It is up to the
+   listeners to parse such multiple announcements as the same session
+   (as identified by the SDP origin field, for example).  The
+   announcement rate for each administrative scope range MUST be
+   calculated separately, as if the multiple announcements were
+   separate.
+
+   Multiple announcers may announce a single session, as an aid to
+   robustness in the face of packet loss and failure of one or more
+   announcers.  The rate at which each announcer repeats its
+   announcement MUST be scaled back such that the total announcement
+   rate is equal to that which a single server would choose.
+   Announcements made in this manner MUST be identical.
+
+   If multiple announcements are being made for a session, then each
+   announcement MUST carry an authentication header signed by the same
+   key, or be treated as a completely separate announcement by
+   listeners.
+
+   An IPv4 SAP listener SHOULD listen on the IPv4 global scope SAP
+   address and on the SAP addresses for each IPv4 administrative scope
+   zone it is within.  The discovery of administrative scope zones is
+   outside the scope of this memo, but it is assumed that each SAP
+   listener within a particular scope zone is aware of that scope zone.
+   A SAP listener which supports IPv6 SHOULD also listen to the IPv6 SAP
+   addresses.
+
+
+
+Handley, et al.               Experimental                      [Page 3]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+3.1 Announcement Interval
+
+   The time period between repetitions of an announcement is chosen such
+   that the total bandwidth used by all announcements on a single SAP
+   group remains below a preconfigured limit.  If not otherwise
+   specified, the bandwidth limit SHOULD be assumed to be 4000 bits per
+   second.
+
+   Each announcer is expected to listen to other announcements in order
+   to determine the total number of sessions being announced on a
+   particular group.  Sessions are uniquely identified by the
+   combination of the message identifier hash and originating source
+   fields of the SAP header (note that SAP v0 announcers always set the
+   message identifier hash to zero, and if such an announcement is
+   received the entire message MUST be compared to determine
+   uniqueness).
+
+   Announcements are made by periodic multicast to the group.  The base
+   interval between announcements is derived from the number of
+   announcements being made in that group, the size of the announcement
+   and the configured bandwidth limit.  The actual transmission time is
+   derived from this base interval as follows:
+
+      1. The announcer initializes the variable tp to be the last time a
+         particular announcement was transmitted (or the current time if
+         this is the first time this announcement is to be made).
+
+      2. Given a configured bandwidth limit in bits/second and an
+         announcement of ad_size bytes, the base announcement interval
+         in seconds is
+
+                interval =max(300; (8*no_of_ads*ad_size)/limit)
+
+      3. An offset is calculated based on the base announcement interval
+
+                offset= rand(interval* 2/3)-(interval/3)
+
+      4. The next transmission time for an announcement derived as
+
+                tn =tp+ interval+ offset
+
+   The announcer then sets a timer to expire at tn and waits.  At time
+   tn the announcer SHOULD recalculate the next transmission time.  If
+   the new value of tn is before the current time, the announcement is
+   sent immediately.  Otherwise the transmission is rescheduled for the
+   new tn.  This reconsideration prevents transient packet bursts on
+   startup and when a network partition heals.
+
+
+
+
+Handley, et al.               Experimental                      [Page 4]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+4  Session Deletion
+
+   Sessions may be deleted in one of several ways:
+
+   Explicit Timeout The session description payload may contain
+      timestamp information specifying the start- and end-times of the
+      session.  If the current time is later than the end-time of the
+      session, then the session SHOULD be deleted from the receiver's
+      session cache.
+
+   Implicit Timeout A session announcement message should be received
+      periodically for each session description in a receiver's session
+      cache.  The announcement period can be predicted by the receiver
+      from the set of sessions currently being announced.  If a session
+      announcement message has not been received for ten times the
+      announcement period, or one hour, whichever is the greater, then
+      the session is deleted from the receiver's session cache.  The one
+      hour minimum is to allow for transient network partitionings.
+
+   Explicit Deletion A session deletion packet is received specifying
+      the session to be deleted.  Session deletion packets SHOULD have a
+      valid authentication header, matching that used to authenticate
+      previous announcement packets.  If this authentication is missing,
+      the deletion message SHOULD be ignored.
+
+5  Session Modification
+
+   A pre-announced session can be modified by simply announcing the
+   modified session description.  In this case, the version hash in the
+   SAP header MUST be changed to indicate to receivers that the packet
+   contents should be parsed (or decrypted and parsed if it is
+   encrypted).  The session itself, as distinct from the session
+   announcement, is uniquely identified by the payload and not by the
+   message identifier hash in the header.
+
+   The same rules apply for session modification as for session
+   deletion:
+
+    o Either the modified announcement must contain an authentication
+      header signed by the same key as the cached session announcement
+      it is modifying, or:
+
+    o The cached session announcement must not contain an authentication
+      header, and the session modification announcement must originate
+      from the same host as the session it is modifying.
+
+
+
+
+
+
+Handley, et al.               Experimental                      [Page 5]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   If an announcement is received containing an authentication header
+   and the cached announcement did not contain an authentication header,
+   or it contained a different authentication header, then the modified
+   announcement MUST be treated as a new and different announcement, and
+   displayed in addition to the un-authenticated announcement.  The same
+   should happen if a modified packet without an authentication header
+   is received from a different source than the original announcement.
+
+   These rules prevent an announcement having an authentication header
+   added by a malicious user and then being deleted using that header,
+   and it also prevents a denial-of-service attack by someone putting
+   out a spoof announcement which, due to packet loss, reaches some
+   participants before the original announcement.  Note that under such
+   circumstances, being able to authenticate the message originator is
+   the only way to discover which session is the correct session.
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | V=1 |A|R|T|E|C|   auth len    |         msg id hash           |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                                                               |
+   :                originating source (32 or 128 bits)            :
+   :                                                               :
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                    optional authentication data               |
+   :                              ....                             :
+   *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
+   |                      optional payload type                    |
+   +                                         +-+- - - - - - - - - -+
+   |                                         |0|                   |
+   + - - - - - - - - - - - - - - - - - - - - +-+                   |
+   |                                                               |
+   :                            payload                            :
+   |                                                               |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                     Figure 1: Packet format
+
+6  Packet Format
+
+   SAP data packets have the format described in figure 1.
+
+   V: Version Number. The version number field MUST be set to 1 (SAPv2
+      announcements which use only SAPv1 features are backwards
+      compatible, those which use new features can be detected by other
+      means, so the SAP version number doesn't need to change).
+
+
+
+
+Handley, et al.               Experimental                      [Page 6]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   A: Address type. If the A bit is 0, the originating source field
+      contains a 32-bit IPv4 address.  If the A bit is 1, the
+      originating source contains a 128-bit IPv6 address.
+
+   R: Reserved. SAP announcers MUST set this to 0, SAP listeners MUST
+      ignore the contents of this field.
+
+   T: Message Type. If the T field is set to 0 this is a session
+      announcement packet, if 1 this is a session deletion packet.
+
+   E: Encryption Bit. If the encryption bit is set to 1, the payload of
+      the SAP packet is encrypted.  If this bit is 0 the packet is not
+      encrypted.  See section 7 for details of the encryption process.
+
+   C: Compressed bit. If the compressed bit is set to 1, the payload is
+      compressed using the zlib compression algorithm [3].  If the
+      payload is to be compressed and encrypted, the compression MUST be
+      performed first.
+
+   Authentication Length. An 8 bit unsigned quantity giving the number
+      of 32 bit words following the main SAP header that contain
+      authentication data.  If it is zero, no authentication header is
+      present.
+
+   Authentication data containing a digital signature of the packet,
+      with length as specified by the authentication length header
+      field.  See section 8 for details of the authentication process.
+
+   Message Identifier Hash. A 16 bit quantity that, used in combination
+      with the originating source, provides a globally unique identifier
+      indicating the precise version of this announcement.  The choice
+      of value for this field is not specified here, except that it MUST
+      be unique for each session announced by a particular SAP announcer
+      and it MUST be changed if the session description is modified (and
+      a session deletion message SHOULD be sent for the old version of
+      the session).
+
+      Earlier versions of SAP used a value of zero to mean that the hash
+      should be ignored and the payload should always be parsed.  This
+      had the unfortunate side-effect that SAP announcers had to study
+      the payload data to determine how many unique sessions were being
+      advertised, making the calculation of the announcement interval
+      more complex that necessary.  In order to decouple the session
+      announcement process from the contents of those announcements, SAP
+      announcers SHOULD NOT set the message identifier hash to zero.
+
+      SAP listeners MAY silently discard messages if the message
+      identifier hash is set to zero.
+
+
+
+Handley, et al.               Experimental                      [Page 7]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   Originating Source. This gives the IP address of the original source
+      of the message.  This is an IPv4 address if the A field is set to
+      zero, else it is an IPv6 address.  The address is stored in
+      network byte order.
+
+      SAPv0 permitted the originating source to be zero if the message
+      identifier hash was also zero.  This practise is no longer legal,
+      and SAP announcers SHOULD NOT set the originating source to zero.
+      SAP listeners MAY silently discard packets with the originating
+      source set to zero.
+
+   The header is followed by an optional payload type field and the
+   payload data itself.  If the E or C bits are set in the header both
+   the payload type and payload are encrypted and/or compressed.
+
+   The payload type field is a MIME content type specifier, describing
+   the format of the payload.  This is a variable length ASCII text
+   string, followed by a single zero byte (ASCII NUL).  The payload type
+   SHOULD be included in all packets.  If the payload type is
+   `application/sdp' both the payload type and its terminating zero byte
+   MAY be omitted, although this is intended for backwards compatibility
+   with SAP v1 listeners only.
+
+   The absence of a payload type field may be noted since the payload
+   section of such a packet will start with an SDP `v=0' field, which is
+   not a legal MIME content type specifier.
+
+   All implementations MUST support payloads of type `application/sdp'
+   [4].  Other formats MAY be supported although since there is no
+   negotiation in SAP an announcer which chooses to use a session
+   description format other than SDP cannot know that the listeners are
+   able to understand the announcement.  A proliferation of payload
+   types in announcements has the potential to lead to severe
+   interoperability problems, and for this reason, the use of non-SDP
+   payloads is NOT RECOMMENDED.
+
+   If the packet is an announcement packet, the payload contains a
+   session description.
+
+   If the packet is a session deletion packet, the payload contains a
+   session deletion message.  If the payload format is `application/sdp'
+   the deletion message is a single SDP line consisting of the origin
+   field of the announcement to be deleted.
+
+   It is desirable for the payload to be sufficiently small that SAP
+   packets do not get fragmented by the underlying network.
+   Fragmentation has a loss multiplier effect, which is known to
+   significantly affect the reliability of announcements.  It is
+
+
+
+Handley, et al.               Experimental                      [Page 8]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   RECOMMENDED that SAP packets are smaller than 1kByte in length,
+   although if it is known that announcements will use a network with a
+   smaller MTU than this, then that SHOULD be used as the maximum
+   recommended packet size.
+
+7  Encrypted Announcements
+
+   An announcement is received by all listeners in the scope to which it
+   is sent.  If an announcement is encrypted, and many of the receivers
+   do not have the encryption key, there is a considerable waste of
+   bandwidth since those receivers cannot use the announcement they have
+   received.  For this reason, the use of encrypted SAP announcements is
+   NOT RECOMMENDED on the global scope SAP group or on administrative
+   scope groups which may have many receivers which cannot decrypt those
+   announcements.
+
+   The opinion of the authors is that encrypted SAP is useful in special
+   cases only, and that the vast majority of scenarios where encrypted
+   SAP has been proposed may be better served by distributing session
+   details using another mechanism.  There are, however, certain
+   scenarios where encrypted announcements may be useful.  For this
+   reason, the encryption bit is included in the SAP header to allow
+   experimentation with encrypted announcements.
+
+   This memo does not specify details of the encryption algorithm to be
+   used or the means by which keys are generated and distributed.  An
+   additional specification should define these, if it is desired to use
+   encrypted SAP.
+
+   Note that if an encrypted announcement is being announced via a
+   proxy, then there may be no way for the proxy to discover that the
+   announcement has been superseded, and so it may continue to relay the
+   old announcement in addition to the new announcement.  SAP provides
+   no mechanism to chain modified encrypted announcements, so it is
+   advisable to announce the unmodified session as deleted for a short
+   time after the modification has occurred.  This does not guarantee
+   that all proxies have deleted the session, and so receivers of
+   encrypted sessions should be prepared to discard old versions of
+   session announcements that they may receive.  In most cases however,
+   the only stateful proxy will be local to (and known to) the sender,
+   and an additional (local-area) protocol involving a handshake for
+   such session modifications can be used to avoid this problem.
+
+   Session announcements that are encrypted with a symmetric algorithm
+   may allow a degree of privacy in the announcement of a session, but
+   it should be recognized that a user in possession of such a key can
+   pass it on to other users who should not be in possession of such a
+   key.  Thus announcements to such a group of key holders cannot be
+
+
+
+Handley, et al.               Experimental                      [Page 9]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   assumed to have come from an authorized key holder unless there is an
+   appropriate authentication header signed by an authorized key holder.
+   In addition the recipients of such encrypted announcements cannot be
+   assumed to only be authorized key holders.  Such encrypted
+   announcements do not provide any real security unless all of the
+   authorized key holders are trusted to maintain security of such
+   session directory keys.  This property is shared by the multicast
+   session tools themselves, where it is possible for an un-trustworthy
+   member of the session to pass on encryption keys to un-authorized
+   users.  However it is likely that keys used for the session tools
+   will be more short lived than those used for session directories.
+
+   Similar considerations should apply when session announcements are
+   encrypted with an asymmetric algorithm, but then it is possible to
+   restrict the possessor(s) of the private key, so that announcements
+   to a key-holder group can not be made, even if one of the untrusted
+   members of the group proves to be un-trustworthy.
+
+                        1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | V=1 |P| Auth  |                                               |
+   +-+-+-+-+-+-+-+-+                                               |
+   |              Format  specific authentication subheader        |
+   :                        ..................                     :
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+    Figure 2:  Format of the authentication data in the SAP header
+
+8  Authenticated Announcements
+
+   The authentication header can be used for two purposes:
+
+    o Verification that changes to a session description or deletion of
+      a session are permitted.
+
+    o Authentication of the identity of the session creator.
+
+   In some circumstances only verification is possible because a
+   certificate signed by a mutually trusted person or authority is not
+   available.  However, under such circumstances, the session originator
+   may still be authenticated to be the same as the session originator
+   of previous sessions claiming to be from the same person.  This may
+   or may not be sufficient depending on the purpose of the session and
+   the people involved.
+
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 10]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   Clearly the key used for the authentication should not be trusted to
+   belong to the session originator unless it has been separately
+   authenticated by some other means, such as being certified by a
+   trusted third party.  Such certificates are not normally included in
+   an SAP header because they take more space than can normally be
+   afforded in an SAP packet, and such verification must therefore take
+   place by some other mechanism.  However, as certified public keys are
+   normally locally cached, authentication of a particular key only has
+   to take place once, rather than every time the session directory
+   retransmits the announcement.
+
+   SAP is not tied to any single authentication mechanism.
+   Authentication data in the header is self-describing, but the precise
+   format depends on the authentication mechanism in use.  The generic
+   format of the authentication data is given in figure 2.  The
+   structure of the format specific authentication subheader, using both
+   the PGP and the CMS formats, is discussed in sections 8.1 and 8.2
+   respectively.  Additional formats may be added in future.
+
+   Version Number, V:  The version number of the authentication format
+      specified by this memo is 1.
+
+   Padding Bit, P:  If necessary the authentication data is padded to be
+      a multiple of 32 bits and the padding bit is set.  In this case
+      the last byte of the authentication data contains the number of
+      padding bytes (including the last byte) that must be discarded.
+
+   Authentication Type, Auth: The authentication type is a  4 bit
+      encoded field that denotes the authentication infrastructure the
+      sender expects the recipients to use to check the authenticity and
+      integrity of the information.  This defines the format of the
+      authentication subheader and can take the values:  0 = PGP format,
+      1 = CMS format.  All other values are undefined and SHOULD be
+      ignored.
+
+   If a SAP packet is to be compressed or encrypted, this MUST be done
+   before the authentication is added.
+
+   The digital signature in the authentication data MUST be calculated
+   over the entire packet, including the header.  The authentication
+   length MUST be set to zero and the authentication data excluded when
+   calculating the digital signature.
+
+   It is to be expected that sessions may be announced by a number of
+   different mechanisms, not only SAP.  For example, a session
+   description may placed on a web page, sent by email or conveyed in a
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 11]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   session initiation protocol.  To ease interoperability with these
+   other mechanisms, application level security is employed, rather than
+   using IPsec authentication headers.
+
+8.1 PGP Authentication
+
+   A full description of the PGP protocol can be found in [2].  When
+   using PGP for SAP authentication the basic format specific
+   authentication subheader comprises a digital signature packet as
+   described in [2].  The signature type MUST be 0x01 which means the
+   signature is that of a canonical text document.
+
+8.2 CMS Authentication
+
+   A full description of the Cryptographic Message Syntax can be found
+   in [6].  The format specific authentication subheader will, in the
+   CMS case, have an ASN.1 ContentInfo type with the ContentType being
+   signedData.
+
+   Use is made of the option available in PKCS#7 to leave the content
+   itself blank as the content which is signed is already present in the
+   packet.  Inclusion of it within the SignedData type would duplicate
+   this data and increase the packet length unnecessarily.  In addition
+   this allows recipients with either no interest in the authentication,
+   or with no mechanism for checking it, to more easily skip the
+   authentication information.
+
+   There SHOULD be only one signerInfo and related fields corresponding
+   to the originator of the SAP announcement.  The signingTime SHOULD be
+   present as a signedAttribute.  However, due to the strict size
+   limitations on the size of SAP packets, certificates and CRLs SHOULD
+   NOT be included in the signedData structure.  It is expected that
+   users of the protocol will have other methods for certificate and CRL
+   distribution.
+
+9  Scalability and caching
+
+   SAP is intended to announce the existence of long-lived wide-area
+   multicast sessions.  It is not an especially timely protocol:
+   sessions are announced by periodic multicast with a repeat rate on
+   the order of tens of minutes, and no enhanced reliability over UDP.
+   This leads to a long startup delay before a complete set of
+   announcements is heard by a listener.  This delay is clearly
+   undesirable for interactive browsing of announced sessions.
+
+   In order to reduce the delays inherent in SAP, it is recommended that
+   proxy caches are deployed.  A SAP proxy cache is expected to listen
+   to all SAP groups in its scope, and to maintain an up-to-date list of
+
+
+
+Handley, et al.               Experimental                     [Page 12]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+   all announced sessions along with the time each announcement was last
+   received.  When a new SAP listeners starts, it should contact its
+   local proxy to download this information, which is then sufficient
+   for it to process future announcements directly, as if it has been
+   continually listening.
+
+   The protocol by which a SAP listener contacts its local proxy cache
+   is not specified here.
+
+10 Security Considerations
+
+   SAP contains mechanisms for ensuring integrity of session
+   announcements, for authenticating the origin of an announcement and
+   for encrypting such announcements (sections 7 and 8).
+
+   As stated in section 5, if a session modification announcement is
+   received that contains a valid authentication header, but which is
+   not signed by the original creator of the session, then the session
+   must be treated as a new session in addition to the original session
+   with the same SDP origin information unless the originator of one of
+   the session descriptions can be authenticated using a certificate
+   signed by a trusted third party.  If this were not done, there would
+   be a possible denial of service attack whereby a party listens for
+   new announcements, strips off the original authentication header,
+   modifies the session description, adds a new authentication header
+   and re-announces the session.  If a rule was imposed that such spoof
+   announcements were ignored, then if packet loss or late starting of a
+   session directory instance caused the original announcement to fail
+   to arrive at a site, but the spoof announcement did so, this would
+   then prevent the original announcement from being accepted at that
+   site.
+
+   A similar denial-of-service attack is possible if a session
+   announcement receiver relies completely on the originating source and
+   hash fields to indicate change, and fails to parse the remainder of
+   announcements for which it has seen the origin/hash combination
+   before.
+
+   A denial of service attack is possible from a malicious site close to
+   a legitimate site which is making a session announcement.  This can
+   happen if the malicious site floods the legitimate site with huge
+   numbers of (illegal) low TTL announcements describing high TTL
+   sessions.  This may reduce the session announcement rate of the
+   legitimate announcement to below a tenth of the rate expected at
+   remote sites and therefore cause the session to time out.  Such an
+   attack is likely to be easily detectable, and we do not provide any
+   mechanism here to prevent it.
+
+
+
+
+Handley, et al.               Experimental                     [Page 13]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+A. Summary of differences between SAPv0 and SAPv1
+
+   For this purpose SAPv0 is defined as the protocol in use by version
+   2.2 of the session directory tool, sdr.  SAPv1 is the protocol
+   described in the 19 November 1996 version of this memo.  The packet
+   headers of SAP messages are the same in V0 and V1 in that a V1 tool
+   can parse a V0 announcement header but not vice-versa.  In SAPv0, the
+   fields have the following values:
+
+     o Version Number:  0
+
+     o Message Type:  0 (Announcement)
+
+     o Authentication Type:  0 (No Authentication)
+
+     o Encryption Bit:  0 (No Encryption)
+
+     o Compression Bit:  0 (No compression)
+
+     o Message Id Hash:  0 (No Hash Specified)
+
+     o Originating Source:  0 (No source specified, announcement has
+       not been relayed)
+
+B. Summary of differences between SAPv1 and SAPv2
+
+   The packet headers of SAP messages are the same in V1 and V2 in that
+   a V2 tool can parse a V1 announcement header but not necessarily
+   vice-versa.
+
+    o The A bit has been added to the SAP header, replacing one of the
+      bits of the SAPv1 message type field.  If set to zero the
+      announcement is of an IPv4 session, and the packet is backwards
+      compatible with SAPv1.  If set to one the announcement is of an
+      IPv6 session, and SAPv1 listeners (which do not support IPv6) will
+      see this as an illegal message type (MT) field.
+
+    o The second bit of the message type field in SAPv1 has been
+      replaced by a reserved, must-be-zero, bit.  This bit was unused in
+      SAPv1, so this change just codifies existing usage.
+
+    o SAPv1 specified encryption of the payload.  SAPv2 includes the E
+      bit in the SAP header to indicate that the payload is encrypted,
+      but does not specify any details of the encryption.
+
+    o SAPv1 allowed the message identifier hash and originating source
+      fields to be set to zero, for backwards compatibility.  This is no
+      longer legal.
+
+
+
+Handley, et al.               Experimental                     [Page 14]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+    o SAPv1 specified gzip compression.  SAPv2 uses zlib (the only known
+      implementation of SAP compression used zlib, and gzip compression
+      was a mistake).
+
+    o SAPv2 provides a more complete specification for authentication.
+
+    o SAPv2 allows for non-SDP payloads to be transported.  SAPv1
+      required that the payload was SDP.
+
+    o SAPv1 included a timeout field for encrypted announcement, SAPv2
+      does not (and relies of explicit deletion messages or implicit
+      timeouts).
+
+C. Acknowledgements
+
+   SAP and SDP were originally based on the protocol used by the sd
+   session directory from Van Jacobson at LBNL.  Version 1 of SAP was
+   designed by Mark Handley as part of the European Commission MICE
+   (Esprit 7602) and MERCI (Telematics 1007) projects.  Version 2
+   includes authentication features developed by Edmund Whelan, Goli
+   Montasser-Kohsari and Peter Kirstein as part of the European
+   Commission ICE-TEL project (Telematics 1005), and support for IPv6
+   developed by Maryann P. Maher and Colin Perkins.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 15]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+D. Authors' Addresses
+
+   Mark Handley
+   AT&T Center for Internet Research at ICSI,
+   International Computer Science Institute,
+   1947 Center Street, Suite 600,
+   Berkeley, CA 94704, USA
+
+   EMail: mjh@aciri.org
+
+
+   Colin Perkins
+   USC Information Sciences Institute
+   4350 N. Fairfax Drive, Suite 620
+   Arlington, VA 22203, USA
+
+   EMail: csp@isi.edu
+
+
+   Edmund Whelan
+   Department of Computer Science,
+   University College London,
+   Gower Street,
+   London, WC1E 6BT, UK
+
+   EMail: e.whelan@cs.ucl.ac.uk
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 16]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+References
+
+   [1] Bradner, S., "Key words for use in RFCs to indicate requirement
+       levels", BCP 14, RFC 2119, March 1997.
+
+   [2] Callas, J., Donnerhacke, L., Finney, H. and R. Thayer. "OpenPGP
+       message format", RFC 2440, November 1998.
+
+   [3] Deutsch, P. and J.-L. Gailly, "Zlib compressed data format
+       specification version 3.3", RFC 1950, May 1996.
+
+   [4] Handley, M. and V. Jacobson, "SDP: Session Description Protocol",
+       RFC 2327, April 1998.
+
+   [5] Handley, M., Thaler, D. and R. Kermode, "Multicast-scope zone
+       announcement protocol (MZAP)", RFC 2776, February 2000.
+
+   [6] Housley, R., "Cryptographic message syntax", RFC 2630, June 1999.
+
+   [7] Mayer, D., "Administratively scoped IP multicast", RFC 2365, July
+       1998.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 17]
+\f
+RFC 2974             Session Announcement Protocol          October 2000
+
+
+Full Copyright Statement
+
+   Copyright (C) The Internet Society (2000).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+   Funding for the RFC Editor function is currently provided by the
+   Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Handley, et al.               Experimental                     [Page 18]
+\f
diff --git a/src/modules/rtp/rfc3550.txt b/src/modules/rtp/rfc3550.txt
new file mode 100644 (file)
index 0000000..165736c
--- /dev/null
@@ -0,0 +1,5827 @@
+
+
+
+
+
+
+Network Working Group                                     H. Schulzrinne
+Request for Comments: 3550                           Columbia University
+Obsoletes: 1889                                               S.  Casner
+Category: Standards Track                                  Packet Design
+                                                            R. Frederick
+                                                  Blue Coat Systems Inc.
+                                                             V. Jacobson
+                                                           Packet Design
+                                                               July 2003
+
+
+          RTP: A Transport Protocol for Real-Time Applications
+
+Status of this Memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+Abstract
+
+   This memorandum describes RTP, the real-time transport protocol.  RTP
+   provides end-to-end network transport functions suitable for
+   applications transmitting real-time data, such as audio, video or
+   simulation data, over multicast or unicast network services.  RTP
+   does not address resource reservation and does not guarantee
+   quality-of-service for real-time services.  The data transport is
+   augmented by a control protocol (RTCP) to allow monitoring of the
+   data delivery in a manner scalable to large multicast networks, and
+   to provide minimal control and identification functionality.  RTP and
+   RTCP are designed to be independent of the underlying transport and
+   network layers.  The protocol supports the use of RTP-level
+   translators and mixers.
+
+   Most of the text in this memorandum is identical to RFC 1889 which it
+   obsoletes.  There are no changes in the packet formats on the wire,
+   only changes to the rules and algorithms governing how the protocol
+   is used.  The biggest change is an enhancement to the scalable timer
+   algorithm for calculating when to send RTCP packets in order to
+   minimize transmission in excess of the intended rate when many
+   participants join a session simultaneously.
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 1]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+Table of Contents
+
+   1.  Introduction ................................................   4
+       1.1  Terminology ............................................   5
+   2.  RTP Use Scenarios ...........................................   5
+       2.1  Simple Multicast Audio Conference ......................   6
+       2.2  Audio and Video Conference .............................   7
+       2.3  Mixers and Translators .................................   7
+       2.4  Layered Encodings ......................................   8
+   3.  Definitions .................................................   8
+   4.  Byte Order, Alignment, and Time Format ......................  12
+   5.  RTP Data Transfer Protocol ..................................  13
+       5.1  RTP Fixed Header Fields ................................  13
+       5.2  Multiplexing RTP Sessions ..............................  16
+       5.3  Profile-Specific Modifications to the RTP Header .......  18
+            5.3.1  RTP Header Extension ............................  18
+   6.  RTP Control Protocol -- RTCP ................................  19
+       6.1  RTCP Packet Format .....................................  21
+       6.2  RTCP Transmission Interval .............................  24
+            6.2.1  Maintaining the Number of Session Members .......  28
+       6.3  RTCP Packet Send and Receive Rules .....................  28
+            6.3.1  Computing the RTCP Transmission Interval ........  29
+            6.3.2  Initialization ..................................  30
+            6.3.3  Receiving an RTP or Non-BYE RTCP Packet .........  31
+            6.3.4  Receiving an RTCP BYE Packet ....................  31
+            6.3.5  Timing Out an SSRC ..............................  32
+            6.3.6  Expiration of Transmission Timer ................  32
+            6.3.7  Transmitting a BYE Packet .......................  33
+            6.3.8  Updating we_sent ................................  34
+            6.3.9  Allocation of Source Description Bandwidth ......  34
+       6.4  Sender and Receiver Reports ............................  35
+            6.4.1  SR: Sender Report RTCP Packet ...................  36
+            6.4.2  RR: Receiver Report RTCP Packet .................  42
+            6.4.3  Extending the Sender and Receiver Reports .......  42
+            6.4.4  Analyzing Sender and Receiver Reports ...........  43
+       6.5  SDES: Source Description RTCP Packet ...................  45
+            6.5.1  CNAME: Canonical End-Point Identifier SDES Item .  46
+            6.5.2  NAME: User Name SDES Item .......................  48
+            6.5.3  EMAIL: Electronic Mail Address SDES Item ........  48
+            6.5.4  PHONE: Phone Number SDES Item ...................  49
+            6.5.5  LOC: Geographic User Location SDES Item .........  49
+            6.5.6  TOOL: Application or Tool Name SDES Item ........  49
+            6.5.7  NOTE: Notice/Status SDES Item ...................  50
+            6.5.8  PRIV: Private Extensions SDES Item ..............  50
+       6.6  BYE: Goodbye RTCP Packet ...............................  51
+       6.7  APP: Application-Defined RTCP Packet ...................  52
+   7.  RTP Translators and Mixers ..................................  53
+       7.1  General Description ....................................  53
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 2]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+       7.2  RTCP Processing in Translators .........................  55
+       7.3  RTCP Processing in Mixers ..............................  57
+       7.4  Cascaded Mixers ........................................  58
+   8.  SSRC Identifier Allocation and Use ..........................  59
+       8.1  Probability of Collision ...............................  59
+       8.2  Collision Resolution and Loop Detection ................  60
+       8.3  Use with Layered Encodings .............................  64
+   9.  Security ....................................................  65
+       9.1  Confidentiality ........................................  65
+       9.2  Authentication and Message Integrity ...................  67
+   10. Congestion Control ..........................................  67
+   11. RTP over Network and Transport Protocols ....................  68
+   12. Summary of Protocol Constants ...............................  69
+       12.1 RTCP Packet Types ......................................  70
+       12.2 SDES Types .............................................  70
+   13. RTP Profiles and Payload Format Specifications ..............  71
+   14. Security Considerations .....................................  73
+   15. IANA Considerations .........................................  73
+   16. Intellectual Property Rights Statement ......................  74
+   17. Acknowledgments .............................................  74
+   Appendix A.   Algorithms ........................................  75
+   Appendix A.1  RTP Data Header Validity Checks ...................  78
+   Appendix A.2  RTCP Header Validity Checks .......................  82
+   Appendix A.3  Determining Number of Packets Expected and Lost ...  83
+   Appendix A.4  Generating RTCP SDES Packets ......................  84
+   Appendix A.5  Parsing RTCP SDES Packets .........................  85
+   Appendix A.6  Generating a Random 32-bit Identifier .............  85
+   Appendix A.7  Computing the RTCP Transmission Interval ..........  87
+   Appendix A.8  Estimating the Interarrival Jitter ................  94
+   Appendix B.   Changes from RFC 1889 .............................  95
+   References ...................................................... 100
+   Normative References ............................................ 100
+   Informative References .......................................... 100
+   Authors' Addresses .............................................. 103
+   Full Copyright Statement ........................................ 104
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 3]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+1. Introduction
+
+   This memorandum specifies the real-time transport protocol (RTP),
+   which provides end-to-end delivery services for data with real-time
+   characteristics, such as interactive audio and video.  Those services
+   include payload type identification, sequence numbering, timestamping
+   and delivery monitoring.  Applications typically run RTP on top of
+   UDP to make use of its multiplexing and checksum services; both
+   protocols contribute parts of the transport protocol functionality.
+   However, RTP may be used with other suitable underlying network or
+   transport protocols (see Section 11).  RTP supports data transfer to
+   multiple destinations using multicast distribution if provided by the
+   underlying network.
+
+   Note that RTP itself does not provide any mechanism to ensure timely
+   delivery or provide other quality-of-service guarantees, but relies
+   on lower-layer services to do so.  It does not guarantee delivery or
+   prevent out-of-order delivery, nor does it assume that the underlying
+   network is reliable and delivers packets in sequence.  The sequence
+   numbers included in RTP allow the receiver to reconstruct the
+   sender's packet sequence, but sequence numbers might also be used to
+   determine the proper location of a packet, for example in video
+   decoding, without necessarily decoding packets in sequence.
+
+   While RTP is primarily designed to satisfy the needs of multi-
+   participant multimedia conferences, it is not limited to that
+   particular application.  Storage of continuous data, interactive
+   distributed simulation, active badge, and control and measurement
+   applications may also find RTP applicable.
+
+   This document defines RTP, consisting of two closely-linked parts:
+
+   o  the real-time transport protocol (RTP), to carry data that has
+      real-time properties.
+
+   o  the RTP control protocol (RTCP), to monitor the quality of service
+      and to convey information about the participants in an on-going
+      session.  The latter aspect of RTCP may be sufficient for "loosely
+      controlled" sessions, i.e., where there is no explicit membership
+      control and set-up, but it is not necessarily intended to support
+      all of an application's control communication requirements.  This
+      functionality may be fully or partially subsumed by a separate
+      session control protocol, which is beyond the scope of this
+      document.
+
+   RTP represents a new style of protocol following the principles of
+   application level framing and integrated layer processing proposed by
+   Clark and Tennenhouse [10].  That is, RTP is intended to be malleable
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 4]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   to provide the information required by a particular application and
+   will often be integrated into the application processing rather than
+   being implemented as a separate layer.  RTP is a protocol framework
+   that is deliberately not complete.  This document specifies those
+   functions expected to be common across all the applications for which
+   RTP would be appropriate.  Unlike conventional protocols in which
+   additional functions might be accommodated by making the protocol
+   more general or by adding an option mechanism that would require
+   parsing, RTP is intended to be tailored through modifications and/or
+   additions to the headers as needed.  Examples are given in Sections
+   5.3 and 6.4.3.
+
+   Therefore, in addition to this document, a complete specification of
+   RTP for a particular application will require one or more companion
+   documents (see Section 13):
+
+   o  a profile specification document, which defines a set of payload
+      type codes and their mapping to payload formats (e.g., media
+      encodings).  A profile may also define extensions or modifications
+      to RTP that are specific to a particular class of applications.
+      Typically an application will operate under only one profile.  A
+      profile for audio and video data may be found in the companion RFC
+      3551 [1].
+
+   o  payload format specification documents, which define how a
+      particular payload, such as an audio or video encoding, is to be
+      carried in RTP.
+
+   A discussion of real-time services and algorithms for their
+   implementation as well as background discussion on some of the RTP
+   design decisions can be found in [11].
+
+1.1 Terminology
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in BCP 14, RFC 2119 [2]
+   and indicate requirement levels for compliant RTP implementations.
+
+2. RTP Use Scenarios
+
+   The following sections describe some aspects of the use of RTP.  The
+   examples were chosen to illustrate the basic operation of
+   applications using RTP, not to limit what RTP may be used for.  In
+   these examples, RTP is carried on top of IP and UDP, and follows the
+   conventions established by the profile for audio and video specified
+   in the companion RFC 3551.
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 5]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+2.1 Simple Multicast Audio Conference
+
+   A working group of the IETF meets to discuss the latest protocol
+   document, using the IP multicast services of the Internet for voice
+   communications.  Through some allocation mechanism the working group
+   chair obtains a multicast group address and pair of ports.  One port
+   is used for audio data, and the other is used for control (RTCP)
+   packets.  This address and port information is distributed to the
+   intended participants.  If privacy is desired, the data and control
+   packets may be encrypted as specified in Section 9.1, in which case
+   an encryption key must also be generated and distributed.  The exact
+   details of these allocation and distribution mechanisms are beyond
+   the scope of RTP.
+
+   The audio conferencing application used by each conference
+   participant sends audio data in small chunks of, say, 20 ms duration.
+   Each chunk of audio data is preceded by an RTP header; RTP header and
+   data are in turn contained in a UDP packet.  The RTP header indicates
+   what type of audio encoding (such as PCM, ADPCM or LPC) is contained
+   in each packet so that senders can change the encoding during a
+   conference, for example, to accommodate a new participant that is
+   connected through a low-bandwidth link or react to indications of
+   network congestion.
+
+   The Internet, like other packet networks, occasionally loses and
+   reorders packets and delays them by variable amounts of time.  To
+   cope with these impairments, the RTP header contains timing
+   information and a sequence number that allow the receivers to
+   reconstruct the timing produced by the source, so that in this
+   example, chunks of audio are contiguously played out the speaker
+   every 20 ms.  This timing reconstruction is performed separately for
+   each source of RTP packets in the conference.  The sequence number
+   can also be used by the receiver to estimate how many packets are
+   being lost.
+
+   Since members of the working group join and leave during the
+   conference, it is useful to know who is participating at any moment
+   and how well they are receiving the audio data.  For that purpose,
+   each instance of the audio application in the conference periodically
+   multicasts a reception report plus the name of its user on the RTCP
+   (control) port.  The reception report indicates how well the current
+   speaker is being received and may be used to control adaptive
+   encodings.  In addition to the user name, other identifying
+   information may also be included subject to control bandwidth limits.
+   A site sends the RTCP BYE packet (Section 6.6) when it leaves the
+   conference.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 6]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+2.2 Audio and Video Conference
+
+   If both audio and video media are used in a conference, they are
+   transmitted as separate RTP sessions.  That is, separate RTP and RTCP
+   packets are transmitted for each medium using two different UDP port
+   pairs and/or multicast addresses.  There is no direct coupling at the
+   RTP level between the audio and video sessions, except that a user
+   participating in both sessions should use the same distinguished
+   (canonical) name in the RTCP packets for both so that the sessions
+   can be associated.
+
+   One motivation for this separation is to allow some participants in
+   the conference to receive only one medium if they choose.  Further
+   explanation is given in Section 5.2.  Despite the separation,
+   synchronized playback of a source's audio and video can be achieved
+   using timing information carried in the RTCP packets for both
+   sessions.
+
+2.3 Mixers and Translators
+
+   So far, we have assumed that all sites want to receive media data in
+   the same format.  However, this may not always be appropriate.
+   Consider the case where participants in one area are connected
+   through a low-speed link to the majority of the conference
+   participants who enjoy high-speed network access.  Instead of forcing
+   everyone to use a lower-bandwidth, reduced-quality audio encoding, an
+   RTP-level relay called a mixer may be placed near the low-bandwidth
+   area.  This mixer resynchronizes incoming audio packets to
+   reconstruct the constant 20 ms spacing generated by the sender, mixes
+   these reconstructed audio streams into a single stream, translates
+   the audio encoding to a lower-bandwidth one and forwards the lower-
+   bandwidth packet stream across the low-speed link.  These packets
+   might be unicast to a single recipient or multicast on a different
+   address to multiple recipients.  The RTP header includes a means for
+   mixers to identify the sources that contributed to a mixed packet so
+   that correct talker indication can be provided at the receivers.
+
+   Some of the intended participants in the audio conference may be
+   connected with high bandwidth links but might not be directly
+   reachable via IP multicast.  For example, they might be behind an
+   application-level firewall that will not let any IP packets pass.
+   For these sites, mixing may not be necessary, in which case another
+   type of RTP-level relay called a translator may be used.  Two
+   translators are installed, one on either side of the firewall, with
+   the outside one funneling all multicast packets received through a
+   secure connection to the translator inside the firewall.  The
+   translator inside the firewall sends them again as multicast packets
+   to a multicast group restricted to the site's internal network.
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 7]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Mixers and translators may be designed for a variety of purposes.  An
+   example is a video mixer that scales the images of individual people
+   in separate video streams and composites them into one video stream
+   to simulate a group scene.  Other examples of translation include the
+   connection of a group of hosts speaking only IP/UDP to a group of
+   hosts that understand only ST-II, or the packet-by-packet encoding
+   translation of video streams from individual sources without
+   resynchronization or mixing.  Details of the operation of mixers and
+   translators are given in Section 7.
+
+2.4 Layered Encodings
+
+   Multimedia applications should be able to adjust the transmission
+   rate to match the capacity of the receiver or to adapt to network
+   congestion.  Many implementations place the responsibility of rate-
+   adaptivity at the source.  This does not work well with multicast
+   transmission because of the conflicting bandwidth requirements of
+   heterogeneous receivers.  The result is often a least-common
+   denominator scenario, where the smallest pipe in the network mesh
+   dictates the quality and fidelity of the overall live multimedia
+   "broadcast".
+
+   Instead, responsibility for rate-adaptation can be placed at the
+   receivers by combining a layered encoding with a layered transmission
+   system.  In the context of RTP over IP multicast, the source can
+   stripe the progressive layers of a hierarchically represented signal
+   across multiple RTP sessions each carried on its own multicast group.
+   Receivers can then adapt to network heterogeneity and control their
+   reception bandwidth by joining only the appropriate subset of the
+   multicast groups.
+
+   Details of the use of RTP with layered encodings are given in
+   Sections 6.3.9, 8.3 and 11.
+
+3. Definitions
+
+   RTP payload: The data transported by RTP in a packet, for
+      example audio samples or compressed video data.  The payload
+      format and interpretation are beyond the scope of this document.
+
+   RTP packet: A data packet consisting of the fixed RTP header, a
+      possibly empty list of contributing sources (see below), and the
+      payload data.  Some underlying protocols may require an
+      encapsulation of the RTP packet to be defined.  Typically one
+      packet of the underlying protocol contains a single RTP packet,
+      but several RTP packets MAY be contained if permitted by the
+      encapsulation method (see Section 11).
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 8]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   RTCP packet: A control packet consisting of a fixed header part
+      similar to that of RTP data packets, followed by structured
+      elements that vary depending upon the RTCP packet type.  The
+      formats are defined in Section 6.  Typically, multiple RTCP
+      packets are sent together as a compound RTCP packet in a single
+      packet of the underlying protocol; this is enabled by the length
+      field in the fixed header of each RTCP packet.
+
+   Port: The "abstraction that transport protocols use to
+      distinguish among multiple destinations within a given host
+      computer.  TCP/IP protocols identify ports using small positive
+      integers." [12] The transport selectors (TSEL) used by the OSI
+      transport layer are equivalent to ports.  RTP depends upon the
+      lower-layer protocol to provide some mechanism such as ports to
+      multiplex the RTP and RTCP packets of a session.
+
+   Transport address: The combination of a network address and port
+      that identifies a transport-level endpoint, for example an IP
+      address and a UDP port.  Packets are transmitted from a source
+      transport address to a destination transport address.
+
+   RTP media type: An RTP media type is the collection of payload
+      types which can be carried within a single RTP session.  The RTP
+      Profile assigns RTP media types to RTP payload types.
+
+   Multimedia session: A set of concurrent RTP sessions among a
+      common group of participants.  For example, a videoconference
+      (which is a multimedia session) may contain an audio RTP session
+      and a video RTP session.
+
+   RTP session: An association among a set of participants
+      communicating with RTP.  A participant may be involved in multiple
+      RTP sessions at the same time.  In a multimedia session, each
+      medium is typically carried in a separate RTP session with its own
+      RTCP packets unless the the encoding itself multiplexes multiple
+      media into a single data stream.  A participant distinguishes
+      multiple RTP sessions by reception of different sessions using
+      different pairs of destination transport addresses, where a pair
+      of transport addresses comprises one network address plus a pair
+      of ports for RTP and RTCP.  All participants in an RTP session may
+      share a common destination transport address pair, as in the case
+      of IP multicast, or the pairs may be different for each
+      participant, as in the case of individual unicast network
+      addresses and port pairs.  In the unicast case, a participant may
+      receive from all other participants in the session using the same
+      pair of ports, or may use a distinct pair of ports for each.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                     [Page 9]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      The distinguishing feature of an RTP session is that each
+      maintains a full, separate space of SSRC identifiers (defined
+      next).  The set of participants included in one RTP session
+      consists of those that can receive an SSRC identifier transmitted
+      by any one of the participants either in RTP as the SSRC or a CSRC
+      (also defined below) or in RTCP.  For example, consider a three-
+      party conference implemented using unicast UDP with each
+      participant receiving from the other two on separate port pairs.
+      If each participant sends RTCP feedback about data received from
+      one other participant only back to that participant, then the
+      conference is composed of three separate point-to-point RTP
+      sessions.  If each participant provides RTCP feedback about its
+      reception of one other participant to both of the other
+      participants, then the conference is composed of one multi-party
+      RTP session.  The latter case simulates the behavior that would
+      occur with IP multicast communication among the three
+      participants.
+
+      The RTP framework allows the variations defined here, but a
+      particular control protocol or application design will usually
+      impose constraints on these variations.
+
+   Synchronization source (SSRC): The source of a stream of RTP
+      packets, identified by a 32-bit numeric SSRC identifier carried in
+      the RTP header so as not to be dependent upon the network address.
+      All packets from a synchronization source form part of the same
+      timing and sequence number space, so a receiver groups packets by
+      synchronization source for playback.  Examples of synchronization
+      sources include the sender of a stream of packets derived from a
+      signal source such as a microphone or a camera, or an RTP mixer
+      (see below).  A synchronization source may change its data format,
+      e.g., audio encoding, over time.  The SSRC identifier is a
+      randomly chosen value meant to be globally unique within a
+      particular RTP session (see Section 8).  A participant need not
+      use the same SSRC identifier for all the RTP sessions in a
+      multimedia session; the binding of the SSRC identifiers is
+      provided through RTCP (see Section 6.5.1).  If a participant
+      generates multiple streams in one RTP session, for example from
+      separate video cameras, each MUST be identified as a different
+      SSRC.
+
+   Contributing source (CSRC): A source of a stream of RTP packets
+      that has contributed to the combined stream produced by an RTP
+      mixer (see below).  The mixer inserts a list of the SSRC
+      identifiers of the sources that contributed to the generation of a
+      particular packet into the RTP header of that packet.  This list
+      is called the CSRC list.  An example application is audio
+      conferencing where a mixer indicates all the talkers whose speech
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 10]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      was combined to produce the outgoing packet, allowing the receiver
+      to indicate the current talker, even though all the audio packets
+      contain the same SSRC identifier (that of the mixer).
+
+   End system: An application that generates the content to be sent
+      in RTP packets and/or consumes the content of received RTP
+      packets.  An end system can act as one or more synchronization
+      sources in a particular RTP session, but typically only one.
+
+   Mixer: An intermediate system that receives RTP packets from one
+      or more sources, possibly changes the data format, combines the
+      packets in some manner and then forwards a new RTP packet.  Since
+      the timing among multiple input sources will not generally be
+      synchronized, the mixer will make timing adjustments among the
+      streams and generate its own timing for the combined stream.
+      Thus, all data packets originating from a mixer will be identified
+      as having the mixer as their synchronization source.
+
+   Translator: An intermediate system that forwards RTP packets
+      with their synchronization source identifier intact.  Examples of
+      translators include devices that convert encodings without mixing,
+      replicators from multicast to unicast, and application-level
+      filters in firewalls.
+
+   Monitor: An application that receives RTCP packets sent by
+      participants in an RTP session, in particular the reception
+      reports, and estimates the current quality of service for
+      distribution monitoring, fault diagnosis and long-term statistics.
+      The monitor function is likely to be built into the application(s)
+      participating in the session, but may also be a separate
+      application that does not otherwise participate and does not send
+      or receive the RTP data packets (since they are on a separate
+      port).  These are called third-party monitors.  It is also
+      acceptable for a third-party monitor to receive the RTP data
+      packets but not send RTCP packets or otherwise be counted in the
+      session.
+
+   Non-RTP means: Protocols and mechanisms that may be needed in
+      addition to RTP to provide a usable service.  In particular, for
+      multimedia conferences, a control protocol may distribute
+      multicast addresses and keys for encryption, negotiate the
+      encryption algorithm to be used, and define dynamic mappings
+      between RTP payload type values and the payload formats they
+      represent for formats that do not have a predefined payload type
+      value.  Examples of such protocols include the Session Initiation
+      Protocol (SIP) (RFC 3261 [13]), ITU Recommendation H.323 [14] and
+      applications using SDP (RFC 2327 [15]), such as RTSP (RFC 2326
+      [16]).  For simple
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 11]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      applications, electronic mail or a conference database may also be
+      used.  The specification of such protocols and mechanisms is
+      outside the scope of this document.
+
+4. Byte Order, Alignment, and Time Format
+
+   All integer fields are carried in network byte order, that is, most
+   significant byte (octet) first.  This byte order is commonly known as
+   big-endian.  The transmission order is described in detail in [3].
+   Unless otherwise noted, numeric constants are in decimal (base 10).
+
+   All header data is aligned to its natural length, i.e., 16-bit fields
+   are aligned on even offsets, 32-bit fields are aligned at offsets
+   divisible by four, etc.  Octets designated as padding have the value
+   zero.
+
+   Wallclock time (absolute date and time) is represented using the
+   timestamp format of the Network Time Protocol (NTP), which is in
+   seconds relative to 0h UTC on 1 January 1900 [4].  The full
+   resolution NTP timestamp is a 64-bit unsigned fixed-point number with
+   the integer part in the first 32 bits and the fractional part in the
+   last 32 bits.  In some fields where a more compact representation is
+   appropriate, only the middle 32 bits are used; that is, the low 16
+   bits of the integer part and the high 16 bits of the fractional part.
+   The high 16 bits of the integer part must be determined
+   independently.
+
+   An implementation is not required to run the Network Time Protocol in
+   order to use RTP.  Other time sources, or none at all, may be used
+   (see the description of the NTP timestamp field in Section 6.4.1).
+   However, running NTP may be useful for synchronizing streams
+   transmitted from separate hosts.
+
+   The NTP timestamp will wrap around to zero some time in the year
+   2036, but for RTP purposes, only differences between pairs of NTP
+   timestamps are used.  So long as the pairs of timestamps can be
+   assumed to be within 68 years of each other, using modular arithmetic
+   for subtractions and comparisons makes the wraparound irrelevant.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 12]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+5. RTP Data Transfer Protocol
+
+5.1 RTP Fixed Header Fields
+
+   The RTP header has the following format:
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |V=2|P|X|  CC   |M|     PT      |       sequence number         |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                           timestamp                           |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |           synchronization source (SSRC) identifier            |
+   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+   |            contributing source (CSRC) identifiers             |
+   |                             ....                              |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The first twelve octets are present in every RTP packet, while the
+   list of CSRC identifiers is present only when inserted by a mixer.
+   The fields have the following meaning:
+
+   version (V): 2 bits
+      This field identifies the version of RTP.  The version defined by
+      this specification is two (2).  (The value 1 is used by the first
+      draft version of RTP and the value 0 is used by the protocol
+      initially implemented in the "vat" audio tool.)
+
+   padding (P): 1 bit
+      If the padding bit is set, the packet contains one or more
+      additional padding octets at the end which are not part of the
+      payload.  The last octet of the padding contains a count of how
+      many padding octets should be ignored, including itself.  Padding
+      may be needed by some encryption algorithms with fixed block sizes
+      or for carrying several RTP packets in a lower-layer protocol data
+      unit.
+
+   extension (X): 1 bit
+      If the extension bit is set, the fixed header MUST be followed by
+      exactly one header extension, with a format defined in Section
+      5.3.1.
+
+   CSRC count (CC): 4 bits
+      The CSRC count contains the number of CSRC identifiers that follow
+      the fixed header.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 13]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   marker (M): 1 bit
+      The interpretation of the marker is defined by a profile.  It is
+      intended to allow significant events such as frame boundaries to
+      be marked in the packet stream.  A profile MAY define additional
+      marker bits or specify that there is no marker bit by changing the
+      number of bits in the payload type field (see Section 5.3).
+
+   payload type (PT): 7 bits
+      This field identifies the format of the RTP payload and determines
+      its interpretation by the application.  A profile MAY specify a
+      default static mapping of payload type codes to payload formats.
+      Additional payload type codes MAY be defined dynamically through
+      non-RTP means (see Section 3).  A set of default mappings for
+      audio and video is specified in the companion RFC 3551 [1].  An
+      RTP source MAY change the payload type during a session, but this
+      field SHOULD NOT be used for multiplexing separate media streams
+      (see Section 5.2).
+
+      A receiver MUST ignore packets with payload types that it does not
+      understand.
+
+   sequence number: 16 bits
+      The sequence number increments by one for each RTP data packet
+      sent, and may be used by the receiver to detect packet loss and to
+      restore packet sequence.  The initial value of the sequence number
+      SHOULD be random (unpredictable) to make known-plaintext attacks
+      on encryption more difficult, even if the source itself does not
+      encrypt according to the method in Section 9.1, because the
+      packets may flow through a translator that does.  Techniques for
+      choosing unpredictable numbers are discussed in [17].
+
+   timestamp: 32 bits
+      The timestamp reflects the sampling instant of the first octet in
+      the RTP data packet.  The sampling instant MUST be derived from a
+      clock that increments monotonically and linearly in time to allow
+      synchronization and jitter calculations (see Section 6.4.1).  The
+      resolution of the clock MUST be sufficient for the desired
+      synchronization accuracy and for measuring packet arrival jitter
+      (one tick per video frame is typically not sufficient).  The clock
+      frequency is dependent on the format of data carried as payload
+      and is specified statically in the profile or payload format
+      specification that defines the format, or MAY be specified
+      dynamically for payload formats defined through non-RTP means.  If
+      RTP packets are generated periodically, the nominal sampling
+      instant as determined from the sampling clock is to be used, not a
+      reading of the system clock.  As an example, for fixed-rate audio
+      the timestamp clock would likely increment by one for each
+      sampling period.  If an audio application reads blocks covering
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 14]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      160 sampling periods from the input device, the timestamp would be
+      increased by 160 for each such block, regardless of whether the
+      block is transmitted in a packet or dropped as silent.
+
+      The initial value of the timestamp SHOULD be random, as for the
+      sequence number.  Several consecutive RTP packets will have equal
+      timestamps if they are (logically) generated at once, e.g., belong
+      to the same video frame.  Consecutive RTP packets MAY contain
+      timestamps that are not monotonic if the data is not transmitted
+      in the order it was sampled, as in the case of MPEG interpolated
+      video frames.  (The sequence numbers of the packets as transmitted
+      will still be monotonic.)
+
+      RTP timestamps from different media streams may advance at
+      different rates and usually have independent, random offsets.
+      Therefore, although these timestamps are sufficient to reconstruct
+      the timing of a single stream, directly comparing RTP timestamps
+      from different media is not effective for synchronization.
+      Instead, for each medium the RTP timestamp is related to the
+      sampling instant by pairing it with a timestamp from a reference
+      clock (wallclock) that represents the time when the data
+      corresponding to the RTP timestamp was sampled.  The reference
+      clock is shared by all media to be synchronized.  The timestamp
+      pairs are not transmitted in every data packet, but at a lower
+      rate in RTCP SR packets as described in Section 6.4.
+
+      The sampling instant is chosen as the point of reference for the
+      RTP timestamp because it is known to the transmitting endpoint and
+      has a common definition for all media, independent of encoding
+      delays or other processing.  The purpose is to allow synchronized
+      presentation of all media sampled at the same time.
+
+      Applications transmitting stored data rather than data sampled in
+      real time typically use a virtual presentation timeline derived
+      from wallclock time to determine when the next frame or other unit
+      of each medium in the stored data should be presented.  In this
+      case, the RTP timestamp would reflect the presentation time for
+      each unit.  That is, the RTP timestamp for each unit would be
+      related to the wallclock time at which the unit becomes current on
+      the virtual presentation timeline.  Actual presentation occurs
+      some time later as determined by the receiver.
+
+      An example describing live audio narration of prerecorded video
+      illustrates the significance of choosing the sampling instant as
+      the reference point.  In this scenario, the video would be
+      presented locally for the narrator to view and would be
+      simultaneously transmitted using RTP.  The "sampling instant" of a
+      video frame transmitted in RTP would be established by referencing
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 15]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      its timestamp to the wallclock time when that video frame was
+      presented to the narrator.  The sampling instant for the audio RTP
+      packets containing the narrator's speech would be established by
+      referencing the same wallclock time when the audio was sampled.
+      The audio and video may even be transmitted by different hosts if
+      the reference clocks on the two hosts are synchronized by some
+      means such as NTP.  A receiver can then synchronize presentation
+      of the audio and video packets by relating their RTP timestamps
+      using the timestamp pairs in RTCP SR packets.
+
+   SSRC: 32 bits
+      The SSRC field identifies the synchronization source.  This
+      identifier SHOULD be chosen randomly, with the intent that no two
+      synchronization sources within the same RTP session will have the
+      same SSRC identifier.  An example algorithm for generating a
+      random identifier is presented in Appendix A.6.  Although the
+      probability of multiple sources choosing the same identifier is
+      low, all RTP implementations must be prepared to detect and
+      resolve collisions.  Section 8 describes the probability of
+      collision along with a mechanism for resolving collisions and
+      detecting RTP-level forwarding loops based on the uniqueness of
+      the SSRC identifier.  If a source changes its source transport
+      address, it must also choose a new SSRC identifier to avoid being
+      interpreted as a looped source (see Section 8.2).
+
+   CSRC list: 0 to 15 items, 32 bits each
+      The CSRC list identifies the contributing sources for the payload
+      contained in this packet.  The number of identifiers is given by
+      the CC field.  If there are more than 15 contributing sources,
+      only 15 can be identified.  CSRC identifiers are inserted by
+      mixers (see Section 7.1), using the SSRC identifiers of
+      contributing sources.  For example, for audio packets the SSRC
+      identifiers of all sources that were mixed together to create a
+      packet are listed, allowing correct talker indication at the
+      receiver.
+
+5.2 Multiplexing RTP Sessions
+
+   For efficient protocol processing, the number of multiplexing points
+   should be minimized, as described in the integrated layer processing
+   design principle [10].  In RTP, multiplexing is provided by the
+   destination transport address (network address and port number) which
+   is different for each RTP session.  For example, in a teleconference
+   composed of audio and video media encoded separately, each medium
+   SHOULD be carried in a separate RTP session with its own destination
+   transport address.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 16]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Separate audio and video streams SHOULD NOT be carried in a single
+   RTP session and demultiplexed based on the payload type or SSRC
+   fields.  Interleaving packets with different RTP media types but
+   using the same SSRC would introduce several problems:
+
+   1. If, say, two audio streams shared the same RTP session and the
+      same SSRC value, and one were to change encodings and thus acquire
+      a different RTP payload type, there would be no general way of
+      identifying which stream had changed encodings.
+
+   2. An SSRC is defined to identify a single timing and sequence number
+      space.  Interleaving multiple payload types would require
+      different timing spaces if the media clock rates differ and would
+      require different sequence number spaces to tell which payload
+      type suffered packet loss.
+
+   3. The RTCP sender and receiver reports (see Section 6.4) can only
+      describe one timing and sequence number space per SSRC and do not
+      carry a payload type field.
+
+   4. An RTP mixer would not be able to combine interleaved streams of
+      incompatible media into one stream.
+
+   5. Carrying multiple media in one RTP session precludes: the use of
+      different network paths or network resource allocations if
+      appropriate; reception of a subset of the media if desired, for
+      example just audio if video would exceed the available bandwidth;
+      and receiver implementations that use separate processes for the
+      different media, whereas using separate RTP sessions permits
+      either single- or multiple-process implementations.
+
+   Using a different SSRC for each medium but sending them in the same
+   RTP session would avoid the first three problems but not the last
+   two.
+
+   On the other hand, multiplexing multiple related sources of the same
+   medium in one RTP session using different SSRC values is the norm for
+   multicast sessions.  The problems listed above don't apply: an RTP
+   mixer can combine multiple audio sources, for example, and the same
+   treatment is applicable for all of them.  It may also be appropriate
+   to multiplex streams of the same medium using different SSRC values
+   in other scenarios where the last two problems do not apply.
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 17]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+5.3 Profile-Specific Modifications to the RTP Header
+
+   The existing RTP data packet header is believed to be complete for
+   the set of functions required in common across all the application
+   classes that RTP might support.  However, in keeping with the ALF
+   design principle, the header MAY be tailored through modifications or
+   additions defined in a profile specification while still allowing
+   profile-independent monitoring and recording tools to function.
+
+   o  The marker bit and payload type field carry profile-specific
+      information, but they are allocated in the fixed header since many
+      applications are expected to need them and might otherwise have to
+      add another 32-bit word just to hold them.  The octet containing
+      these fields MAY be redefined by a profile to suit different
+      requirements, for example with more or fewer marker bits.  If
+      there are any marker bits, one SHOULD be located in the most
+      significant bit of the octet since profile-independent monitors
+      may be able to observe a correlation between packet loss patterns
+      and the marker bit.
+
+   o  Additional information that is required for a particular payload
+      format, such as a video encoding, SHOULD be carried in the payload
+      section of the packet.  This might be in a header that is always
+      present at the start of the payload section, or might be indicated
+      by a reserved value in the data pattern.
+
+   o  If a particular class of applications needs additional
+      functionality independent of payload format, the profile under
+      which those applications operate SHOULD define additional fixed
+      fields to follow immediately after the SSRC field of the existing
+      fixed header.  Those applications will be able to quickly and
+      directly access the additional fields while profile-independent
+      monitors or recorders can still process the RTP packets by
+      interpreting only the first twelve octets.
+
+   If it turns out that additional functionality is needed in common
+   across all profiles, then a new version of RTP should be defined to
+   make a permanent change to the fixed header.
+
+5.3.1 RTP Header Extension
+
+   An extension mechanism is provided to allow individual
+   implementations to experiment with new payload-format-independent
+   functions that require additional information to be carried in the
+   RTP data packet header.  This mechanism is designed so that the
+   header extension may be ignored by other interoperating
+   implementations that have not been extended.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 18]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Note that this header extension is intended only for limited use.
+   Most potential uses of this mechanism would be better done another
+   way, using the methods described in the previous section.  For
+   example, a profile-specific extension to the fixed header is less
+   expensive to process because it is not conditional nor in a variable
+   location.  Additional information required for a particular payload
+   format SHOULD NOT use this header extension, but SHOULD be carried in
+   the payload section of the packet.
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |      defined by profile       |           length              |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                        header extension                       |
+   |                             ....                              |
+
+   If the X bit in the RTP header is one, a variable-length header
+   extension MUST be appended to the RTP header, following the CSRC list
+   if present.  The header extension contains a 16-bit length field that
+   counts the number of 32-bit words in the extension, excluding the
+   four-octet extension header (therefore zero is a valid length).  Only
+   a single extension can be appended to the RTP data header.  To allow
+   multiple interoperating implementations to each experiment
+   independently with different header extensions, or to allow a
+   particular implementation to experiment with more than one type of
+   header extension, the first 16 bits of the header extension are left
+   open for distinguishing identifiers or parameters.  The format of
+   these 16 bits is to be defined by the profile specification under
+   which the implementations are operating.  This RTP specification does
+   not define any header extensions itself.
+
+6. RTP Control Protocol -- RTCP
+
+   The RTP control protocol (RTCP) is based on the periodic transmission
+   of control packets to all participants in the session, using the same
+   distribution mechanism as the data packets.  The underlying protocol
+   MUST provide multiplexing of the data and control packets, for
+   example using separate port numbers with UDP.  RTCP performs four
+   functions:
+
+   1. The primary function is to provide feedback on the quality of the
+      data distribution.  This is an integral part of the RTP's role as
+      a transport protocol and is related to the flow and congestion
+      control functions of other transport protocols (see Section 10 on
+      the requirement for congestion control).  The feedback may be
+      directly useful for control of adaptive encodings [18,19], but
+      experiments with IP multicasting have shown that it is also
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 19]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      critical to get feedback from the receivers to diagnose faults in
+      the distribution.  Sending reception feedback reports to all
+      participants allows one who is observing problems to evaluate
+      whether those problems are local or global.  With a distribution
+      mechanism like IP multicast, it is also possible for an entity
+      such as a network service provider who is not otherwise involved
+      in the session to receive the feedback information and act as a
+      third-party monitor to diagnose network problems.  This feedback
+      function is performed by the RTCP sender and receiver reports,
+      described below in Section 6.4.
+
+   2. RTCP carries a persistent transport-level identifier for an RTP
+      source called the canonical name or CNAME, Section 6.5.1.  Since
+      the SSRC identifier may change if a conflict is discovered or a
+      program is restarted, receivers require the CNAME to keep track of
+      each participant.  Receivers may also require the CNAME to
+      associate multiple data streams from a given participant in a set
+      of related RTP sessions, for example to synchronize audio and
+      video.  Inter-media synchronization also requires the NTP and RTP
+      timestamps included in RTCP packets by data senders.
+
+   3. The first two functions require that all participants send RTCP
+      packets, therefore the rate must be controlled in order for RTP to
+      scale up to a large number of participants.  By having each
+      participant send its control packets to all the others, each can
+      independently observe the number of participants.  This number is
+      used to calculate the rate at which the packets are sent, as
+      explained in Section 6.2.
+
+   4. A fourth, OPTIONAL function is to convey minimal session control
+      information, for example participant identification to be
+      displayed in the user interface.  This is most likely to be useful
+      in "loosely controlled" sessions where participants enter and
+      leave without membership control or parameter negotiation.  RTCP
+      serves as a convenient channel to reach all the participants, but
+      it is not necessarily expected to support all the control
+      communication requirements of an application.  A higher-level
+      session control protocol, which is beyond the scope of this
+      document, may be needed.
+
+   Functions 1-3 SHOULD be used in all environments, but particularly in
+   the IP multicast environment.  RTP application designers SHOULD avoid
+   mechanisms that can only work in unicast mode and will not scale to
+   larger numbers.  Transmission of RTCP MAY be controlled separately
+   for senders and receivers, as described in Section 6.2, for cases
+   such as unidirectional links where feedback from receivers is not
+   possible.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 20]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Non-normative note:  In the multicast routing approach
+      called Source-Specific Multicast (SSM), there is only one sender
+      per "channel" (a source address, group address pair), and
+      receivers (except for the channel source) cannot use multicast to
+      communicate directly with other channel members.  The
+      recommendations here accommodate SSM only through Section 6.2's
+      option of turning off receivers' RTCP entirely.  Future work will
+      specify adaptation of RTCP for SSM so that feedback from receivers
+      can be maintained.
+
+6.1 RTCP Packet Format
+
+   This specification defines several RTCP packet types to carry a
+   variety of control information:
+
+   SR:   Sender report, for transmission and reception statistics from
+         participants that are active senders
+
+   RR:   Receiver report, for reception statistics from participants
+         that are not active senders and in combination with SR for
+         active senders reporting on more than 31 sources
+
+   SDES: Source description items, including CNAME
+
+   BYE:  Indicates end of participation
+
+   APP:  Application-specific functions
+
+   Each RTCP packet begins with a fixed part similar to that of RTP data
+   packets, followed by structured elements that MAY be of variable
+   length according to the packet type but MUST end on a 32-bit
+   boundary.  The alignment requirement and a length field in the fixed
+   part of each packet are included to make RTCP packets "stackable".
+   Multiple RTCP packets can be concatenated without any intervening
+   separators to form a compound RTCP packet that is sent in a single
+   packet of the lower layer protocol, for example UDP.  There is no
+   explicit count of individual RTCP packets in the compound packet
+   since the lower layer protocols are expected to provide an overall
+   length to determine the end of the compound packet.
+
+   Each individual RTCP packet in the compound packet may be processed
+   independently with no requirements upon the order or combination of
+   packets.  However, in order to perform the functions of the protocol,
+   the following constraints are imposed:
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 21]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  Reception statistics (in SR or RR) should be sent as often as
+      bandwidth constraints will allow to maximize the resolution of the
+      statistics, therefore each periodically transmitted compound RTCP
+      packet MUST include a report packet.
+
+   o  New receivers need to receive the CNAME for a source as soon as
+      possible to identify the source and to begin associating media for
+      purposes such as lip-sync, so each compound RTCP packet MUST also
+      include the SDES CNAME except when the compound RTCP packet is
+      split for partial encryption as described in Section 9.1.
+
+   o  The number of packet types that may appear first in the compound
+      packet needs to be limited to increase the number of constant bits
+      in the first word and the probability of successfully validating
+      RTCP packets against misaddressed RTP data packets or other
+      unrelated packets.
+
+   Thus, all RTCP packets MUST be sent in a compound packet of at least
+   two individual packets, with the following format:
+
+   Encryption prefix:  If and only if the compound packet is to be
+      encrypted according to the method in Section 9.1, it MUST be
+      prefixed by a random 32-bit quantity redrawn for every compound
+      packet transmitted.  If padding is required for the encryption, it
+      MUST be added to the last packet of the compound packet.
+
+   SR or RR:  The first RTCP packet in the compound packet MUST
+      always be a report packet to facilitate header validation as
+      described in Appendix A.2.  This is true even if no data has been
+      sent or received, in which case an empty RR MUST be sent, and even
+      if the only other RTCP packet in the compound packet is a BYE.
+
+   Additional RRs:  If the number of sources for which reception
+      statistics are being reported exceeds 31, the number that will fit
+      into one SR or RR packet, then additional RR packets SHOULD follow
+      the initial report packet.
+
+   SDES:  An SDES packet containing a CNAME item MUST be included
+      in each compound RTCP packet, except as noted in Section 9.1.
+      Other source description items MAY optionally be included if
+      required by a particular application, subject to bandwidth
+      constraints (see Section 6.3.9).
+
+   BYE or APP:  Other RTCP packet types, including those yet to be
+      defined, MAY follow in any order, except that BYE SHOULD be the
+      last packet sent with a given SSRC/CSRC.  Packet types MAY appear
+      more than once.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 22]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   An individual RTP participant SHOULD send only one compound RTCP
+   packet per report interval in order for the RTCP bandwidth per
+   participant to be estimated correctly (see Section 6.2), except when
+   the compound RTCP packet is split for partial encryption as described
+   in Section 9.1.  If there are too many sources to fit all the
+   necessary RR packets into one compound RTCP packet without exceeding
+   the maximum transmission unit (MTU) of the network path, then only
+   the subset that will fit into one MTU SHOULD be included in each
+   interval.  The subsets SHOULD be selected round-robin across multiple
+   intervals so that all sources are reported.
+
+   It is RECOMMENDED that translators and mixers combine individual RTCP
+   packets from the multiple sources they are forwarding into one
+   compound packet whenever feasible in order to amortize the packet
+   overhead (see Section 7).  An example RTCP compound packet as might
+   be produced by a mixer is shown in Fig. 1.  If the overall length of
+   a compound packet would exceed the MTU of the network path, it SHOULD
+   be segmented into multiple shorter compound packets to be transmitted
+   in separate packets of the underlying protocol.  This does not impair
+   the RTCP bandwidth estimation because each compound packet represents
+   at least one distinct participant.  Note that each of the compound
+   packets MUST begin with an SR or RR packet.
+
+   An implementation SHOULD ignore incoming RTCP packets with types
+   unknown to it.  Additional RTCP packet types may be registered with
+   the Internet Assigned Numbers Authority (IANA) as described in
+   Section 15.
+
+   if encrypted: random 32-bit integer
+   |
+   |[--------- packet --------][---------- packet ----------][-packet-]
+   |
+   |                receiver            chunk        chunk
+   V                reports           item  item   item  item
+   --------------------------------------------------------------------
+   R[SR #sendinfo #site1#site2][SDES #CNAME PHONE #CNAME LOC][BYE##why]
+   --------------------------------------------------------------------
+   |                                                                  |
+   |<-----------------------  compound packet ----------------------->|
+   |<--------------------------  UDP packet ------------------------->|
+
+   #: SSRC/CSRC identifier
+
+              Figure 1: Example of an RTCP compound packet
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 23]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.2 RTCP Transmission Interval
+
+   RTP is designed to allow an application to scale automatically over
+   session sizes ranging from a few participants to thousands.  For
+   example, in an audio conference the data traffic is inherently self-
+   limiting because only one or two people will speak at a time, so with
+   multicast distribution the data rate on any given link remains
+   relatively constant independent of the number of participants.
+   However, the control traffic is not self-limiting.  If the reception
+   reports from each participant were sent at a constant rate, the
+   control traffic would grow linearly with the number of participants.
+   Therefore, the rate must be scaled down by dynamically calculating
+   the interval between RTCP packet transmissions.
+
+   For each session, it is assumed that the data traffic is subject to
+   an aggregate limit called the "session bandwidth" to be divided among
+   the participants.  This bandwidth might be reserved and the limit
+   enforced by the network.  If there is no reservation, there may be
+   other constraints, depending on the environment, that establish the
+   "reasonable" maximum for the session to use, and that would be the
+   session bandwidth.  The session bandwidth may be chosen based on some
+   cost or a priori knowledge of the available network bandwidth for the
+   session.  It is somewhat independent of the media encoding, but the
+   encoding choice may be limited by the session bandwidth.  Often, the
+   session bandwidth is the sum of the nominal bandwidths of the senders
+   expected to be concurrently active.  For teleconference audio, this
+   number would typically be one sender's bandwidth.  For layered
+   encodings, each layer is a separate RTP session with its own session
+   bandwidth parameter.
+
+   The session bandwidth parameter is expected to be supplied by a
+   session management application when it invokes a media application,
+   but media applications MAY set a default based on the single-sender
+   data bandwidth for the encoding selected for the session.  The
+   application MAY also enforce bandwidth limits based on multicast
+   scope rules or other criteria.  All participants MUST use the same
+   value for the session bandwidth so that the same RTCP interval will
+   be calculated.
+
+   Bandwidth calculations for control and data traffic include lower-
+   layer transport and network protocols (e.g., UDP and IP) since that
+   is what the resource reservation system would need to know.  The
+   application can also be expected to know which of these protocols are
+   in use.  Link level headers are not included in the calculation since
+   the packet will be encapsulated with different link level headers as
+   it travels.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 24]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   The control traffic should be limited to a small and known fraction
+   of the session bandwidth: small so that the primary function of the
+   transport protocol to carry data is not impaired; known so that the
+   control traffic can be included in the bandwidth specification given
+   to a resource reservation protocol, and so that each participant can
+   independently calculate its share.  The control traffic bandwidth is
+   in addition to the session bandwidth for the data traffic.  It is
+   RECOMMENDED that the fraction of the session bandwidth added for RTCP
+   be fixed at 5%.  It is also RECOMMENDED that 1/4 of the RTCP
+   bandwidth be dedicated to participants that are sending data so that
+   in sessions with a large number of receivers but a small number of
+   senders, newly joining participants will more quickly receive the
+   CNAME for the sending sites.  When the proportion of senders is
+   greater than 1/4 of the participants, the senders get their
+   proportion of the full RTCP bandwidth.  While the values of these and
+   other constants in the interval calculation are not critical, all
+   participants in the session MUST use the same values so the same
+   interval will be calculated.  Therefore, these constants SHOULD be
+   fixed for a particular profile.
+
+   A profile MAY specify that the control traffic bandwidth may be a
+   separate parameter of the session rather than a strict percentage of
+   the session bandwidth.  Using a separate parameter allows rate-
+   adaptive applications to set an RTCP bandwidth consistent with a
+   "typical" data bandwidth that is lower than the maximum bandwidth
+   specified by the session bandwidth parameter.
+
+   The profile MAY further specify that the control traffic bandwidth
+   may be divided into two separate session parameters for those
+   participants which are active data senders and those which are not;
+   let us call the parameters S and R.  Following the recommendation
+   that 1/4 of the RTCP bandwidth be dedicated to data senders, the
+   RECOMMENDED default values for these two parameters would be 1.25%
+   and 3.75%, respectively.  When the proportion of senders is greater
+   than S/(S+R) of the participants, the senders get their proportion of
+   the sum of these parameters.  Using two parameters allows RTCP
+   reception reports to be turned off entirely for a particular session
+   by setting the RTCP bandwidth for non-data-senders to zero while
+   keeping the RTCP bandwidth for data senders non-zero so that sender
+   reports can still be sent for inter-media synchronization.  Turning
+   off RTCP reception reports is NOT RECOMMENDED because they are needed
+   for the functions listed at the beginning of Section 6, particularly
+   reception quality feedback and congestion control.  However, doing so
+   may be appropriate for systems operating on unidirectional links or
+   for sessions that don't require feedback on the quality of reception
+   or liveness of receivers and that have other means to avoid
+   congestion.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 25]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   The calculated interval between transmissions of compound RTCP
+   packets SHOULD also have a lower bound to avoid having bursts of
+   packets exceed the allowed bandwidth when the number of participants
+   is small and the traffic isn't smoothed according to the law of large
+   numbers.  It also keeps the report interval from becoming too small
+   during transient outages like a network partition such that
+   adaptation is delayed when the partition heals.  At application
+   startup, a delay SHOULD be imposed before the first compound RTCP
+   packet is sent to allow time for RTCP packets to be received from
+   other participants so the report interval will converge to the
+   correct value more quickly.  This delay MAY be set to half the
+   minimum interval to allow quicker notification that the new
+   participant is present.  The RECOMMENDED value for a fixed minimum
+   interval is 5 seconds.
+
+   An implementation MAY scale the minimum RTCP interval to a smaller
+   value inversely proportional to the session bandwidth parameter with
+   the following limitations:
+
+   o  For multicast sessions, only active data senders MAY use the
+      reduced minimum value to calculate the interval for transmission
+      of compound RTCP packets.
+
+   o  For unicast sessions, the reduced value MAY be used by
+      participants that are not active data senders as well, and the
+      delay before sending the initial compound RTCP packet MAY be zero.
+
+   o  For all sessions, the fixed minimum SHOULD be used when
+      calculating the participant timeout interval (see Section 6.3.5)
+      so that implementations which do not use the reduced value for
+      transmitting RTCP packets are not timed out by other participants
+      prematurely.
+
+   o  The RECOMMENDED value for the reduced minimum in seconds is 360
+      divided by the session bandwidth in kilobits/second.  This minimum
+      is smaller than 5 seconds for bandwidths greater than 72 kb/s.
+
+   The algorithm described in Section 6.3 and Appendix A.7 was designed
+   to meet the goals outlined in this section.  It calculates the
+   interval between sending compound RTCP packets to divide the allowed
+   control traffic bandwidth among the participants.  This allows an
+   application to provide fast response for small sessions where, for
+   example, identification of all participants is important, yet
+   automatically adapt to large sessions.  The algorithm incorporates
+   the following characteristics:
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 26]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  The calculated interval between RTCP packets scales linearly with
+      the number of members in the group.  It is this linear factor
+      which allows for a constant amount of control traffic when summed
+      across all members.
+
+   o  The interval between RTCP packets is varied randomly over the
+      range [0.5,1.5] times the calculated interval to avoid unintended
+      synchronization of all participants [20].  The first RTCP packet
+      sent after joining a session is also delayed by a random variation
+      of half the minimum RTCP interval.
+
+   o  A dynamic estimate of the average compound RTCP packet size is
+      calculated, including all those packets received and sent, to
+      automatically adapt to changes in the amount of control
+      information carried.
+
+   o  Since the calculated interval is dependent on the number of
+      observed group members, there may be undesirable startup effects
+      when a new user joins an existing session, or many users
+      simultaneously join a new session.  These new users will initially
+      have incorrect estimates of the group membership, and thus their
+      RTCP transmission interval will be too short.  This problem can be
+      significant if many users join the session simultaneously.  To
+      deal with this, an algorithm called "timer reconsideration" is
+      employed.  This algorithm implements a simple back-off mechanism
+      which causes users to hold back RTCP packet transmission if the
+      group sizes are increasing.
+
+   o  When users leave a session, either with a BYE or by timeout, the
+      group membership decreases, and thus the calculated interval
+      should decrease.  A "reverse reconsideration" algorithm is used to
+      allow members to more quickly reduce their intervals in response
+      to group membership decreases.
+
+   o  BYE packets are given different treatment than other RTCP packets.
+      When a user leaves a group, and wishes to send a BYE packet, it
+      may do so before its next scheduled RTCP packet.  However,
+      transmission of BYEs follows a back-off algorithm which avoids
+      floods of BYE packets should a large number of members
+      simultaneously leave the session.
+
+   This algorithm may be used for sessions in which all participants are
+   allowed to send.  In that case, the session bandwidth parameter is
+   the product of the individual sender's bandwidth times the number of
+   participants, and the RTCP bandwidth is 5% of that.
+
+   Details of the algorithm's operation are given in the sections that
+   follow.  Appendix A.7 gives an example implementation.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 27]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.2.1 Maintaining the Number of Session Members
+
+   Calculation of the RTCP packet interval depends upon an estimate of
+   the number of sites participating in the session.  New sites are
+   added to the count when they are heard, and an entry for each SHOULD
+   be created in a table indexed by the SSRC or CSRC identifier (see
+   Section 8.2) to keep track of them.  New entries MAY be considered
+   not valid until multiple packets carrying the new SSRC have been
+   received (see Appendix A.1), or until an SDES RTCP packet containing
+   a CNAME for that SSRC has been received.  Entries MAY be deleted from
+   the table when an RTCP BYE packet with the corresponding SSRC
+   identifier is received, except that some straggler data packets might
+   arrive after the BYE and cause the entry to be recreated.  Instead,
+   the entry SHOULD be marked as having received a BYE and then deleted
+   after an appropriate delay.
+
+   A participant MAY mark another site inactive, or delete it if not yet
+   valid, if no RTP or RTCP packet has been received for a small number
+   of RTCP report intervals (5 is RECOMMENDED).  This provides some
+   robustness against packet loss.  All sites must have the same value
+   for this multiplier and must calculate roughly the same value for the
+   RTCP report interval in order for this timeout to work properly.
+   Therefore, this multiplier SHOULD be fixed for a particular profile.
+
+   For sessions with a very large number of participants, it may be
+   impractical to maintain a table to store the SSRC identifier and
+   state information for all of them.  An implementation MAY use SSRC
+   sampling, as described in [21], to reduce the storage requirements.
+   An implementation MAY use any other algorithm with similar
+   performance.  A key requirement is that any algorithm considered
+   SHOULD NOT substantially underestimate the group size, although it
+   MAY overestimate.
+
+6.3 RTCP Packet Send and Receive Rules
+
+   The rules for how to send, and what to do when receiving an RTCP
+   packet are outlined here.  An implementation that allows operation in
+   a multicast environment or a multipoint unicast environment MUST meet
+   the requirements in Section 6.2.  Such an implementation MAY use the
+   algorithm defined in this section to meet those requirements, or MAY
+   use some other algorithm so long as it provides equivalent or better
+   performance.  An implementation which is constrained to two-party
+   unicast operation SHOULD still use randomization of the RTCP
+   transmission interval to avoid unintended synchronization of multiple
+   instances operating in the same environment, but MAY omit the "timer
+   reconsideration" and "reverse reconsideration" algorithms in Sections
+   6.3.3, 6.3.6 and 6.3.7.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 28]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   To execute these rules, a session participant must maintain several
+   pieces of state:
+
+   tp: the last time an RTCP packet was transmitted;
+
+   tc: the current time;
+
+   tn: the next scheduled transmission time of an RTCP packet;
+
+   pmembers: the estimated number of session members at the time tn
+      was last recomputed;
+
+   members: the most current estimate for the number of session
+      members;
+
+   senders: the most current estimate for the number of senders in
+      the session;
+
+   rtcp_bw: The target RTCP bandwidth, i.e., the total bandwidth
+      that will be used for RTCP packets by all members of this session,
+      in octets per second.  This will be a specified fraction of the
+      "session bandwidth" parameter supplied to the application at
+      startup.
+
+   we_sent: Flag that is true if the application has sent data
+      since the 2nd previous RTCP report was transmitted.
+
+   avg_rtcp_size: The average compound RTCP packet size, in octets,
+      over all RTCP packets sent and received by this participant.  The
+      size includes lower-layer transport and network protocol headers
+      (e.g., UDP and IP) as explained in Section 6.2.
+
+   initial: Flag that is true if the application has not yet sent
+      an RTCP packet.
+
+   Many of these rules make use of the "calculated interval" between
+   packet transmissions.  This interval is described in the following
+   section.
+
+6.3.1 Computing the RTCP Transmission Interval
+
+   To maintain scalability, the average interval between packets from a
+   session participant should scale with the group size.  This interval
+   is called the calculated interval.  It is obtained by combining a
+   number of the pieces of state described above.  The calculated
+   interval T is then determined as follows:
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 29]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   1. If the number of senders is less than or equal to 25% of the
+      membership (members), the interval depends on whether the
+      participant is a sender or not (based on the value of we_sent).
+      If the participant is a sender (we_sent true), the constant C is
+      set to the average RTCP packet size (avg_rtcp_size) divided by 25%
+      of the RTCP bandwidth (rtcp_bw), and the constant n is set to the
+      number of senders.  If we_sent is not true, the constant C is set
+      to the average RTCP packet size divided by 75% of the RTCP
+      bandwidth.  The constant n is set to the number of receivers
+      (members - senders).  If the number of senders is greater than
+      25%, senders and receivers are treated together.  The constant C
+      is set to the average RTCP packet size divided by the total RTCP
+      bandwidth and n is set to the total number of members.  As stated
+      in Section 6.2, an RTP profile MAY specify that the RTCP bandwidth
+      may be explicitly defined by two separate parameters (call them S
+      and R) for those participants which are senders and those which
+      are not.  In that case, the 25% fraction becomes S/(S+R) and the
+      75% fraction becomes R/(S+R).  Note that if R is zero, the
+      percentage of senders is never greater than S/(S+R), and the
+      implementation must avoid division by zero.
+
+   2. If the participant has not yet sent an RTCP packet (the variable
+      initial is true), the constant Tmin is set to 2.5 seconds, else it
+      is set to 5 seconds.
+
+   3. The deterministic calculated interval Td is set to max(Tmin, n*C).
+
+   4. The calculated interval T is set to a number uniformly distributed
+      between 0.5 and 1.5 times the deterministic calculated interval.
+
+   5. The resulting value of T is divided by e-3/2=1.21828 to compensate
+      for the fact that the timer reconsideration algorithm converges to
+      a value of the RTCP bandwidth below the intended average.
+
+   This procedure results in an interval which is random, but which, on
+   average, gives at least 25% of the RTCP bandwidth to senders and the
+   rest to receivers.  If the senders constitute more than one quarter
+   of the membership, this procedure splits the bandwidth equally among
+   all participants, on average.
+
+6.3.2 Initialization
+
+   Upon joining the session, the participant initializes tp to 0, tc to
+   0, senders to 0, pmembers to 1, members to 1, we_sent to false,
+   rtcp_bw to the specified fraction of the session bandwidth, initial
+   to true, and avg_rtcp_size to the probable size of the first RTCP
+   packet that the application will later construct.  The calculated
+   interval T is then computed, and the first packet is scheduled for
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 30]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   time tn = T.  This means that a transmission timer is set which
+   expires at time T.  Note that an application MAY use any desired
+   approach for implementing this timer.
+
+   The participant adds its own SSRC to the member table.
+
+6.3.3 Receiving an RTP or Non-BYE RTCP Packet
+
+   When an RTP or RTCP packet is received from a participant whose SSRC
+   is not in the member table, the SSRC is added to the table, and the
+   value for members is updated once the participant has been validated
+   as described in Section 6.2.1.  The same processing occurs for each
+   CSRC in a validated RTP packet.
+
+   When an RTP packet is received from a participant whose SSRC is not
+   in the sender table, the SSRC is added to the table, and the value
+   for senders is updated.
+
+   For each compound RTCP packet received, the value of avg_rtcp_size is
+   updated:
+
+      avg_rtcp_size = (1/16) * packet_size + (15/16) * avg_rtcp_size
+
+   where packet_size is the size of the RTCP packet just received.
+
+6.3.4 Receiving an RTCP BYE Packet
+
+   Except as described in Section 6.3.7 for the case when an RTCP BYE is
+   to be transmitted, if the received packet is an RTCP BYE packet, the
+   SSRC is checked against the member table.  If present, the entry is
+   removed from the table, and the value for members is updated.  The
+   SSRC is then checked against the sender table.  If present, the entry
+   is removed from the table, and the value for senders is updated.
+
+   Furthermore, to make the transmission rate of RTCP packets more
+   adaptive to changes in group membership, the following "reverse
+   reconsideration" algorithm SHOULD be executed when a BYE packet is
+   received that reduces members to a value less than pmembers:
+
+   o  The value for tn is updated according to the following formula:
+
+         tn = tc + (members/pmembers) * (tn - tc)
+
+   o  The value for tp is updated according the following formula:
+
+         tp = tc - (members/pmembers) * (tc - tp).
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 31]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  The next RTCP packet is rescheduled for transmission at time tn,
+      which is now earlier.
+
+   o  The value of pmembers is set equal to members.
+
+   This algorithm does not prevent the group size estimate from
+   incorrectly dropping to zero for a short time due to premature
+   timeouts when most participants of a large session leave at once but
+   some remain.  The algorithm does make the estimate return to the
+   correct value more rapidly.  This situation is unusual enough and the
+   consequences are sufficiently harmless that this problem is deemed
+   only a secondary concern.
+
+6.3.5 Timing Out an SSRC
+
+   At occasional intervals, the participant MUST check to see if any of
+   the other participants time out.  To do this, the participant
+   computes the deterministic (without the randomization factor)
+   calculated interval Td for a receiver, that is, with we_sent false.
+   Any other session member who has not sent an RTP or RTCP packet since
+   time tc - MTd (M is the timeout multiplier, and defaults to 5) is
+   timed out.  This means that its SSRC is removed from the member list,
+   and members is updated.  A similar check is performed on the sender
+   list.  Any member on the sender list who has not sent an RTP packet
+   since time tc - 2T (within the last two RTCP report intervals) is
+   removed from the sender list, and senders is updated.
+
+   If any members time out, the reverse reconsideration algorithm
+   described in Section 6.3.4 SHOULD be performed.
+
+   The participant MUST perform this check at least once per RTCP
+   transmission interval.
+
+6.3.6 Expiration of Transmission Timer
+
+   When the packet transmission timer expires, the participant performs
+   the following operations:
+
+   o  The transmission interval T is computed as described in Section
+      6.3.1, including the randomization factor.
+
+   o  If tp + T is less than or equal to tc, an RTCP packet is
+      transmitted.  tp is set to tc, then another value for T is
+      calculated as in the previous step and tn is set to tc + T.  The
+      transmission timer is set to expire again at time tn.  If tp + T
+      is greater than tc, tn is set to tp + T.  No RTCP packet is
+      transmitted.  The transmission timer is set to expire at time tn.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 32]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  pmembers is set to members.
+
+   If an RTCP packet is transmitted, the value of initial is set to
+   FALSE.  Furthermore, the value of avg_rtcp_size is updated:
+
+      avg_rtcp_size = (1/16) * packet_size + (15/16) * avg_rtcp_size
+
+   where packet_size is the size of the RTCP packet just transmitted.
+
+6.3.7 Transmitting a BYE Packet
+
+   When a participant wishes to leave a session, a BYE packet is
+   transmitted to inform the other participants of the event.  In order
+   to avoid a flood of BYE packets when many participants leave the
+   system, a participant MUST execute the following algorithm if the
+   number of members is more than 50 when the participant chooses to
+   leave.  This algorithm usurps the normal role of the members variable
+   to count BYE packets instead:
+
+   o  When the participant decides to leave the system, tp is reset to
+      tc, the current time, members and pmembers are initialized to 1,
+      initial is set to 1, we_sent is set to false, senders is set to 0,
+      and avg_rtcp_size is set to the size of the compound BYE packet.
+      The calculated interval T is computed.  The BYE packet is then
+      scheduled for time tn = tc + T.
+
+   o  Every time a BYE packet from another participant is received,
+      members is incremented by 1 regardless of whether that participant
+      exists in the member table or not, and when SSRC sampling is in
+      use, regardless of whether or not the BYE SSRC would be included
+      in the sample.  members is NOT incremented when other RTCP packets
+      or RTP packets are received, but only for BYE packets.  Similarly,
+      avg_rtcp_size is updated only for received BYE packets.  senders
+      is NOT updated when RTP packets arrive; it remains 0.
+
+   o  Transmission of the BYE packet then follows the rules for
+      transmitting a regular RTCP packet, as above.
+
+   This allows BYE packets to be sent right away, yet controls their
+   total bandwidth usage.  In the worst case, this could cause RTCP
+   control packets to use twice the bandwidth as normal (10%) -- 5% for
+   non-BYE RTCP packets and 5% for BYE.
+
+   A participant that does not want to wait for the above mechanism to
+   allow transmission of a BYE packet MAY leave the group without
+   sending a BYE at all.  That participant will eventually be timed out
+   by the other group members.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 33]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   If the group size estimate members is less than 50 when the
+   participant decides to leave, the participant MAY send a BYE packet
+   immediately.  Alternatively, the participant MAY choose to execute
+   the above BYE backoff algorithm.
+
+   In either case, a participant which never sent an RTP or RTCP packet
+   MUST NOT send a BYE packet when they leave the group.
+
+6.3.8 Updating we_sent
+
+   The variable we_sent contains true if the participant has sent an RTP
+   packet recently, false otherwise.  This determination is made by
+   using the same mechanisms as for managing the set of other
+   participants listed in the senders table.  If the participant sends
+   an RTP packet when we_sent is false, it adds itself to the sender
+   table and sets we_sent to true.  The reverse reconsideration
+   algorithm described in Section 6.3.4 SHOULD be performed to possibly
+   reduce the delay before sending an SR packet.  Every time another RTP
+   packet is sent, the time of transmission of that packet is maintained
+   in the table.  The normal sender timeout algorithm is then applied to
+   the participant -- if an RTP packet has not been transmitted since
+   time tc - 2T, the participant removes itself from the sender table,
+   decrements the sender count, and sets we_sent to false.
+
+6.3.9 Allocation of Source Description Bandwidth
+
+   This specification defines several source description (SDES) items in
+   addition to the mandatory CNAME item, such as NAME (personal name)
+   and EMAIL (email address).  It also provides a means to define new
+   application-specific RTCP packet types.  Applications should exercise
+   caution in allocating control bandwidth to this additional
+   information because it will slow down the rate at which reception
+   reports and CNAME are sent, thus impairing the performance of the
+   protocol.  It is RECOMMENDED that no more than 20% of the RTCP
+   bandwidth allocated to a single participant be used to carry the
+   additional information.  Furthermore, it is not intended that all
+   SDES items will be included in every application.  Those that are
+   included SHOULD be assigned a fraction of the bandwidth according to
+   their utility.  Rather than estimate these fractions dynamically, it
+   is recommended that the percentages be translated statically into
+   report interval counts based on the typical length of an item.
+
+   For example, an application may be designed to send only CNAME, NAME
+   and EMAIL and not any others.  NAME might be given much higher
+   priority than EMAIL because the NAME would be displayed continuously
+   in the application's user interface, whereas EMAIL would be displayed
+   only when requested.  At every RTCP interval, an RR packet and an
+   SDES packet with the CNAME item would be sent.  For a small session
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 34]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   operating at the minimum interval, that would be every 5 seconds on
+   the average.  Every third interval (15 seconds), one extra item would
+   be included in the SDES packet.  Seven out of eight times this would
+   be the NAME item, and every eighth time (2 minutes) it would be the
+   EMAIL item.
+
+   When multiple applications operate in concert using cross-application
+   binding through a common CNAME for each participant, for example in a
+   multimedia conference composed of an RTP session for each medium, the
+   additional SDES information MAY be sent in only one RTP session.  The
+   other sessions would carry only the CNAME item.  In particular, this
+   approach should be applied to the multiple sessions of a layered
+   encoding scheme (see Section 2.4).
+
+6.4 Sender and Receiver Reports
+
+   RTP receivers provide reception quality feedback using RTCP report
+   packets which may take one of two forms depending upon whether or not
+   the receiver is also a sender.  The only difference between the
+   sender report (SR) and receiver report (RR) forms, besides the packet
+   type code, is that the sender report includes a 20-byte sender
+   information section for use by active senders.  The SR is issued if a
+   site has sent any data packets during the interval since issuing the
+   last report or the previous one, otherwise the RR is issued.
+
+   Both the SR and RR forms include zero or more reception report
+   blocks, one for each of the synchronization sources from which this
+   receiver has received RTP data packets since the last report.
+   Reports are not issued for contributing sources listed in the CSRC
+   list.  Each reception report block provides statistics about the data
+   received from the particular source indicated in that block.  Since a
+   maximum of 31 reception report blocks will fit in an SR or RR packet,
+   additional RR packets SHOULD be stacked after the initial SR or RR
+   packet as needed to contain the reception reports for all sources
+   heard during the interval since the last report.  If there are too
+   many sources to fit all the necessary RR packets into one compound
+   RTCP packet without exceeding the MTU of the network path, then only
+   the subset that will fit into one MTU SHOULD be included in each
+   interval.  The subsets SHOULD be selected round-robin across multiple
+   intervals so that all sources are reported.
+
+   The next sections define the formats of the two reports, how they may
+   be extended in a profile-specific manner if an application requires
+   additional feedback information, and how the reports may be used.
+   Details of reception reporting by translators and mixers is given in
+   Section 7.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 35]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.4.1 SR: Sender Report RTCP Packet
+
+        0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+header |V=2|P|    RC   |   PT=SR=200   |             length            |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                         SSRC of sender                        |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+sender |              NTP timestamp, most significant word             |
+info   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |             NTP timestamp, least significant word             |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                         RTP timestamp                         |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                     sender's packet count                     |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                      sender's octet count                     |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+report |                 SSRC_1 (SSRC of first source)                 |
+block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  1    | fraction lost |       cumulative number of packets lost       |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |           extended highest sequence number received           |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                      interarrival jitter                      |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                         last SR (LSR)                         |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                   delay since last SR (DLSR)                  |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+report |                 SSRC_2 (SSRC of second source)                |
+block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  2    :                               ...                             :
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+       |                  profile-specific extensions                  |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The sender report packet consists of three sections, possibly
+   followed by a fourth profile-specific extension section if defined.
+   The first section, the header, is 8 octets long.  The fields have the
+   following meaning:
+
+   version (V): 2 bits
+      Identifies the version of RTP, which is the same in RTCP packets
+      as in RTP data packets.  The version defined by this specification
+      is two (2).
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 36]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   padding (P): 1 bit
+      If the padding bit is set, this individual RTCP packet contains
+      some additional padding octets at the end which are not part of
+      the control information but are included in the length field.  The
+      last octet of the padding is a count of how many padding octets
+      should be ignored, including itself (it will be a multiple of
+      four).  Padding may be needed by some encryption algorithms with
+      fixed block sizes.  In a compound RTCP packet, padding is only
+      required on one individual packet because the compound packet is
+      encrypted as a whole for the method in Section 9.1.  Thus, padding
+      MUST only be added to the last individual packet, and if padding
+      is added to that packet, the padding bit MUST be set only on that
+      packet.  This convention aids the header validity checks described
+      in Appendix A.2 and allows detection of packets from some early
+      implementations that incorrectly set the padding bit on the first
+      individual packet and add padding to the last individual packet.
+
+   reception report count (RC): 5 bits
+      The number of reception report blocks contained in this packet.  A
+      value of zero is valid.
+
+   packet type (PT): 8 bits
+      Contains the constant 200 to identify this as an RTCP SR packet.
+
+   length: 16 bits
+      The length of this RTCP packet in 32-bit words minus one,
+      including the header and any padding.  (The offset of one makes
+      zero a valid length and avoids a possible infinite loop in
+      scanning a compound RTCP packet, while counting 32-bit words
+      avoids a validity check for a multiple of 4.)
+
+   SSRC: 32 bits
+      The synchronization source identifier for the originator of this
+      SR packet.
+
+   The second section, the sender information, is 20 octets long and is
+   present in every sender report packet.  It summarizes the data
+   transmissions from this sender.  The fields have the following
+   meaning:
+
+   NTP timestamp: 64 bits
+      Indicates the wallclock time (see Section 4) when this report was
+      sent so that it may be used in combination with timestamps
+      returned in reception reports from other receivers to measure
+      round-trip propagation to those receivers.  Receivers should
+      expect that the measurement accuracy of the timestamp may be
+      limited to far less than the resolution of the NTP timestamp.  The
+      measurement uncertainty of the timestamp is not indicated as it
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 37]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      may not be known.  On a system that has no notion of wallclock
+      time but does have some system-specific clock such as "system
+      uptime", a sender MAY use that clock as a reference to calculate
+      relative NTP timestamps.  It is important to choose a commonly
+      used clock so that if separate implementations are used to produce
+      the individual streams of a multimedia session, all
+      implementations will use the same clock.  Until the year 2036,
+      relative and absolute timestamps will differ in the high bit so
+      (invalid) comparisons will show a large difference; by then one
+      hopes relative timestamps will no longer be needed.  A sender that
+      has no notion of wallclock or elapsed time MAY set the NTP
+      timestamp to zero.
+
+   RTP timestamp: 32 bits
+      Corresponds to the same time as the NTP timestamp (above), but in
+      the same units and with the same random offset as the RTP
+      timestamps in data packets.  This correspondence may be used for
+      intra- and inter-media synchronization for sources whose NTP
+      timestamps are synchronized, and may be used by media-independent
+      receivers to estimate the nominal RTP clock frequency.  Note that
+      in most cases this timestamp will not be equal to the RTP
+      timestamp in any adjacent data packet.  Rather, it MUST be
+      calculated from the corresponding NTP timestamp using the
+      relationship between the RTP timestamp counter and real time as
+      maintained by periodically checking the wallclock time at a
+      sampling instant.
+
+   sender's packet count: 32 bits
+      The total number of RTP data packets transmitted by the sender
+      since starting transmission up until the time this SR packet was
+      generated.  The count SHOULD be reset if the sender changes its
+      SSRC identifier.
+
+   sender's octet count: 32 bits
+      The total number of payload octets (i.e., not including header or
+      padding) transmitted in RTP data packets by the sender since
+      starting transmission up until the time this SR packet was
+      generated.  The count SHOULD be reset if the sender changes its
+      SSRC identifier.  This field can be used to estimate the average
+      payload data rate.
+
+   The third section contains zero or more reception report blocks
+   depending on the number of other sources heard by this sender since
+   the last report.  Each reception report block conveys statistics on
+   the reception of RTP packets from a single synchronization source.
+   Receivers SHOULD NOT carry over statistics when a source changes its
+   SSRC identifier due to a collision.  These statistics are:
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 38]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   SSRC_n (source identifier): 32 bits
+      The SSRC identifier of the source to which the information in this
+      reception report block pertains.
+
+   fraction lost: 8 bits
+      The fraction of RTP data packets from source SSRC_n lost since the
+      previous SR or RR packet was sent, expressed as a fixed point
+      number with the binary point at the left edge of the field.  (That
+      is equivalent to taking the integer part after multiplying the
+      loss fraction by 256.)  This fraction is defined to be the number
+      of packets lost divided by the number of packets expected, as
+      defined in the next paragraph.  An implementation is shown in
+      Appendix A.3.  If the loss is negative due to duplicates, the
+      fraction lost is set to zero.  Note that a receiver cannot tell
+      whether any packets were lost after the last one received, and
+      that there will be no reception report block issued for a source
+      if all packets from that source sent during the last reporting
+      interval have been lost.
+
+   cumulative number of packets lost: 24 bits
+      The total number of RTP data packets from source SSRC_n that have
+      been lost since the beginning of reception.  This number is
+      defined to be the number of packets expected less the number of
+      packets actually received, where the number of packets received
+      includes any which are late or duplicates.  Thus, packets that
+      arrive late are not counted as lost, and the loss may be negative
+      if there are duplicates.  The number of packets expected is
+      defined to be the extended last sequence number received, as
+      defined next, less the initial sequence number received.  This may
+      be calculated as shown in Appendix A.3.
+
+   extended highest sequence number received: 32 bits
+      The low 16 bits contain the highest sequence number received in an
+      RTP data packet from source SSRC_n, and the most significant 16
+      bits extend that sequence number with the corresponding count of
+      sequence number cycles, which may be maintained according to the
+      algorithm in Appendix A.1.  Note that different receivers within
+      the same session will generate different extensions to the
+      sequence number if their start times differ significantly.
+
+   interarrival jitter: 32 bits
+      An estimate of the statistical variance of the RTP data packet
+      interarrival time, measured in timestamp units and expressed as an
+      unsigned integer.  The interarrival jitter J is defined to be the
+      mean deviation (smoothed absolute value) of the difference D in
+      packet spacing at the receiver compared to the sender for a pair
+      of packets.  As shown in the equation below, this is equivalent to
+      the difference in the "relative transit time" for the two packets;
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 39]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      the relative transit time is the difference between a packet's RTP
+      timestamp and the receiver's clock at the time of arrival,
+      measured in the same units.
+
+      If Si is the RTP timestamp from packet i, and Ri is the time of
+      arrival in RTP timestamp units for packet i, then for two packets
+      i and j, D may be expressed as
+
+         D(i,j) = (Rj - Ri) - (Sj - Si) = (Rj - Sj) - (Ri - Si)
+
+      The interarrival jitter SHOULD be calculated continuously as each
+      data packet i is received from source SSRC_n, using this
+      difference D for that packet and the previous packet i-1 in order
+      of arrival (not necessarily in sequence), according to the formula
+
+         J(i) = J(i-1) + (|D(i-1,i)| - J(i-1))/16
+
+      Whenever a reception report is issued, the current value of J is
+      sampled.
+
+      The jitter calculation MUST conform to the formula specified here
+      in order to allow profile-independent monitors to make valid
+      interpretations of reports coming from different implementations.
+      This algorithm is the optimal first-order estimator and the gain
+      parameter 1/16 gives a good noise reduction ratio while
+      maintaining a reasonable rate of convergence [22].  A sample
+      implementation is shown in Appendix A.8.  See Section 6.4.4 for a
+      discussion of the effects of varying packet duration and delay
+      before transmission.
+
+   last SR timestamp (LSR): 32 bits
+      The middle 32 bits out of 64 in the NTP timestamp (as explained in
+      Section 4) received as part of the most recent RTCP sender report
+      (SR) packet from source SSRC_n.  If no SR has been received yet,
+      the field is set to zero.
+
+   delay since last SR (DLSR): 32 bits
+      The delay, expressed in units of 1/65536 seconds, between
+      receiving the last SR packet from source SSRC_n and sending this
+      reception report block.  If no SR packet has been received yet
+      from SSRC_n, the DLSR field is set to zero.
+
+      Let SSRC_r denote the receiver issuing this receiver report.
+      Source SSRC_n can compute the round-trip propagation delay to
+      SSRC_r by recording the time A when this reception report block is
+      received.  It calculates the total round-trip time A-LSR using the
+      last SR timestamp (LSR) field, and then subtracting this field to
+      leave the round-trip propagation delay as (A - LSR - DLSR).  This
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 40]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      is illustrated in Fig. 2.  Times are shown in both a hexadecimal
+      representation of the 32-bit fields and the equivalent floating-
+      point decimal representation.  Colons indicate a 32-bit field
+      divided into a 16-bit integer part and 16-bit fraction part.
+
+      This may be used as an approximate measure of distance to cluster
+      receivers, although some links have very asymmetric delays.
+
+   [10 Nov 1995 11:33:25.125 UTC]       [10 Nov 1995 11:33:36.5 UTC]
+   n                 SR(n)              A=b710:8000 (46864.500 s)
+   ---------------------------------------------------------------->
+                      v                 ^
+   ntp_sec =0xb44db705 v               ^ dlsr=0x0005:4000 (    5.250s)
+   ntp_frac=0x20000000  v             ^  lsr =0xb705:2000 (46853.125s)
+     (3024992005.125 s)  v           ^
+   r                      v         ^ RR(n)
+   ---------------------------------------------------------------->
+                          |<-DLSR->|
+                           (5.250 s)
+
+   A     0xb710:8000 (46864.500 s)
+   DLSR -0x0005:4000 (    5.250 s)
+   LSR  -0xb705:2000 (46853.125 s)
+   -------------------------------
+   delay 0x0006:2000 (    6.125 s)
+
+           Figure 2: Example for round-trip time computation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 41]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.4.2 RR: Receiver Report RTCP Packet
+
+        0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+header |V=2|P|    RC   |   PT=RR=201   |             length            |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                     SSRC of packet sender                     |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+report |                 SSRC_1 (SSRC of first source)                 |
+block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  1    | fraction lost |       cumulative number of packets lost       |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |           extended highest sequence number received           |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                      interarrival jitter                      |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                         last SR (LSR)                         |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                   delay since last SR (DLSR)                  |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+report |                 SSRC_2 (SSRC of second source)                |
+block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  2    :                               ...                             :
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+       |                  profile-specific extensions                  |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The format of the receiver report (RR) packet is the same as that of
+   the SR packet except that the packet type field contains the constant
+   201 and the five words of sender information are omitted (these are
+   the NTP and RTP timestamps and sender's packet and octet counts).
+   The remaining fields have the same meaning as for the SR packet.
+
+   An empty RR packet (RC = 0) MUST be put at the head of a compound
+   RTCP packet when there is no data transmission or reception to
+   report.
+
+6.4.3 Extending the Sender and Receiver Reports
+
+   A profile SHOULD define profile-specific extensions to the sender
+   report and receiver report if there is additional information that
+   needs to be reported regularly about the sender or receivers.  This
+   method SHOULD be used in preference to defining another RTCP packet
+   type because it requires less overhead:
+
+   o  fewer octets in the packet (no RTCP header or SSRC field);
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 42]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  simpler and faster parsing because applications running under that
+      profile would be programmed to always expect the extension fields
+      in the directly accessible location after the reception reports.
+
+   The extension is a fourth section in the sender- or receiver-report
+   packet which comes at the end after the reception report blocks, if
+   any.  If additional sender information is required, then for sender
+   reports it would be included first in the extension section, but for
+   receiver reports it would not be present.  If information about
+   receivers is to be included, that data SHOULD be structured as an
+   array of blocks parallel to the existing array of reception report
+   blocks; that is, the number of blocks would be indicated by the RC
+   field.
+
+6.4.4 Analyzing Sender and Receiver Reports
+
+   It is expected that reception quality feedback will be useful not
+   only for the sender but also for other receivers and third-party
+   monitors.  The sender may modify its transmissions based on the
+   feedback; receivers can determine whether problems are local,
+   regional or global; network managers may use profile-independent
+   monitors that receive only the RTCP packets and not the corresponding
+   RTP data packets to evaluate the performance of their networks for
+   multicast distribution.
+
+   Cumulative counts are used in both the sender information and
+   receiver report blocks so that differences may be calculated between
+   any two reports to make measurements over both short and long time
+   periods, and to provide resilience against the loss of a report.  The
+   difference between the last two reports received can be used to
+   estimate the recent quality of the distribution.  The NTP timestamp
+   is included so that rates may be calculated from these differences
+   over the interval between two reports.  Since that timestamp is
+   independent of the clock rate for the data encoding, it is possible
+   to implement encoding- and profile-independent quality monitors.
+
+   An example calculation is the packet loss rate over the interval
+   between two reception reports.  The difference in the cumulative
+   number of packets lost gives the number lost during that interval.
+   The difference in the extended last sequence numbers received gives
+   the number of packets expected during the interval.  The ratio of
+   these two is the packet loss fraction over the interval.  This ratio
+   should equal the fraction lost field if the two reports are
+   consecutive, but otherwise it may not.  The loss rate per second can
+   be obtained by dividing the loss fraction by the difference in NTP
+   timestamps, expressed in seconds.  The number of packets received is
+   the number of packets expected minus the number lost.  The number of
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 43]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   packets expected may also be used to judge the statistical validity
+   of any loss estimates.  For example, 1 out of 5 packets lost has a
+   lower significance than 200 out of 1000.
+
+   From the sender information, a third-party monitor can calculate the
+   average payload data rate and the average packet rate over an
+   interval without receiving the data.  Taking the ratio of the two
+   gives the average payload size.  If it can be assumed that packet
+   loss is independent of packet size, then the number of packets
+   received by a particular receiver times the average payload size (or
+   the corresponding packet size) gives the apparent throughput
+   available to that receiver.
+
+   In addition to the cumulative counts which allow long-term packet
+   loss measurements using differences between reports, the fraction
+   lost field provides a short-term measurement from a single report.
+   This becomes more important as the size of a session scales up enough
+   that reception state information might not be kept for all receivers
+   or the interval between reports becomes long enough that only one
+   report might have been received from a particular receiver.
+
+   The interarrival jitter field provides a second short-term measure of
+   network congestion.  Packet loss tracks persistent congestion while
+   the jitter measure tracks transient congestion.  The jitter measure
+   may indicate congestion before it leads to packet loss.  The
+   interarrival jitter field is only a snapshot of the jitter at the
+   time of a report and is not intended to be taken quantitatively.
+   Rather, it is intended for comparison across a number of reports from
+   one receiver over time or from multiple receivers, e.g., within a
+   single network, at the same time.  To allow comparison across
+   receivers, it is important the the jitter be calculated according to
+   the same formula by all receivers.
+
+   Because the jitter calculation is based on the RTP timestamp which
+   represents the instant when the first data in the packet was sampled,
+   any variation in the delay between that sampling instant and the time
+   the packet is transmitted will affect the resulting jitter that is
+   calculated.  Such a variation in delay would occur for audio packets
+   of varying duration.  It will also occur for video encodings because
+   the timestamp is the same for all the packets of one frame but those
+   packets are not all transmitted at the same time.  The variation in
+   delay until transmission does reduce the accuracy of the jitter
+   calculation as a measure of the behavior of the network by itself,
+   but it is appropriate to include considering that the receiver buffer
+   must accommodate it.  When the jitter calculation is used as a
+   comparative measure, the (constant) component due to variation in
+   delay until transmission subtracts out so that a change in the
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 44]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   network jitter component can then be observed unless it is relatively
+   small.  If the change is small, then it is likely to be
+   inconsequential.
+
+6.5 SDES: Source Description RTCP Packet
+
+        0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+header |V=2|P|    SC   |  PT=SDES=202  |             length            |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+chunk  |                          SSRC/CSRC_1                          |
+  1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                           SDES items                          |
+       |                              ...                              |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+chunk  |                          SSRC/CSRC_2                          |
+  2    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                           SDES items                          |
+       |                              ...                              |
+       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+
+   The SDES packet is a three-level structure composed of a header and
+   zero or more chunks, each of which is composed of items describing
+   the source identified in that chunk.  The items are described
+   individually in subsequent sections.
+
+   version (V), padding (P), length:
+      As described for the SR packet (see Section 6.4.1).
+
+   packet type (PT): 8 bits
+      Contains the constant 202 to identify this as an RTCP SDES packet.
+
+   source count (SC): 5 bits
+      The number of SSRC/CSRC chunks contained in this SDES packet.  A
+      value of zero is valid but useless.
+
+   Each chunk consists of an SSRC/CSRC identifier followed by a list of
+   zero or more items, which carry information about the SSRC/CSRC.
+   Each chunk starts on a 32-bit boundary.  Each item consists of an 8-
+   bit type field, an 8-bit octet count describing the length of the
+   text (thus, not including this two-octet header), and the text
+   itself.  Note that the text can be no longer than 255 octets, but
+   this is consistent with the need to limit RTCP bandwidth consumption.
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 45]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   The text is encoded according to the UTF-8 encoding specified in RFC
+   2279 [5].  US-ASCII is a subset of this encoding and requires no
+   additional encoding.  The presence of multi-octet encodings is
+   indicated by setting the most significant bit of a character to a
+   value of one.
+
+   Items are contiguous, i.e., items are not individually padded to a
+   32-bit boundary.  Text is not null terminated because some multi-
+   octet encodings include null octets.  The list of items in each chunk
+   MUST be terminated by one or more null octets, the first of which is
+   interpreted as an item type of zero to denote the end of the list.
+   No length octet follows the null item type octet, but additional null
+   octets MUST be included if needed to pad until the next 32-bit
+   boundary.  Note that this padding is separate from that indicated by
+   the P bit in the RTCP header.  A chunk with zero items (four null
+   octets) is valid but useless.
+
+   End systems send one SDES packet containing their own source
+   identifier (the same as the SSRC in the fixed RTP header).  A mixer
+   sends one SDES packet containing a chunk for each contributing source
+   from which it is receiving SDES information, or multiple complete
+   SDES packets in the format above if there are more than 31 such
+   sources (see Section 7).
+
+   The SDES items currently defined are described in the next sections.
+   Only the CNAME item is mandatory.  Some items shown here may be
+   useful only for particular profiles, but the item types are all
+   assigned from one common space to promote shared use and to simplify
+   profile-independent applications.  Additional items may be defined in
+   a profile by registering the type numbers with IANA as described in
+   Section 15.
+
+6.5.1 CNAME: Canonical End-Point Identifier SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    CNAME=1    |     length    | user and domain name        ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The CNAME identifier has the following properties:
+
+   o  Because the randomly allocated SSRC identifier may change if a
+      conflict is discovered or if a program is restarted, the CNAME
+      item MUST be included to provide the binding from the SSRC
+      identifier to an identifier for the source (sender or receiver)
+      that remains constant.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 46]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  Like the SSRC identifier, the CNAME identifier SHOULD also be
+      unique among all participants within one RTP session.
+
+   o  To provide a binding across multiple media tools used by one
+      participant in a set of related RTP sessions, the CNAME SHOULD be
+      fixed for that participant.
+
+   o  To facilitate third-party monitoring, the CNAME SHOULD be suitable
+      for either a program or a person to locate the source.
+
+   Therefore, the CNAME SHOULD be derived algorithmically and not
+   entered manually, when possible.  To meet these requirements, the
+   following format SHOULD be used unless a profile specifies an
+   alternate syntax or semantics.  The CNAME item SHOULD have the format
+   "user@host", or "host" if a user name is not available as on single-
+   user systems.  For both formats, "host" is either the fully qualified
+   domain name of the host from which the real-time data originates,
+   formatted according to the rules specified in RFC 1034 [6], RFC 1035
+   [7] and Section 2.1 of RFC 1123 [8]; or the standard ASCII
+   representation of the host's numeric address on the interface used
+   for the RTP communication.  For example, the standard ASCII
+   representation of an IP Version 4 address is "dotted decimal", also
+   known as dotted quad, and for IP Version 6, addresses are textually
+   represented as groups of hexadecimal digits separated by colons (with
+   variations as detailed in RFC 3513 [23]).  Other address types are
+   expected to have ASCII representations that are mutually unique.  The
+   fully qualified domain name is more convenient for a human observer
+   and may avoid the need to send a NAME item in addition, but it may be
+   difficult or impossible to obtain reliably in some operating
+   environments.  Applications that may be run in such environments
+   SHOULD use the ASCII representation of the address instead.
+
+   Examples are "doe@sleepy.example.com", "doe@192.0.2.89" or
+   "doe@2201:056D::112E:144A:1E24" for a multi-user system.  On a system
+   with no user name, examples would be "sleepy.example.com",
+   "192.0.2.89" or "2201:056D::112E:144A:1E24".
+
+   The user name SHOULD be in a form that a program such as "finger" or
+   "talk" could use, i.e., it typically is the login name rather than
+   the personal name.  The host name is not necessarily identical to the
+   one in the participant's electronic mail address.
+
+   This syntax will not provide unique identifiers for each source if an
+   application permits a user to generate multiple sources from one
+   host.  Such an application would have to rely on the SSRC to further
+   identify the source, or the profile for that application would have
+   to specify additional syntax for the CNAME identifier.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 47]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   If each application creates its CNAME independently, the resulting
+   CNAMEs may not be identical as would be required to provide a binding
+   across multiple media tools belonging to one participant in a set of
+   related RTP sessions.  If cross-media binding is required, it may be
+   necessary for the CNAME of each tool to be externally configured with
+   the same value by a coordination tool.
+
+   Application writers should be aware that private network address
+   assignments such as the Net-10 assignment proposed in RFC 1918 [24]
+   may create network addresses that are not globally unique.  This
+   would lead to non-unique CNAMEs if hosts with private addresses and
+   no direct IP connectivity to the public Internet have their RTP
+   packets forwarded to the public Internet through an RTP-level
+   translator.  (See also RFC 1627 [25].)  To handle this case,
+   applications MAY provide a means to configure a unique CNAME, but the
+   burden is on the translator to translate CNAMEs from private
+   addresses to public addresses if necessary to keep private addresses
+   from being exposed.
+
+6.5.2 NAME: User Name SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     NAME=2    |     length    | common name of source       ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   This is the real name used to describe the source, e.g., "John Doe,
+   Bit Recycler".  It may be in any form desired by the user.  For
+   applications such as conferencing, this form of name may be the most
+   desirable for display in participant lists, and therefore might be
+   sent most frequently of those items other than CNAME.  Profiles MAY
+   establish such priorities.  The NAME value is expected to remain
+   constant at least for the duration of a session.  It SHOULD NOT be
+   relied upon to be unique among all participants in the session.
+
+6.5.3 EMAIL: Electronic Mail Address SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    EMAIL=3    |     length    | email address of source     ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The email address is formatted according to RFC 2822 [9], for
+   example, "John.Doe@example.com".  The EMAIL value is expected to
+   remain constant for the duration of a session.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 48]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.5.4 PHONE: Phone Number SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    PHONE=4    |     length    | phone number of source      ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The phone number SHOULD be formatted with the plus sign replacing the
+   international access code.  For example, "+1 908 555 1212" for a
+   number in the United States.
+
+6.5.5 LOC: Geographic User Location SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     LOC=5     |     length    | geographic location of site ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Depending on the application, different degrees of detail are
+   appropriate for this item.  For conference applications, a string
+   like "Murray Hill, New Jersey" may be sufficient, while, for an
+   active badge system, strings like "Room 2A244, AT&T BL MH" might be
+   appropriate.  The degree of detail is left to the implementation
+   and/or user, but format and content MAY be prescribed by a profile.
+   The LOC value is expected to remain constant for the duration of a
+   session, except for mobile hosts.
+
+6.5.6 TOOL: Application or Tool Name SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     TOOL=6    |     length    |name/version of source appl. ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   A string giving the name and possibly version of the application
+   generating the stream, e.g., "videotool 1.2".  This information may
+   be useful for debugging purposes and is similar to the Mailer or
+   Mail-System-Version SMTP headers.  The TOOL value is expected to
+   remain constant for the duration of the session.
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 49]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+6.5.7 NOTE: Notice/Status SDES Item
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     NOTE=7    |     length    | note about the source       ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The following semantics are suggested for this item, but these or
+   other semantics MAY be explicitly defined by a profile.  The NOTE
+   item is intended for transient messages describing the current state
+   of the source, e.g., "on the phone, can't talk".  Or, during a
+   seminar, this item might be used to convey the title of the talk.  It
+   should be used only to carry exceptional information and SHOULD NOT
+   be included routinely by all participants because this would slow
+   down the rate at which reception reports and CNAME are sent, thus
+   impairing the performance of the protocol.  In particular, it SHOULD
+   NOT be included as an item in a user's configuration file nor
+   automatically generated as in a quote-of-the-day.
+
+   Since the NOTE item may be important to display while it is active,
+   the rate at which other non-CNAME items such as NAME are transmitted
+   might be reduced so that the NOTE item can take that part of the RTCP
+   bandwidth.  When the transient message becomes inactive, the NOTE
+   item SHOULD continue to be transmitted a few times at the same
+   repetition rate but with a string of length zero to signal the
+   receivers.  However, receivers SHOULD also consider the NOTE item
+   inactive if it is not received for a small multiple of the repetition
+   rate, or perhaps 20-30 RTCP intervals.
+
+6.5.8 PRIV: Private Extensions SDES Item
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |     PRIV=8    |     length    | prefix length |prefix string...
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    ...             |                  value string               ...
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   This item is used to define experimental or application-specific SDES
+   extensions.  The item contains a prefix consisting of a length-string
+   pair, followed by the value string filling the remainder of the item
+   and carrying the desired information.  The prefix length field is 8
+   bits long.  The prefix string is a name chosen by the person defining
+   the PRIV item to be unique with respect to other PRIV items this
+   application might receive.  The application creator might choose to
+   use the application name plus an additional subtype identification if
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 50]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   needed.  Alternatively, it is RECOMMENDED that others choose a name
+   based on the entity they represent, then coordinate the use of the
+   name within that entity.
+
+   Note that the prefix consumes some space within the item's total
+   length of 255 octets, so the prefix should be kept as short as
+   possible.  This facility and the constrained RTCP bandwidth SHOULD
+   NOT be overloaded; it is not intended to satisfy all the control
+   communication requirements of all applications.
+
+   SDES PRIV prefixes will not be registered by IANA.  If some form of
+   the PRIV item proves to be of general utility, it SHOULD instead be
+   assigned a regular SDES item type registered with IANA so that no
+   prefix is required.  This simplifies use and increases transmission
+   efficiency.
+
+6.6 BYE: Goodbye RTCP Packet
+
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |V=2|P|    SC   |   PT=BYE=203  |             length            |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                           SSRC/CSRC                           |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      :                              ...                              :
+      +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+(opt) |     length    |               reason for leaving            ...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The BYE packet indicates that one or more sources are no longer
+   active.
+
+   version (V), padding (P), length:
+      As described for the SR packet (see Section 6.4.1).
+
+   packet type (PT): 8 bits
+      Contains the constant 203 to identify this as an RTCP BYE packet.
+
+   source count (SC): 5 bits
+      The number of SSRC/CSRC identifiers included in this BYE packet.
+      A count value of zero is valid, but useless.
+
+   The rules for when a BYE packet should be sent are specified in
+   Sections 6.3.7 and 8.2.
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 51]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   If a BYE packet is received by a mixer, the mixer SHOULD forward the
+   BYE packet with the SSRC/CSRC identifier(s) unchanged.  If a mixer
+   shuts down, it SHOULD send a BYE packet listing all contributing
+   sources it handles, as well as its own SSRC identifier.  Optionally,
+   the BYE packet MAY include an 8-bit octet count followed by that many
+   octets of text indicating the reason for leaving, e.g., "camera
+   malfunction" or "RTP loop detected".  The string has the same
+   encoding as that described for SDES.  If the string fills the packet
+   to the next 32-bit boundary, the string is not null terminated.  If
+   not, the BYE packet MUST be padded with null octets to the next 32-
+   bit boundary.  This padding is separate from that indicated by the P
+   bit in the RTCP header.
+
+6.7 APP: Application-Defined RTCP Packet
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |V=2|P| subtype |   PT=APP=204  |             length            |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                           SSRC/CSRC                           |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                          name (ASCII)                         |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                   application-dependent data                ...
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The APP packet is intended for experimental use as new applications
+   and new features are developed, without requiring packet type value
+   registration.  APP packets with unrecognized names SHOULD be ignored.
+   After testing and if wider use is justified, it is RECOMMENDED that
+   each APP packet be redefined without the subtype and name fields and
+   registered with IANA using an RTCP packet type.
+
+   version (V), padding (P), length:
+      As described for the SR packet (see Section 6.4.1).
+
+   subtype: 5 bits
+      May be used as a subtype to allow a set of APP packets to be
+      defined under one unique name, or for any application-dependent
+      data.
+
+   packet type (PT): 8 bits
+      Contains the constant 204 to identify this as an RTCP APP packet.
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 52]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   name: 4 octets
+      A name chosen by the person defining the set of APP packets to be
+      unique with respect to other APP packets this application might
+      receive.  The application creator might choose to use the
+      application name, and then coordinate the allocation of subtype
+      values to others who want to define new packet types for the
+      application.  Alternatively, it is RECOMMENDED that others choose
+      a name based on the entity they represent, then coordinate the use
+      of the name within that entity.  The name is interpreted as a
+      sequence of four ASCII characters, with uppercase and lowercase
+      characters treated as distinct.
+
+   application-dependent data: variable length
+      Application-dependent data may or may not appear in an APP packet.
+      It is interpreted by the application and not RTP itself.  It MUST
+      be a multiple of 32 bits long.
+
+7. RTP Translators and Mixers
+
+   In addition to end systems, RTP supports the notion of "translators"
+   and "mixers", which could be considered as "intermediate systems" at
+   the RTP level.  Although this support adds some complexity to the
+   protocol, the need for these functions has been clearly established
+   by experiments with multicast audio and video applications in the
+   Internet.  Example uses of translators and mixers given in Section
+   2.3 stem from the presence of firewalls and low bandwidth
+   connections, both of which are likely to remain.
+
+7.1 General Description
+
+   An RTP translator/mixer connects two or more transport-level
+   "clouds".  Typically, each cloud is defined by a common network and
+   transport protocol (e.g., IP/UDP) plus a multicast address and
+   transport level destination port or a pair of unicast addresses and
+   ports.  (Network-level protocol translators, such as IP version 4 to
+   IP version 6, may be present within a cloud invisibly to RTP.)  One
+   system may serve as a translator or mixer for a number of RTP
+   sessions, but each is considered a logically separate entity.
+
+   In order to avoid creating a loop when a translator or mixer is
+   installed, the following rules MUST be observed:
+
+   o  Each of the clouds connected by translators and mixers
+      participating in one RTP session either MUST be distinct from all
+      the others in at least one of these parameters (protocol, address,
+      port), or MUST be isolated at the network level from the others.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 53]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  A derivative of the first rule is that there MUST NOT be multiple
+      translators or mixers connected in parallel unless by some
+      arrangement they partition the set of sources to be forwarded.
+
+   Similarly, all RTP end systems that can communicate through one or
+   more RTP translators or mixers share the same SSRC space, that is,
+   the SSRC identifiers MUST be unique among all these end systems.
+   Section 8.2 describes the collision resolution algorithm by which
+   SSRC identifiers are kept unique and loops are detected.
+
+   There may be many varieties of translators and mixers designed for
+   different purposes and applications.  Some examples are to add or
+   remove encryption, change the encoding of the data or the underlying
+   protocols, or replicate between a multicast address and one or more
+   unicast addresses.  The distinction between translators and mixers is
+   that a translator passes through the data streams from different
+   sources separately, whereas a mixer combines them to form one new
+   stream:
+
+   Translator: Forwards RTP packets with their SSRC identifier
+      intact; this makes it possible for receivers to identify
+      individual sources even though packets from all the sources pass
+      through the same translator and carry the translator's network
+      source address.  Some kinds of translators will pass through the
+      data untouched, but others MAY change the encoding of the data and
+      thus the RTP data payload type and timestamp.  If multiple data
+      packets are re-encoded into one, or vice versa, a translator MUST
+      assign new sequence numbers to the outgoing packets.  Losses in
+      the incoming packet stream may induce corresponding gaps in the
+      outgoing sequence numbers.  Receivers cannot detect the presence
+      of a translator unless they know by some other means what payload
+      type or transport address was used by the original source.
+
+   Mixer: Receives streams of RTP data packets from one or more
+      sources, possibly changes the data format, combines the streams in
+      some manner and then forwards the combined stream.  Since the
+      timing among multiple input sources will not generally be
+      synchronized, the mixer will make timing adjustments among the
+      streams and generate its own timing for the combined stream, so it
+      is the synchronization source.  Thus, all data packets forwarded
+      by a mixer MUST be marked with the mixer's own SSRC identifier.
+      In order to preserve the identity of the original sources
+      contributing to the mixed packet, the mixer SHOULD insert their
+      SSRC identifiers into the CSRC identifier list following the fixed
+      RTP header of the packet.  A mixer that is also itself a
+      contributing source for some packet SHOULD explicitly include its
+      own SSRC identifier in the CSRC list for that packet.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 54]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      For some applications, it MAY be acceptable for a mixer not to
+      identify sources in the CSRC list.  However, this introduces the
+      danger that loops involving those sources could not be detected.
+
+   The advantage of a mixer over a translator for applications like
+   audio is that the output bandwidth is limited to that of one source
+   even when multiple sources are active on the input side.  This may be
+   important for low-bandwidth links.  The disadvantage is that
+   receivers on the output side don't have any control over which
+   sources are passed through or muted, unless some mechanism is
+   implemented for remote control of the mixer.  The regeneration of
+   synchronization information by mixers also means that receivers can't
+   do inter-media synchronization of the original streams.  A multi-
+   media mixer could do it.
+
+         [E1]                                    [E6]
+          |                                       |
+    E1:17 |                                 E6:15 |
+          |                                       |   E6:15
+          V  M1:48 (1,17)         M1:48 (1,17)    V   M1:48 (1,17)
+         (M1)-------------><T1>-----------------><T2>-------------->[E7]
+          ^                 ^     E4:47           ^   E4:47
+     E2:1 |           E4:47 |                     |   M3:89 (64,45)
+          |                 |                     |
+         [E2]              [E4]     M3:89 (64,45) |
+                                                  |        legend:
+   [E3] --------->(M2)----------->(M3)------------|        [End system]
+          E3:64        M2:12 (64)  ^                       (Mixer)
+                                   | E5:45                 <Translator>
+                                   |
+                                  [E5]          source: SSRC (CSRCs)
+                                                ------------------->
+
+   Figure 3: Sample RTP network with end systems, mixers and translators
+
+   A collection of mixers and translators is shown in Fig. 3 to
+   illustrate their effect on SSRC and CSRC identifiers.  In the figure,
+   end systems are shown as rectangles (named E), translators as
+   triangles (named T) and mixers as ovals (named M).  The notation "M1:
+   48(1,17)" designates a packet originating a mixer M1, identified by
+   M1's (random) SSRC value of 48 and two CSRC identifiers, 1 and 17,
+   copied from the SSRC identifiers of packets from E1 and E2.
+
+7.2 RTCP Processing in Translators
+
+   In addition to forwarding data packets, perhaps modified, translators
+   and mixers MUST also process RTCP packets.  In many cases, they will
+   take apart the compound RTCP packets received from end systems to
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 55]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   aggregate SDES information and to modify the SR or RR packets.
+   Retransmission of this information may be triggered by the packet
+   arrival or by the RTCP interval timer of the translator or mixer
+   itself.
+
+   A translator that does not modify the data packets, for example one
+   that just replicates between a multicast address and a unicast
+   address, MAY simply forward RTCP packets unmodified as well.  A
+   translator that transforms the payload in some way MUST make
+   corresponding transformations in the SR and RR information so that it
+   still reflects the characteristics of the data and the reception
+   quality.  These translators MUST NOT simply forward RTCP packets.  In
+   general, a translator SHOULD NOT aggregate SR and RR packets from
+   different sources into one packet since that would reduce the
+   accuracy of the propagation delay measurements based on the LSR and
+   DLSR fields.
+
+   SR sender information:  A translator does not generate its own
+      sender information, but forwards the SR packets received from one
+      cloud to the others.  The SSRC is left intact but the sender
+      information MUST be modified if required by the translation.  If a
+      translator changes the data encoding, it MUST change the "sender's
+      byte count" field.  If it also combines several data packets into
+      one output packet, it MUST change the "sender's packet count"
+      field.  If it changes the timestamp frequency, it MUST change the
+      "RTP timestamp" field in the SR packet.
+
+   SR/RR reception report blocks:  A translator forwards reception
+      reports received from one cloud to the others.  Note that these
+      flow in the direction opposite to the data.  The SSRC is left
+      intact.  If a translator combines several data packets into one
+      output packet, and therefore changes the sequence numbers, it MUST
+      make the inverse manipulation for the packet loss fields and the
+      "extended last sequence number" field.  This may be complex.  In
+      the extreme case, there may be no meaningful way to translate the
+      reception reports, so the translator MAY pass on no reception
+      report at all or a synthetic report based on its own reception.
+      The general rule is to do what makes sense for a particular
+      translation.
+
+      A translator does not require an SSRC identifier of its own, but
+      MAY choose to allocate one for the purpose of sending reports
+      about what it has received.  These would be sent to all the
+      connected clouds, each corresponding to the translation of the
+      data stream as sent to that cloud, since reception reports are
+      normally multicast to all participants.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 56]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   SDES:  Translators typically forward without change the SDES
+      information they receive from one cloud to the others, but MAY,
+      for example, decide to filter non-CNAME SDES information if
+      bandwidth is limited.  The CNAMEs MUST be forwarded to allow SSRC
+      identifier collision detection to work.  A translator that
+      generates its own RR packets MUST send SDES CNAME information
+      about itself to the same clouds that it sends those RR packets.
+
+   BYE:  Translators forward BYE packets unchanged.  A translator
+      that is about to cease forwarding packets SHOULD send a BYE packet
+      to each connected cloud containing all the SSRC identifiers that
+      were previously being forwarded to that cloud, including the
+      translator's own SSRC identifier if it sent reports of its own.
+
+   APP:  Translators forward APP packets unchanged.
+
+7.3 RTCP Processing in Mixers
+
+   Since a mixer generates a new data stream of its own, it does not
+   pass through SR or RR packets at all and instead generates new
+   information for both sides.
+
+   SR sender information:  A mixer does not pass through sender
+      information from the sources it mixes because the characteristics
+      of the source streams are lost in the mix.  As a synchronization
+      source, the mixer SHOULD generate its own SR packets with sender
+      information about the mixed data stream and send them in the same
+      direction as the mixed stream.
+
+   SR/RR reception report blocks:  A mixer generates its own
+      reception reports for sources in each cloud and sends them out
+      only to the same cloud.  It MUST NOT send these reception reports
+      to the other clouds and MUST NOT forward reception reports from
+      one cloud to the others because the sources would not be SSRCs
+      there (only CSRCs).
+
+   SDES:  Mixers typically forward without change the SDES
+      information they receive from one cloud to the others, but MAY,
+      for example, decide to filter non-CNAME SDES information if
+      bandwidth is limited.  The CNAMEs MUST be forwarded to allow SSRC
+      identifier collision detection to work.  (An identifier in a CSRC
+      list generated by a mixer might collide with an SSRC identifier
+      generated by an end system.)  A mixer MUST send SDES CNAME
+      information about itself to the same clouds that it sends SR or RR
+      packets.
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 57]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      Since mixers do not forward SR or RR packets, they will typically
+      be extracting SDES packets from a compound RTCP packet.  To
+      minimize overhead, chunks from the SDES packets MAY be aggregated
+      into a single SDES packet which is then stacked on an SR or RR
+      packet originating from the mixer.  A mixer which aggregates SDES
+      packets will use more RTCP bandwidth than an individual source
+      because the compound packets will be longer, but that is
+      appropriate since the mixer represents multiple sources.
+      Similarly, a mixer which passes through SDES packets as they are
+      received will be transmitting RTCP packets at higher than the
+      single source rate, but again that is correct since the packets
+      come from multiple sources.  The RTCP packet rate may be different
+      on each side of the mixer.
+
+      A mixer that does not insert CSRC identifiers MAY also refrain
+      from forwarding SDES CNAMEs.  In this case, the SSRC identifier
+      spaces in the two clouds are independent.  As mentioned earlier,
+      this mode of operation creates a danger that loops can't be
+      detected.
+
+   BYE:  Mixers MUST forward BYE packets.  A mixer that is about to
+      cease forwarding packets SHOULD send a BYE packet to each
+      connected cloud containing all the SSRC identifiers that were
+      previously being forwarded to that cloud, including the mixer's
+      own SSRC identifier if it sent reports of its own.
+
+   APP:  The treatment of APP packets by mixers is application-specific.
+
+7.4 Cascaded Mixers
+
+   An RTP session may involve a collection of mixers and translators as
+   shown in Fig. 3.  If two mixers are cascaded, such as M2 and M3 in
+   the figure, packets received by a mixer may already have been mixed
+   and may include a CSRC list with multiple identifiers.  The second
+   mixer SHOULD build the CSRC list for the outgoing packet using the
+   CSRC identifiers from already-mixed input packets and the SSRC
+   identifiers from unmixed input packets.  This is shown in the output
+   arc from mixer M3 labeled M3:89(64,45) in the figure.  As in the case
+   of mixers that are not cascaded, if the resulting CSRC list has more
+   than 15 identifiers, the remainder cannot be included.
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 58]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+8.  SSRC Identifier Allocation and Use
+
+   The SSRC identifier carried in the RTP header and in various fields
+   of RTCP packets is a random 32-bit number that is required to be
+   globally unique within an RTP session.  It is crucial that the number
+   be chosen with care in order that participants on the same network or
+   starting at the same time are not likely to choose the same number.
+
+   It is not sufficient to use the local network address (such as an
+   IPv4 address) for the identifier because the address may not be
+   unique.  Since RTP translators and mixers enable interoperation among
+   multiple networks with different address spaces, the allocation
+   patterns for addresses within two spaces might result in a much
+   higher rate of collision than would occur with random allocation.
+
+   Multiple sources running on one host would also conflict.
+
+   It is also not sufficient to obtain an SSRC identifier simply by
+   calling random() without carefully initializing the state.  An
+   example of how to generate a random identifier is presented in
+   Appendix A.6.
+
+8.1 Probability of Collision
+
+   Since the identifiers are chosen randomly, it is possible that two or
+   more sources will choose the same number.  Collision occurs with the
+   highest probability when all sources are started simultaneously, for
+   example when triggered automatically by some session management
+   event.  If N is the number of sources and L the length of the
+   identifier (here, 32 bits), the probability that two sources
+   independently pick the same value can be approximated for large N
+   [26] as 1 - exp(-N**2 / 2**(L+1)).  For N=1000, the probability is
+   roughly 10**-4.
+
+   The typical collision probability is much lower than the worst-case
+   above.  When one new source joins an RTP session in which all the
+   other sources already have unique identifiers, the probability of
+   collision is just the fraction of numbers used out of the space.
+   Again, if N is the number of sources and L the length of the
+   identifier, the probability of collision is N / 2**L.  For N=1000,
+   the probability is roughly 2*10**-7.
+
+   The probability of collision is further reduced by the opportunity
+   for a new source to receive packets from other participants before
+   sending its first packet (either data or control).  If the new source
+   keeps track of the other participants (by SSRC identifier), then
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 59]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   before transmitting its first packet the new source can verify that
+   its identifier does not conflict with any that have been received, or
+   else choose again.
+
+8.2 Collision Resolution and Loop Detection
+
+   Although the probability of SSRC identifier collision is low, all RTP
+   implementations MUST be prepared to detect collisions and take the
+   appropriate actions to resolve them.  If a source discovers at any
+   time that another source is using the same SSRC identifier as its
+   own, it MUST send an RTCP BYE packet for the old identifier and
+   choose another random one.  (As explained below, this step is taken
+   only once in case of a loop.)  If a receiver discovers that two other
+   sources are colliding, it MAY keep the packets from one and discard
+   the packets from the other when this can be detected by different
+   source transport addresses or CNAMEs.  The two sources are expected
+   to resolve the collision so that the situation doesn't last.
+
+   Because the random SSRC identifiers are kept globally unique for each
+   RTP session, they can also be used to detect loops that may be
+   introduced by mixers or translators.  A loop causes duplication of
+   data and control information, either unmodified or possibly mixed, as
+   in the following examples:
+
+   o  A translator may incorrectly forward a packet to the same
+      multicast group from which it has received the packet, either
+      directly or through a chain of translators.  In that case, the
+      same packet appears several times, originating from different
+      network sources.
+
+   o  Two translators incorrectly set up in parallel, i.e., with the
+      same multicast groups on both sides, would both forward packets
+      from one multicast group to the other.  Unidirectional translators
+      would produce two copies; bidirectional translators would form a
+      loop.
+
+   o  A mixer can close a loop by sending to the same transport
+      destination upon which it receives packets, either directly or
+      through another mixer or translator.  In this case a source might
+      show up both as an SSRC on a data packet and a CSRC in a mixed
+      data packet.
+
+   A source may discover that its own packets are being looped, or that
+   packets from another source are being looped (a third-party loop).
+   Both loops and collisions in the random selection of a source
+   identifier result in packets arriving with the same SSRC identifier
+   but a different source transport address, which may be that of the
+   end system originating the packet or an intermediate system.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 60]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Therefore, if a source changes its source transport address, it MAY
+   also choose a new SSRC identifier to avoid being interpreted as a
+   looped source.  (This is not MUST because in some applications of RTP
+   sources may be expected to change addresses during a session.)  Note
+   that if a translator restarts and consequently changes the source
+   transport address (e.g., changes the UDP source port number) on which
+   it forwards packets, then all those packets will appear to receivers
+   to be looped because the SSRC identifiers are applied by the original
+   source and will not change.  This problem can be avoided by keeping
+   the source transport address fixed across restarts, but in any case
+   will be resolved after a timeout at the receivers.
+
+   Loops or collisions occurring on the far side of a translator or
+   mixer cannot be detected using the source transport address if all
+   copies of the packets go through the translator or mixer, however,
+   collisions may still be detected when chunks from two RTCP SDES
+   packets contain the same SSRC identifier but different CNAMEs.
+
+   To detect and resolve these conflicts, an RTP implementation MUST
+   include an algorithm similar to the one described below, though the
+   implementation MAY choose a different policy for which packets from
+   colliding third-party sources are kept.  The algorithm described
+   below ignores packets from a new source or loop that collide with an
+   established source.  It resolves collisions with the participant's
+   own SSRC identifier by sending an RTCP BYE for the old identifier and
+   choosing a new one.  However, when the collision was induced by a
+   loop of the participant's own packets, the algorithm will choose a
+   new identifier only once and thereafter ignore packets from the
+   looping source transport address.  This is required to avoid a flood
+   of BYE packets.
+
+   This algorithm requires keeping a table indexed by the source
+   identifier and containing the source transport addresses from the
+   first RTP packet and first RTCP packet received with that identifier,
+   along with other state for that source.  Two source transport
+   addresses are required since, for example, the UDP source port
+   numbers may be different on RTP and RTCP packets.  However, it may be
+   assumed that the network address is the same in both source transport
+   addresses.
+
+   Each SSRC or CSRC identifier received in an RTP or RTCP packet is
+   looked up in the source identifier table in order to process that
+   data or control information.  The source transport address from the
+   packet is compared to the corresponding source transport address in
+   the table to detect a loop or collision if they don't match.  For
+   control packets, each element with its own SSRC identifier, for
+   example an SDES chunk, requires a separate lookup.  (The SSRC
+   identifier in a reception report block is an exception because it
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 61]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   identifies a source heard by the reporter, and that SSRC identifier
+   is unrelated to the source transport address of the RTCP packet sent
+   by the reporter.)  If the SSRC or CSRC is not found, a new entry is
+   created.  These table entries are removed when an RTCP BYE packet is
+   received with the corresponding SSRC identifier and validated by a
+   matching source transport address, or after no packets have arrived
+   for a relatively long time (see Section 6.2.1).
+
+   Note that if two sources on the same host are transmitting with the
+   same source identifier at the time a receiver begins operation, it
+   would be possible that the first RTP packet received came from one of
+   the sources while the first RTCP packet received came from the other.
+   This would cause the wrong RTCP information to be associated with the
+   RTP data, but this situation should be sufficiently rare and harmless
+   that it may be disregarded.
+
+   In order to track loops of the participant's own data packets, the
+   implementation MUST also keep a separate list of source transport
+   addresses (not identifiers) that have been found to be conflicting.
+   As in the source identifier table, two source transport addresses
+   MUST be kept to separately track conflicting RTP and RTCP packets.
+   Note that the conflicting address list should be short, usually
+   empty.  Each element in this list stores the source addresses plus
+   the time when the most recent conflicting packet was received.  An
+   element MAY be removed from the list when no conflicting packet has
+   arrived from that source for a time on the order of 10 RTCP report
+   intervals (see Section 6.2).
+
+   For the algorithm as shown, it is assumed that the participant's own
+   source identifier and state are included in the source identifier
+   table.  The algorithm could be restructured to first make a separate
+   comparison against the participant's own source identifier.
+
+      if (SSRC or CSRC identifier is not found in the source
+          identifier table) {
+          create a new entry storing the data or control source
+              transport address, the SSRC or CSRC and other state;
+      }
+
+      /* Identifier is found in the table */
+
+      else if (table entry was created on receipt of a control packet
+               and this is the first data packet or vice versa) {
+          store the source transport address from this packet;
+      }
+      else if (source transport address from the packet does not match
+               the one saved in the table entry for this identifier) {
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 62]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+          /* An identifier collision or a loop is indicated */
+
+          if (source identifier is not the participant's own) {
+              /* OPTIONAL error counter step */
+              if (source identifier is from an RTCP SDES chunk
+                  containing a CNAME item that differs from the CNAME
+                  in the table entry) {
+                  count a third-party collision;
+              } else {
+                  count a third-party loop;
+              }
+              abort processing of data packet or control element;
+              /* MAY choose a different policy to keep new source */
+          }
+
+          /* A collision or loop of the participant's own packets */
+
+          else if (source transport address is found in the list of
+                   conflicting data or control source transport
+                   addresses) {
+              /* OPTIONAL error counter step */
+              if (source identifier is not from an RTCP SDES chunk
+                  containing a CNAME item or CNAME is the
+                  participant's own) {
+                  count occurrence of own traffic looped;
+              }
+              mark current time in conflicting address list entry;
+              abort processing of data packet or control element;
+          }
+
+          /* New collision, change SSRC identifier */
+
+          else {
+              log occurrence of a collision;
+              create a new entry in the conflicting data or control
+                  source transport address list and mark current time;
+              send an RTCP BYE packet with the old SSRC identifier;
+              choose a new SSRC identifier;
+              create a new entry in the source identifier table with
+                  the old SSRC plus the source transport address from
+                  the data or control packet being processed;
+          }
+      }
+
+   In this algorithm, packets from a newly conflicting source address
+   will be ignored and packets from the original source address will be
+   kept.  If no packets arrive from the original source for an extended
+   period, the table entry will be timed out and the new source will be
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 63]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   able to take over.  This might occur if the original source detects
+   the collision and moves to a new source identifier, but in the usual
+   case an RTCP BYE packet will be received from the original source to
+   delete the state without having to wait for a timeout.
+
+   If the original source address was received through a mixer (i.e.,
+   learned as a CSRC) and later the same source is received directly,
+   the receiver may be well advised to switch to the new source address
+   unless other sources in the mix would be lost.  Furthermore, for
+   applications such as telephony in which some sources such as mobile
+   entities may change addresses during the course of an RTP session,
+   the RTP implementation SHOULD modify the collision detection
+   algorithm to accept packets from the new source transport address.
+   To guard against flip-flopping between addresses if a genuine
+   collision does occur, the algorithm SHOULD include some means to
+   detect this case and avoid switching.
+
+   When a new SSRC identifier is chosen due to a collision, the
+   candidate identifier SHOULD first be looked up in the source
+   identifier table to see if it was already in use by some other
+   source.  If so, another candidate MUST be generated and the process
+   repeated.
+
+   A loop of data packets to a multicast destination can cause severe
+   network flooding.  All mixers and translators MUST implement a loop
+   detection algorithm like the one here so that they can break loops.
+   This should limit the excess traffic to no more than one duplicate
+   copy of the original traffic, which may allow the session to continue
+   so that the cause of the loop can be found and fixed.  However, in
+   extreme cases where a mixer or translator does not properly break the
+   loop and high traffic levels result, it may be necessary for end
+   systems to cease transmitting data or control packets entirely.  This
+   decision may depend upon the application.  An error condition SHOULD
+   be indicated as appropriate.  Transmission MAY be attempted again
+   periodically after a long, random time (on the order of minutes).
+
+8.3 Use with Layered Encodings
+
+   For layered encodings transmitted on separate RTP sessions (see
+   Section 2.4), a single SSRC identifier space SHOULD be used across
+   the sessions of all layers and the core (base) layer SHOULD be used
+   for SSRC identifier allocation and collision resolution.  When a
+   source discovers that it has collided, it transmits an RTCP BYE
+   packet on only the base layer but changes the SSRC identifier to the
+   new value in all layers.
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 64]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+9. Security
+
+   Lower layer protocols may eventually provide all the security
+   services that may be desired for applications of RTP, including
+   authentication, integrity, and confidentiality.  These services have
+   been specified for IP in [27].  Since the initial audio and video
+   applications using RTP needed a confidentiality service before such
+   services were available for the IP layer, the confidentiality service
+   described in the next section was defined for use with RTP and RTCP.
+   That description is included here to codify existing practice.  New
+   applications of RTP MAY implement this RTP-specific confidentiality
+   service for backward compatibility, and/or they MAY implement
+   alternative security services.  The overhead on the RTP protocol for
+   this confidentiality service is low, so the penalty will be minimal
+   if this service is obsoleted by other services in the future.
+
+   Alternatively, other services, other implementations of services and
+   other algorithms may be defined for RTP in the future.  In
+   particular, an RTP profile called Secure Real-time Transport Protocol
+   (SRTP) [28] is being developed to provide confidentiality of the RTP
+   payload while leaving the RTP header in the clear so that link-level
+   header compression algorithms can still operate.  It is expected that
+   SRTP will be the correct choice for many applications.  SRTP is based
+   on the Advanced Encryption Standard (AES) and provides stronger
+   security than the service described here.  No claim is made that the
+   methods presented here are appropriate for a particular security
+   need.  A profile may specify which services and algorithms should be
+   offered by applications, and may provide guidance as to their
+   appropriate use.
+
+   Key distribution and certificates are outside the scope of this
+   document.
+
+9.1 Confidentiality
+
+   Confidentiality means that only the intended receiver(s) can decode
+   the received packets; for others, the packet contains no useful
+   information.  Confidentiality of the content is achieved by
+   encryption.
+
+   When it is desired to encrypt RTP or RTCP according to the method
+   specified in this section, all the octets that will be encapsulated
+   for transmission in a single lower-layer packet are encrypted as a
+   unit.  For RTCP, a 32-bit random number redrawn for each unit MUST be
+   prepended to the unit before encryption.  For RTP, no prefix is
+   prepended; instead, the sequence number and timestamp fields are
+   initialized with random offsets.  This is considered to be a weak
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 65]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   initialization vector (IV) because of poor randomness properties.  In
+   addition, if the subsequent field, the SSRC, can be manipulated by an
+   enemy, there is further weakness of the encryption method.
+
+   For RTCP, an implementation MAY segregate the individual RTCP packets
+   in a compound RTCP packet into two separate compound RTCP packets,
+   one to be encrypted and one to be sent in the clear.  For example,
+   SDES information might be encrypted while reception reports were sent
+   in the clear to accommodate third-party monitors that are not privy
+   to the encryption key.  In this example, depicted in Fig. 4, the SDES
+   information MUST be appended to an RR packet with no reports (and the
+   random number) to satisfy the requirement that all compound RTCP
+   packets begin with an SR or RR packet.  The SDES CNAME item is
+   required in either the encrypted or unencrypted packet, but not both.
+   The same SDES information SHOULD NOT be carried in both packets as
+   this may compromise the encryption.
+
+             UDP packet                     UDP packet
+   -----------------------------  ------------------------------
+   [random][RR][SDES #CNAME ...]  [SR #senderinfo #site1 #site2]
+   -----------------------------  ------------------------------
+             encrypted                     not encrypted
+
+   #: SSRC identifier
+
+       Figure 4: Encrypted and non-encrypted RTCP packets
+
+   The presence of encryption and the use of the correct key are
+   confirmed by the receiver through header or payload validity checks.
+   Examples of such validity checks for RTP and RTCP headers are given
+   in Appendices A.1 and A.2.
+
+   To be consistent with existing implementations of the initial
+   specification of RTP in RFC 1889, the default encryption algorithm is
+   the Data Encryption Standard (DES) algorithm in cipher block chaining
+   (CBC) mode, as described in Section 1.1 of RFC 1423 [29], except that
+   padding to a multiple of 8 octets is indicated as described for the P
+   bit in Section 5.1.  The initialization vector is zero because random
+   values are supplied in the RTP header or by the random prefix for
+   compound RTCP packets.  For details on the use of CBC initialization
+   vectors, see [30].
+
+   Implementations that support the encryption method specified here
+   SHOULD always support the DES algorithm in CBC mode as the default
+   cipher for this method to maximize interoperability.  This method was
+   chosen because it has been demonstrated to be easy and practical to
+   use in experimental audio and video tools in operation on the
+   Internet.  However, DES has since been found to be too easily broken.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 66]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   It is RECOMMENDED that stronger encryption algorithms such as
+   Triple-DES be used in place of the default algorithm.  Furthermore,
+   secure CBC mode requires that the first block of each packet be XORed
+   with a random, independent IV of the same size as the cipher's block
+   size.  For RTCP, this is (partially) achieved by prepending each
+   packet with a 32-bit random number, independently chosen for each
+   packet.  For RTP, the timestamp and sequence number start from random
+   values, but consecutive packets will not be independently randomized.
+   It should be noted that the randomness in both cases (RTP and RTCP)
+   is limited.  High-security applications SHOULD consider other, more
+   conventional, protection means.  Other encryption algorithms MAY be
+   specified dynamically for a session by non-RTP means.  In particular,
+   the SRTP profile [28] based on AES is being developed to take into
+   account known plaintext and CBC plaintext manipulation concerns, and
+   will be the correct choice in the future.
+
+   As an alternative to encryption at the IP level or at the RTP level
+   as described above, profiles MAY define additional payload types for
+   encrypted encodings.  Those encodings MUST specify how padding and
+   other aspects of the encryption are to be handled.  This method
+   allows encrypting only the data while leaving the headers in the
+   clear for applications where that is desired.  It may be particularly
+   useful for hardware devices that will handle both decryption and
+   decoding.  It is also valuable for applications where link-level
+   compression of RTP and lower-layer headers is desired and
+   confidentiality of the payload (but not addresses) is sufficient
+   since encryption of the headers precludes compression.
+
+9.2 Authentication and Message Integrity
+
+   Authentication and message integrity services are not defined at the
+   RTP level since these services would not be directly feasible without
+   a key management infrastructure.  It is expected that authentication
+   and integrity services will be provided by lower layer protocols.
+
+10. Congestion Control
+
+   All transport protocols used on the Internet need to address
+   congestion control in some way [31].  RTP is not an exception, but
+   because the data transported over RTP is often inelastic (generated
+   at a fixed or controlled rate), the means to control congestion in
+   RTP may be quite different from those for other transport protocols
+   such as TCP.  In one sense, inelasticity reduces the risk of
+   congestion because the RTP stream will not expand to consume all
+   available bandwidth as a TCP stream can.  However, inelasticity also
+   means that the RTP stream cannot arbitrarily reduce its load on the
+   network to eliminate congestion when it occurs.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 67]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Since RTP may be used for a wide variety of applications in many
+   different contexts, there is no single congestion control mechanism
+   that will work for all.  Therefore, congestion control SHOULD be
+   defined in each RTP profile as appropriate.  For some profiles, it
+   may be sufficient to include an applicability statement restricting
+   the use of that profile to environments where congestion is avoided
+   by engineering.  For other profiles, specific methods such as data
+   rate adaptation based on RTCP feedback may be required.
+
+11. RTP over Network and Transport Protocols
+
+   This section describes issues specific to carrying RTP packets within
+   particular network and transport protocols.  The following rules
+   apply unless superseded by protocol-specific definitions outside this
+   specification.
+
+   RTP relies on the underlying protocol(s) to provide demultiplexing of
+   RTP data and RTCP control streams.  For UDP and similar protocols,
+   RTP SHOULD use an even destination port number and the corresponding
+   RTCP stream SHOULD use the next higher (odd) destination port number.
+   For applications that take a single port number as a parameter and
+   derive the RTP and RTCP port pair from that number, if an odd number
+   is supplied then the application SHOULD replace that number with the
+   next lower (even) number to use as the base of the port pair.  For
+   applications in which the RTP and RTCP destination port numbers are
+   specified via explicit, separate parameters (using a signaling
+   protocol or other means), the application MAY disregard the
+   restrictions that the port numbers be even/odd and consecutive
+   although the use of an even/odd port pair is still encouraged.  The
+   RTP and RTCP port numbers MUST NOT be the same since RTP relies on
+   the port numbers to demultiplex the RTP data and RTCP control
+   streams.
+
+   In a unicast session, both participants need to identify a port pair
+   for receiving RTP and RTCP packets.  Both participants MAY use the
+   same port pair.  A participant MUST NOT assume that the source port
+   of the incoming RTP or RTCP packet can be used as the destination
+   port for outgoing RTP or RTCP packets.  When RTP data packets are
+   being sent in both directions, each participant's RTCP SR packets
+   MUST be sent to the port that the other participant has specified for
+   reception of RTCP.  The RTCP SR packets combine sender information
+   for the outgoing data plus reception report information for the
+   incoming data.  If a side is not actively sending data (see Section
+   6.4), an RTCP RR packet is sent instead.
+
+   It is RECOMMENDED that layered encoding applications (see Section
+   2.4) use a set of contiguous port numbers.  The port numbers MUST be
+   distinct because of a widespread deficiency in existing operating
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 68]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   systems that prevents use of the same port with multiple multicast
+   addresses, and for unicast, there is only one permissible address.
+   Thus for layer n, the data port is P + 2n, and the control port is P
+   + 2n + 1.  When IP multicast is used, the addresses MUST also be
+   distinct because multicast routing and group membership are managed
+   on an address granularity.  However, allocation of contiguous IP
+   multicast addresses cannot be assumed because some groups may require
+   different scopes and may therefore be allocated from different
+   address ranges.
+
+   The previous paragraph conflicts with the SDP specification, RFC 2327
+   [15], which says that it is illegal for both multiple addresses and
+   multiple ports to be specified in the same session description
+   because the association of addresses with ports could be ambiguous.
+   It is intended that this restriction will be relaxed in a revision of
+   RFC 2327 to allow an equal number of addresses and ports to be
+   specified with a one-to-one mapping implied.
+
+   RTP data packets contain no length field or other delineation,
+   therefore RTP relies on the underlying protocol(s) to provide a
+   length indication.  The maximum length of RTP packets is limited only
+   by the underlying protocols.
+
+   If RTP packets are to be carried in an underlying protocol that
+   provides the abstraction of a continuous octet stream rather than
+   messages (packets), an encapsulation of the RTP packets MUST be
+   defined to provide a framing mechanism.  Framing is also needed if
+   the underlying protocol may contain padding so that the extent of the
+   RTP payload cannot be determined.  The framing mechanism is not
+   defined here.
+
+   A profile MAY specify a framing method to be used even when RTP is
+   carried in protocols that do provide framing in order to allow
+   carrying several RTP packets in one lower-layer protocol data unit,
+   such as a UDP packet.  Carrying several RTP packets in one network or
+   transport packet reduces header overhead and may simplify
+   synchronization between different streams.
+
+12. Summary of Protocol Constants
+
+   This section contains a summary listing of the constants defined in
+   this specification.
+
+   The RTP payload type (PT) constants are defined in profiles rather
+   than this document.  However, the octet of the RTP header which
+   contains the marker bit(s) and payload type MUST avoid the reserved
+   values 200 and 201 (decimal) to distinguish RTP packets from the RTCP
+   SR and RR packet types for the header validation procedure described
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 69]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   in Appendix A.1.  For the standard definition of one marker bit and a
+   7-bit payload type field as shown in this specification, this
+   restriction means that payload types 72 and 73 are reserved.
+
+12.1 RTCP Packet Types
+
+   abbrev.  name                 value
+   SR       sender report          200
+   RR       receiver report        201
+   SDES     source description     202
+   BYE      goodbye                203
+   APP      application-defined    204
+
+   These type values were chosen in the range 200-204 for improved
+   header validity checking of RTCP packets compared to RTP packets or
+   other unrelated packets.  When the RTCP packet type field is compared
+   to the corresponding octet of the RTP header, this range corresponds
+   to the marker bit being 1 (which it usually is not in data packets)
+   and to the high bit of the standard payload type field being 1 (since
+   the static payload types are typically defined in the low half).
+   This range was also chosen to be some distance numerically from 0 and
+   255 since all-zeros and all-ones are common data patterns.
+
+   Since all compound RTCP packets MUST begin with SR or RR, these codes
+   were chosen as an even/odd pair to allow the RTCP validity check to
+   test the maximum number of bits with mask and value.
+
+   Additional RTCP packet types may be registered through IANA (see
+   Section 15).
+
+12.2 SDES Types
+
+   abbrev.  name                            value
+   END      end of SDES list                    0
+   CNAME    canonical name                      1
+   NAME     user name                           2
+   EMAIL    user's electronic mail address      3
+   PHONE    user's phone number                 4
+   LOC      geographic user location            5
+   TOOL     name of application or tool         6
+   NOTE     notice about the source             7
+   PRIV     private extensions                  8
+
+   Additional SDES types may be registered through IANA (see Section
+   15).
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 70]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+13.  RTP Profiles and Payload Format Specifications
+
+   A complete specification of RTP for a particular application will
+   require one or more companion documents of two types described here:
+   profiles, and payload format specifications.
+
+   RTP may be used for a variety of applications with somewhat differing
+   requirements.  The flexibility to adapt to those requirements is
+   provided by allowing multiple choices in the main protocol
+   specification, then selecting the appropriate choices or defining
+   extensions for a particular environment and class of applications in
+   a separate profile document.  Typically an application will operate
+   under only one profile in a particular RTP session, so there is no
+   explicit indication within the RTP protocol itself as to which
+   profile is in use.  A profile for audio and video applications may be
+   found in the companion RFC 3551.  Profiles are typically titled "RTP
+   Profile for ...".
+
+   The second type of companion document is a payload format
+   specification, which defines how a particular kind of payload data,
+   such as H.261 encoded video, should be carried in RTP.  These
+   documents are typically titled "RTP Payload Format for XYZ
+   Audio/Video Encoding".  Payload formats may be useful under multiple
+   profiles and may therefore be defined independently of any particular
+   profile.  The profile documents are then responsible for assigning a
+   default mapping of that format to a payload type value if needed.
+
+   Within this specification, the following items have been identified
+   for possible definition within a profile, but this list is not meant
+   to be exhaustive:
+
+   RTP data header: The octet in the RTP data header that contains
+      the marker bit and payload type field MAY be redefined by a
+      profile to suit different requirements, for example with more or
+      fewer marker bits (Section 5.3, p. 18).
+
+   Payload types: Assuming that a payload type field is included,
+      the profile will usually define a set of payload formats (e.g.,
+      media encodings) and a default static mapping of those formats to
+      payload type values.  Some of the payload formats may be defined
+      by reference to separate payload format specifications.  For each
+      payload type defined, the profile MUST specify the RTP timestamp
+      clock rate to be used (Section 5.1, p. 14).
+
+   RTP data header additions: Additional fields MAY be appended to
+      the fixed RTP data header if some additional functionality is
+      required across the profile's class of applications independent of
+      payload type (Section 5.3, p. 18).
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 71]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   RTP data header extensions: The contents of the first 16 bits of
+      the RTP data header extension structure MUST be defined if use of
+      that mechanism is to be allowed under the profile for
+      implementation-specific extensions (Section 5.3.1, p. 18).
+
+   RTCP packet types: New application-class-specific RTCP packet
+      types MAY be defined and registered with IANA.
+
+   RTCP report interval: A profile SHOULD specify that the values
+      suggested in Section 6.2 for the constants employed in the
+      calculation of the RTCP report interval will be used.  Those are
+      the RTCP fraction of session bandwidth, the minimum report
+      interval, and the bandwidth split between senders and receivers.
+      A profile MAY specify alternate values if they have been
+      demonstrated to work in a scalable manner.
+
+   SR/RR extension: An extension section MAY be defined for the
+      RTCP SR and RR packets if there is additional information that
+      should be reported regularly about the sender or receivers
+      (Section 6.4.3, p. 42 and 43).
+
+   SDES use: The profile MAY specify the relative priorities for
+      RTCP SDES items to be transmitted or excluded entirely (Section
+      6.3.9); an alternate syntax or semantics for the CNAME item
+      (Section 6.5.1); the format of the LOC item (Section 6.5.5); the
+      semantics and use of the NOTE item (Section 6.5.7); or new SDES
+      item types to be registered with IANA.
+
+   Security: A profile MAY specify which security services and
+      algorithms should be offered by applications, and MAY provide
+      guidance as to their appropriate use (Section 9, p. 65).
+
+   String-to-key mapping: A profile MAY specify how a user-provided
+      password or pass phrase is mapped into an encryption key.
+
+   Congestion: A profile SHOULD specify the congestion control
+      behavior appropriate for that profile.
+
+   Underlying protocol: Use of a particular underlying network or
+      transport layer protocol to carry RTP packets MAY be required.
+
+   Transport mapping: A mapping of RTP and RTCP to transport-level
+      addresses, e.g., UDP ports, other than the standard mapping
+      defined in Section 11, p. 68 may be specified.
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 72]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Encapsulation: An encapsulation of RTP packets may be defined to
+      allow multiple RTP data packets to be carried in one lower-layer
+      packet or to provide framing over underlying protocols that do not
+      already do so (Section 11, p. 69).
+
+   It is not expected that a new profile will be required for every
+   application.  Within one application class, it would be better to
+   extend an existing profile rather than make a new one in order to
+   facilitate interoperation among the applications since each will
+   typically run under only one profile.  Simple extensions such as the
+   definition of additional payload type values or RTCP packet types may
+   be accomplished by registering them through IANA and publishing their
+   descriptions in an addendum to the profile or in a payload format
+   specification.
+
+14. Security Considerations
+
+   RTP suffers from the same security liabilities as the underlying
+   protocols.  For example, an impostor can fake source or destination
+   network addresses, or change the header or payload.  Within RTCP, the
+   CNAME and NAME information may be used to impersonate another
+   participant.  In addition, RTP may be sent via IP multicast, which
+   provides no direct means for a sender to know all the receivers of
+   the data sent and therefore no measure of privacy.  Rightly or not,
+   users may be more sensitive to privacy concerns with audio and video
+   communication than they have been with more traditional forms of
+   network communication [33].  Therefore, the use of security
+   mechanisms with RTP is important.  These mechanisms are discussed in
+   Section 9.
+
+   RTP-level translators or mixers may be used to allow RTP traffic to
+   reach hosts behind firewalls.  Appropriate firewall security
+   principles and practices, which are beyond the scope of this
+   document, should be followed in the design and installation of these
+   devices and in the admission of RTP applications for use behind the
+   firewall.
+
+15. IANA Considerations
+
+   Additional RTCP packet types and SDES item types may be registered
+   through the Internet Assigned Numbers Authority (IANA).  Since these
+   number spaces are small, allowing unconstrained registration of new
+   values would not be prudent.  To facilitate review of requests and to
+   promote shared use of new types among multiple applications, requests
+   for registration of new values must be documented in an RFC or other
+   permanent and readily available reference such as the product of
+   another cooperative standards body (e.g., ITU-T).  Other requests may
+   also be accepted, under the advice of a "designated expert."
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 73]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   (Contact the IANA for the contact information of the current expert.)
+
+   RTP profile specifications SHOULD register with IANA a name for the
+   profile in the form "RTP/xxx", where xxx is a short abbreviation of
+   the profile title.  These names are for use by higher-level control
+   protocols, such as the Session Description Protocol (SDP), RFC 2327
+   [15], to refer to transport methods.
+
+16. Intellectual Property Rights Statement
+
+   The IETF takes no position regarding the validity or scope of any
+   intellectual property or other rights that might be claimed to
+   pertain to the implementation or use of the technology described in
+   this document or the extent to which any license under such rights
+   might or might not be available; neither does it represent that it
+   has made any effort to identify any such rights.  Information on the
+   IETF's procedures with respect to rights in standards-track and
+   standards-related documentation can be found in BCP-11.  Copies of
+   claims of rights made available for publication and any assurances of
+   licenses to be made available, or the result of an attempt made to
+   obtain a general license or permission for the use of such
+   proprietary rights by implementors or users of this specification can
+   be obtained from the IETF Secretariat.
+
+   The IETF invites any interested party to bring to its attention any
+   copyrights, patents or patent applications, or other proprietary
+   rights which may cover technology that may be required to practice
+   this standard.  Please address the information to the IETF Executive
+   Director.
+
+17.  Acknowledgments
+
+   This memorandum is based on discussions within the IETF Audio/Video
+   Transport working group chaired by Stephen Casner and Colin Perkins.
+   The current protocol has its origins in the Network Voice Protocol
+   and the Packet Video Protocol (Danny Cohen and Randy Cole) and the
+   protocol implemented by the vat application (Van Jacobson and Steve
+   McCanne).  Christian Huitema provided ideas for the random identifier
+   generator.  Extensive analysis and simulation of the timer
+   reconsideration algorithm was done by Jonathan Rosenberg.  The
+   additions for layered encodings were specified by Michael Speer and
+   Steve McCanne.
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 74]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+Appendix A - Algorithms
+
+   We provide examples of C code for aspects of RTP sender and receiver
+   algorithms.  There may be other implementation methods that are
+   faster in particular operating environments or have other advantages.
+   These implementation notes are for informational purposes only and
+   are meant to clarify the RTP specification.
+
+   The following definitions are used for all examples; for clarity and
+   brevity, the structure definitions are only valid for 32-bit big-
+   endian (most significant octet first) architectures.  Bit fields are
+   assumed to be packed tightly in big-endian bit order, with no
+   additional padding.  Modifications would be required to construct a
+   portable implementation.
+
+   /*
+    * rtp.h  --  RTP header file
+    */
+   #include <sys/types.h>
+
+   /*
+    * The type definitions below are valid for 32-bit architectures and
+    * may have to be adjusted for 16- or 64-bit architectures.
+    */
+   typedef unsigned char  u_int8;
+   typedef unsigned short u_int16;
+   typedef unsigned int   u_int32;
+   typedef          short int16;
+
+   /*
+    * Current protocol version.
+    */
+   #define RTP_VERSION    2
+
+   #define RTP_SEQ_MOD (1<<16)
+   #define RTP_MAX_SDES 255      /* maximum text length for SDES */
+
+   typedef enum {
+       RTCP_SR   = 200,
+       RTCP_RR   = 201,
+       RTCP_SDES = 202,
+       RTCP_BYE  = 203,
+       RTCP_APP  = 204
+   } rtcp_type_t;
+
+   typedef enum {
+       RTCP_SDES_END   = 0,
+       RTCP_SDES_CNAME = 1,
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 75]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+       RTCP_SDES_NAME  = 2,
+       RTCP_SDES_EMAIL = 3,
+       RTCP_SDES_PHONE = 4,
+       RTCP_SDES_LOC   = 5,
+       RTCP_SDES_TOOL  = 6,
+       RTCP_SDES_NOTE  = 7,
+       RTCP_SDES_PRIV  = 8
+   } rtcp_sdes_type_t;
+
+   /*
+    * RTP data header
+    */
+   typedef struct {
+       unsigned int version:2;   /* protocol version */
+       unsigned int p:1;         /* padding flag */
+       unsigned int x:1;         /* header extension flag */
+       unsigned int cc:4;        /* CSRC count */
+       unsigned int m:1;         /* marker bit */
+       unsigned int pt:7;        /* payload type */
+       unsigned int seq:16;      /* sequence number */
+       u_int32 ts;               /* timestamp */
+       u_int32 ssrc;             /* synchronization source */
+       u_int32 csrc[1];          /* optional CSRC list */
+   } rtp_hdr_t;
+
+   /*
+    * RTCP common header word
+    */
+   typedef struct {
+       unsigned int version:2;   /* protocol version */
+       unsigned int p:1;         /* padding flag */
+       unsigned int count:5;     /* varies by packet type */
+       unsigned int pt:8;        /* RTCP packet type */
+       u_int16 length;           /* pkt len in words, w/o this word */
+   } rtcp_common_t;
+
+   /*
+    * Big-endian mask for version, padding bit and packet type pair
+    */
+   #define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
+   #define RTCP_VALID_VALUE ((RTP_VERSION << 14) | RTCP_SR)
+
+   /*
+    * Reception report block
+    */
+   typedef struct {
+       u_int32 ssrc;             /* data source being reported */
+       unsigned int fraction:8;  /* fraction lost since last SR/RR */
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 76]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+       int lost:24;              /* cumul. no. pkts lost (signed!) */
+       u_int32 last_seq;         /* extended last seq. no. received */
+       u_int32 jitter;           /* interarrival jitter */
+       u_int32 lsr;              /* last SR packet from this source */
+       u_int32 dlsr;             /* delay since last SR packet */
+   } rtcp_rr_t;
+
+   /*
+    * SDES item
+    */
+   typedef struct {
+       u_int8 type;              /* type of item (rtcp_sdes_type_t) */
+       u_int8 length;            /* length of item (in octets) */
+       char data[1];             /* text, not null-terminated */
+   } rtcp_sdes_item_t;
+
+   /*
+    * One RTCP packet
+    */
+   typedef struct {
+       rtcp_common_t common;     /* common header */
+       union {
+           /* sender report (SR) */
+           struct {
+               u_int32 ssrc;     /* sender generating this report */
+               u_int32 ntp_sec;  /* NTP timestamp */
+               u_int32 ntp_frac;
+               u_int32 rtp_ts;   /* RTP timestamp */
+               u_int32 psent;    /* packets sent */
+               u_int32 osent;    /* octets sent */
+               rtcp_rr_t rr[1];  /* variable-length list */
+           } sr;
+
+           /* reception report (RR) */
+           struct {
+               u_int32 ssrc;     /* receiver generating this report */
+               rtcp_rr_t rr[1];  /* variable-length list */
+           } rr;
+
+           /* source description (SDES) */
+           struct rtcp_sdes {
+               u_int32 src;      /* first SSRC/CSRC */
+               rtcp_sdes_item_t item[1]; /* list of SDES items */
+           } sdes;
+
+           /* BYE */
+           struct {
+               u_int32 src[1];   /* list of sources */
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 77]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+               /* can't express trailing text for reason */
+           } bye;
+       } r;
+   } rtcp_t;
+
+   typedef struct rtcp_sdes rtcp_sdes_t;
+
+   /*
+    * Per-source state information
+    */
+   typedef struct {
+       u_int16 max_seq;        /* highest seq. number seen */
+       u_int32 cycles;         /* shifted count of seq. number cycles */
+       u_int32 base_seq;       /* base seq number */
+       u_int32 bad_seq;        /* last 'bad' seq number + 1 */
+       u_int32 probation;      /* sequ. packets till source is valid */
+       u_int32 received;       /* packets received */
+       u_int32 expected_prior; /* packet expected at last interval */
+       u_int32 received_prior; /* packet received at last interval */
+       u_int32 transit;        /* relative trans time for prev pkt */
+       u_int32 jitter;         /* estimated jitter */
+       /* ... */
+   } source;
+
+A.1 RTP Data Header Validity Checks
+
+   An RTP receiver should check the validity of the RTP header on
+   incoming packets since they might be encrypted or might be from a
+   different application that happens to be misaddressed.  Similarly, if
+   encryption according to the method described in Section 9 is enabled,
+   the header validity check is needed to verify that incoming packets
+   have been correctly decrypted, although a failure of the header
+   validity check (e.g., unknown payload type) may not necessarily
+   indicate decryption failure.
+
+   Only weak validity checks are possible on an RTP data packet from a
+   source that has not been heard before:
+
+   o  RTP version field must equal 2.
+
+   o  The payload type must be known, and in particular it must not be
+      equal to SR or RR.
+
+   o  If the P bit is set, then the last octet of the packet must
+      contain a valid octet count, in particular, less than the total
+      packet length minus the header size.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 78]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  The X bit must be zero if the profile does not specify that the
+      header extension mechanism may be used.  Otherwise, the extension
+      length field must be less than the total packet size minus the
+      fixed header length and padding.
+
+   o  The length of the packet must be consistent with CC and payload
+      type (if payloads have a known length).
+
+   The last three checks are somewhat complex and not always possible,
+   leaving only the first two which total just a few bits.  If the SSRC
+   identifier in the packet is one that has been received before, then
+   the packet is probably valid and checking if the sequence number is
+   in the expected range provides further validation.  If the SSRC
+   identifier has not been seen before, then data packets carrying that
+   identifier may be considered invalid until a small number of them
+   arrive with consecutive sequence numbers.  Those invalid packets MAY
+   be discarded or they MAY be stored and delivered once validation has
+   been achieved if the resulting delay is acceptable.
+
+   The routine update_seq shown below ensures that a source is declared
+   valid only after MIN_SEQUENTIAL packets have been received in
+   sequence.  It also validates the sequence number seq of a newly
+   received packet and updates the sequence state for the packet's
+   source in the structure to which s points.
+
+   When a new source is heard for the first time, that is, its SSRC
+   identifier is not in the table (see Section 8.2), and the per-source
+   state is allocated for it, s->probation is set to the number of
+   sequential packets required before declaring a source valid
+   (parameter MIN_SEQUENTIAL) and other variables are initialized:
+
+      init_seq(s, seq);
+      s->max_seq = seq - 1;
+      s->probation = MIN_SEQUENTIAL;
+
+   A non-zero s->probation marks the source as not yet valid so the
+   state may be discarded after a short timeout rather than a long one,
+   as discussed in Section 6.2.1.
+
+   After a source is considered valid, the sequence number is considered
+   valid if it is no more than MAX_DROPOUT ahead of s->max_seq nor more
+   than MAX_MISORDER behind.  If the new sequence number is ahead of
+   max_seq modulo the RTP sequence number range (16 bits), but is
+   smaller than max_seq, it has wrapped around and the (shifted) count
+   of sequence number cycles is incremented.  A value of one is returned
+   to indicate a valid sequence number.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 79]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Otherwise, the value zero is returned to indicate that the validation
+   failed, and the bad sequence number plus 1 is stored.  If the next
+   packet received carries the next higher sequence number, it is
+   considered the valid start of a new packet sequence presumably caused
+   by an extended dropout or a source restart.  Since multiple complete
+   sequence number cycles may have been missed, the packet loss
+   statistics are reset.
+
+   Typical values for the parameters are shown, based on a maximum
+   misordering time of 2 seconds at 50 packets/second and a maximum
+   dropout of 1 minute.  The dropout parameter MAX_DROPOUT should be a
+   small fraction of the 16-bit sequence number space to give a
+   reasonable probability that new sequence numbers after a restart will
+   not fall in the acceptable range for sequence numbers from before the
+   restart.
+
+   void init_seq(source *s, u_int16 seq)
+   {
+       s->base_seq = seq;
+       s->max_seq = seq;
+       s->bad_seq = RTP_SEQ_MOD + 1;   /* so seq == bad_seq is false */
+       s->cycles = 0;
+       s->received = 0;
+       s->received_prior = 0;
+       s->expected_prior = 0;
+       /* other initialization */
+   }
+
+   int update_seq(source *s, u_int16 seq)
+   {
+       u_int16 udelta = seq - s->max_seq;
+       const int MAX_DROPOUT = 3000;
+       const int MAX_MISORDER = 100;
+       const int MIN_SEQUENTIAL = 2;
+
+       /*
+        * Source is not valid until MIN_SEQUENTIAL packets with
+        * sequential sequence numbers have been received.
+        */
+       if (s->probation) {
+           /* packet is in sequence */
+           if (seq == s->max_seq + 1) {
+               s->probation--;
+               s->max_seq = seq;
+               if (s->probation == 0) {
+                   init_seq(s, seq);
+                   s->received++;
+                   return 1;
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 80]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+               }
+           } else {
+               s->probation = MIN_SEQUENTIAL - 1;
+               s->max_seq = seq;
+           }
+           return 0;
+       } else if (udelta < MAX_DROPOUT) {
+           /* in order, with permissible gap */
+           if (seq < s->max_seq) {
+               /*
+                * Sequence number wrapped - count another 64K cycle.
+                */
+               s->cycles += RTP_SEQ_MOD;
+           }
+           s->max_seq = seq;
+       } else if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) {
+           /* the sequence number made a very large jump */
+           if (seq == s->bad_seq) {
+               /*
+                * Two sequential packets -- assume that the other side
+                * restarted without telling us so just re-sync
+                * (i.e., pretend this was the first packet).
+                */
+               init_seq(s, seq);
+           }
+           else {
+               s->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
+               return 0;
+           }
+       } else {
+           /* duplicate or reordered packet */
+       }
+       s->received++;
+       return 1;
+   }
+
+   The validity check can be made stronger requiring more than two
+   packets in sequence.  The disadvantages are that a larger number of
+   initial packets will be discarded (or delayed in a queue) and that
+   high packet loss rates could prevent validation.  However, because
+   the RTCP header validation is relatively strong, if an RTCP packet is
+   received from a source before the data packets, the count could be
+   adjusted so that only two packets are required in sequence.  If
+   initial data loss for a few seconds can be tolerated, an application
+   MAY choose to discard all data packets from a source until a valid
+   RTCP packet has been received from that source.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 81]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   Depending on the application and encoding, algorithms may exploit
+   additional knowledge about the payload format for further validation.
+   For payload types where the timestamp increment is the same for all
+   packets, the timestamp values can be predicted from the previous
+   packet received from the same source using the sequence number
+   difference (assuming no change in payload type).
+
+   A strong "fast-path" check is possible since with high probability
+   the first four octets in the header of a newly received RTP data
+   packet will be just the same as that of the previous packet from the
+   same SSRC except that the sequence number will have increased by one.
+   Similarly, a single-entry cache may be used for faster SSRC lookups
+   in applications where data is typically received from one source at a
+   time.
+
+A.2 RTCP Header Validity Checks
+
+   The following checks should be applied to RTCP packets.
+
+   o  RTP version field must equal 2.
+
+   o  The payload type field of the first RTCP packet in a compound
+      packet must be equal to SR or RR.
+
+   o  The padding bit (P) should be zero for the first packet of a
+      compound RTCP packet because padding should only be applied, if it
+      is needed, to the last packet.
+
+   o  The length fields of the individual RTCP packets must add up to
+      the overall length of the compound RTCP packet as received.  This
+      is a fairly strong check.
+
+   The code fragment below performs all of these checks.  The packet
+   type is not checked for subsequent packets since unknown packet types
+   may be present and should be ignored.
+
+      u_int32 len;        /* length of compound RTCP packet in words */
+      rtcp_t *r;          /* RTCP header */
+      rtcp_t *end;        /* end of compound RTCP packet */
+
+      if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
+          /* something wrong with packet format */
+      }
+      end = (rtcp_t *)((u_int32 *)r + len);
+
+      do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
+      while (r < end && r->common.version == 2);
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 82]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      if (r != end) {
+          /* something wrong with packet format */
+      }
+
+A.3 Determining Number of Packets Expected and Lost
+
+   In order to compute packet loss rates, the number of RTP packets
+   expected and actually received from each source needs to be known,
+   using per-source state information defined in struct source
+   referenced via pointer s in the code below.  The number of packets
+   received is simply the count of packets as they arrive, including any
+   late or duplicate packets.  The number of packets expected can be
+   computed by the receiver as the difference between the highest
+   sequence number received (s->max_seq) and the first sequence number
+   received (s->base_seq).  Since the sequence number is only 16 bits
+   and will wrap around, it is necessary to extend the highest sequence
+   number with the (shifted) count of sequence number wraparounds
+   (s->cycles).  Both the received packet count and the count of cycles
+   are maintained the RTP header validity check routine in Appendix A.1.
+
+      extended_max = s->cycles + s->max_seq;
+      expected = extended_max - s->base_seq + 1;
+
+   The number of packets lost is defined to be the number of packets
+   expected less the number of packets actually received:
+
+      lost = expected - s->received;
+
+   Since this signed number is carried in 24 bits, it should be clamped
+   at 0x7fffff for positive loss or 0x800000 for negative loss rather
+   than wrapping around.
+
+   The fraction of packets lost during the last reporting interval
+   (since the previous SR or RR packet was sent) is calculated from
+   differences in the expected and received packet counts across the
+   interval, where expected_prior and received_prior are the values
+   saved when the previous reception report was generated:
+
+      expected_interval = expected - s->expected_prior;
+      s->expected_prior = expected;
+      received_interval = s->received - s->received_prior;
+      s->received_prior = s->received;
+      lost_interval = expected_interval - received_interval;
+      if (expected_interval == 0 || lost_interval <= 0) fraction = 0;
+      else fraction = (lost_interval << 8) / expected_interval;
+
+   The resulting fraction is an 8-bit fixed point number with the binary
+   point at the left edge.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 83]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+A.4 Generating RTCP SDES Packets
+
+   This function builds one SDES chunk into buffer b composed of argc
+   items supplied in arrays type, value and length.  It returns a
+   pointer to the next available location within b.
+
+   char *rtp_write_sdes(char *b, u_int32 src, int argc,
+                        rtcp_sdes_type_t type[], char *value[],
+                        int length[])
+   {
+       rtcp_sdes_t *s = (rtcp_sdes_t *)b;
+       rtcp_sdes_item_t *rsp;
+       int i;
+       int len;
+       int pad;
+
+       /* SSRC header */
+       s->src = src;
+       rsp = &s->item[0];
+
+       /* SDES items */
+       for (i = 0; i < argc; i++) {
+           rsp->type = type[i];
+           len = length[i];
+           if (len > RTP_MAX_SDES) {
+               /* invalid length, may want to take other action */
+               len = RTP_MAX_SDES;
+           }
+           rsp->length = len;
+           memcpy(rsp->data, value[i], len);
+           rsp = (rtcp_sdes_item_t *)&rsp->data[len];
+       }
+
+       /* terminate with end marker and pad to next 4-octet boundary */
+       len = ((char *) rsp) - b;
+       pad = 4 - (len & 0x3);
+       b = (char *) rsp;
+       while (pad--) *b++ = RTCP_SDES_END;
+
+       return b;
+   }
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 84]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+A.5 Parsing RTCP SDES Packets
+
+   This function parses an SDES packet, calling functions find_member()
+   to find a pointer to the information for a session member given the
+   SSRC identifier and member_sdes() to store the new SDES information
+   for that member.  This function expects a pointer to the header of
+   the RTCP packet.
+
+   void rtp_read_sdes(rtcp_t *r)
+   {
+       int count = r->common.count;
+       rtcp_sdes_t *sd = &r->r.sdes;
+       rtcp_sdes_item_t *rsp, *rspn;
+       rtcp_sdes_item_t *end = (rtcp_sdes_item_t *)
+                               ((u_int32 *)r + r->common.length + 1);
+       source *s;
+
+       while (--count >= 0) {
+           rsp = &sd->item[0];
+           if (rsp >= end) break;
+           s = find_member(sd->src);
+
+           for (; rsp->type; rsp = rspn ) {
+               rspn = (rtcp_sdes_item_t *)((char*)rsp+rsp->length+2);
+               if (rspn >= end) {
+                   rsp = rspn;
+                   break;
+               }
+               member_sdes(s, rsp->type, rsp->data, rsp->length);
+           }
+           sd = (rtcp_sdes_t *)
+                ((u_int32 *)sd + (((char *)rsp - (char *)sd) >> 2)+1);
+       }
+       if (count >= 0) {
+           /* invalid packet format */
+       }
+   }
+
+A.6 Generating a Random 32-bit Identifier
+
+   The following subroutine generates a random 32-bit identifier using
+   the MD5 routines published in RFC 1321 [32].  The system routines may
+   not be present on all operating systems, but they should serve as
+   hints as to what kinds of information may be used.  Other system
+   calls that may be appropriate include
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 85]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  getdomainname(),
+
+   o  getwd(), or
+
+   o  getrusage().
+
+   "Live" video or audio samples are also a good source of random
+   numbers, but care must be taken to avoid using a turned-off
+   microphone or blinded camera as a source [17].
+
+   Use of this or a similar routine is recommended to generate the
+   initial seed for the random number generator producing the RTCP
+   period (as shown in Appendix A.7), to generate the initial values for
+   the sequence number and timestamp, and to generate SSRC values.
+   Since this routine is likely to be CPU-intensive, its direct use to
+   generate RTCP periods is inappropriate because predictability is not
+   an issue.  Note that this routine produces the same result on
+   repeated calls until the value of the system clock changes unless
+   different values are supplied for the type argument.
+
+   /*
+    * Generate a random 32-bit quantity.
+    */
+   #include <sys/types.h>   /* u_long */
+   #include <sys/time.h>    /* gettimeofday() */
+   #include <unistd.h>      /* get..() */
+   #include <stdio.h>       /* printf() */
+   #include <time.h>        /* clock() */
+   #include <sys/utsname.h> /* uname() */
+   #include "global.h"      /* from RFC 1321 */
+   #include "md5.h"         /* from RFC 1321 */
+
+   #define MD_CTX MD5_CTX
+   #define MDInit MD5Init
+   #define MDUpdate MD5Update
+   #define MDFinal MD5Final
+
+   static u_long md_32(char *string, int length)
+   {
+       MD_CTX context;
+       union {
+           char   c[16];
+           u_long x[4];
+       } digest;
+       u_long r;
+       int i;
+
+       MDInit (&context);
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 86]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+       MDUpdate (&context, string, length);
+       MDFinal ((unsigned char *)&digest, &context);
+       r = 0;
+       for (i = 0; i < 3; i++) {
+           r ^= digest.x[i];
+       }
+       return r;
+   }                               /* md_32 */
+
+   /*
+    * Return random unsigned 32-bit quantity.  Use 'type' argument if
+    * you need to generate several different values in close succession.
+    */
+   u_int32 random32(int type)
+   {
+       struct {
+           int     type;
+           struct  timeval tv;
+           clock_t cpu;
+           pid_t   pid;
+           u_long  hid;
+           uid_t   uid;
+           gid_t   gid;
+           struct  utsname name;
+       } s;
+
+       gettimeofday(&s.tv, 0);
+       uname(&s.name);
+       s.type = type;
+       s.cpu  = clock();
+       s.pid  = getpid();
+       s.hid  = gethostid();
+       s.uid  = getuid();
+       s.gid  = getgid();
+       /* also: system uptime */
+
+       return md_32((char *)&s, sizeof(s));
+   }                               /* random32 */
+
+A.7 Computing the RTCP Transmission Interval
+
+   The following functions implement the RTCP transmission and reception
+   rules described in Section 6.2.  These rules are coded in several
+   functions:
+
+   o  rtcp_interval() computes the deterministic calculated interval,
+      measured in seconds.  The parameters are defined in Section 6.3.
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 87]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  OnExpire() is called when the RTCP transmission timer expires.
+
+   o  OnReceive() is called whenever an RTCP packet is received.
+
+   Both OnExpire() and OnReceive() have event e as an argument.  This is
+   the next scheduled event for that participant, either an RTCP report
+   or a BYE packet.  It is assumed that the following functions are
+   available:
+
+   o  Schedule(time t, event e) schedules an event e to occur at time t.
+      When time t arrives, the function OnExpire is called with e as an
+      argument.
+
+   o  Reschedule(time t, event e) reschedules a previously scheduled
+      event e for time t.
+
+   o  SendRTCPReport(event e) sends an RTCP report.
+
+   o  SendBYEPacket(event e) sends a BYE packet.
+
+   o  TypeOfEvent(event e) returns EVENT_BYE if the event being
+      processed is for a BYE packet to be sent, else it returns
+      EVENT_REPORT.
+
+   o  PacketType(p) returns PACKET_RTCP_REPORT if packet p is an RTCP
+      report (not BYE), PACKET_BYE if its a BYE RTCP packet, and
+      PACKET_RTP if its a regular RTP data packet.
+
+   o  ReceivedPacketSize() and SentPacketSize() return the size of the
+      referenced packet in octets.
+
+   o  NewMember(p) returns a 1 if the participant who sent packet p is
+      not currently in the member list, 0 otherwise.  Note this function
+      is not sufficient for a complete implementation because each CSRC
+      identifier in an RTP packet and each SSRC in a BYE packet should
+      be processed.
+
+   o  NewSender(p) returns a 1 if the participant who sent packet p is
+      not currently in the sender sublist of the member list, 0
+      otherwise.
+
+   o  AddMember() and RemoveMember() to add and remove participants from
+      the member list.
+
+   o  AddSender() and RemoveSender() to add and remove participants from
+      the sender sublist of the member list.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 88]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   These functions would have to be extended for an implementation that
+   allows the RTCP bandwidth fractions for senders and non-senders to be
+   specified as explicit parameters rather than fixed values of 25% and
+   75%.  The extended implementation of rtcp_interval() would need to
+   avoid division by zero if one of the parameters was zero.
+
+   double rtcp_interval(int members,
+                        int senders,
+                        double rtcp_bw,
+                        int we_sent,
+                        double avg_rtcp_size,
+                        int initial)
+   {
+       /*
+        * Minimum average time between RTCP packets from this site (in
+        * seconds).  This time prevents the reports from `clumping' when
+        * sessions are small and the law of large numbers isn't helping
+        * to smooth out the traffic.  It also keeps the report interval
+        * from becoming ridiculously small during transient outages like
+        * a network partition.
+        */
+       double const RTCP_MIN_TIME = 5.;
+       /*
+        * Fraction of the RTCP bandwidth to be shared among active
+        * senders.  (This fraction was chosen so that in a typical
+        * session with one or two active senders, the computed report
+        * time would be roughly equal to the minimum report time so that
+        * we don't unnecessarily slow down receiver reports.)  The
+        * receiver fraction must be 1 - the sender fraction.
+        */
+       double const RTCP_SENDER_BW_FRACTION = 0.25;
+       double const RTCP_RCVR_BW_FRACTION = (1-RTCP_SENDER_BW_FRACTION);
+       /*
+       /* To compensate for "timer reconsideration" converging to a
+        * value below the intended average.
+        */
+       double const COMPENSATION = 2.71828 - 1.5;
+
+       double t;                   /* interval */
+       double rtcp_min_time = RTCP_MIN_TIME;
+       int n;                      /* no. of members for computation */
+
+       /*
+        * Very first call at application start-up uses half the min
+        * delay for quicker notification while still allowing some time
+        * before reporting for randomization and to learn about other
+        * sources so the report interval will converge to the correct
+        * interval more quickly.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 89]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+        */
+       if (initial) {
+           rtcp_min_time /= 2;
+       }
+       /*
+        * Dedicate a fraction of the RTCP bandwidth to senders unless
+        * the number of senders is large enough that their share is
+        * more than that fraction.
+        */
+       n = members;
+       if (senders <= members * RTCP_SENDER_BW_FRACTION) {
+           if (we_sent) {
+               rtcp_bw *= RTCP_SENDER_BW_FRACTION;
+               n = senders;
+           } else {
+               rtcp_bw *= RTCP_RCVR_BW_FRACTION;
+               n -= senders;
+           }
+       }
+
+       /*
+        * The effective number of sites times the average packet size is
+        * the total number of octets sent when each site sends a report.
+        * Dividing this by the effective bandwidth gives the time
+        * interval over which those packets must be sent in order to
+        * meet the bandwidth target, with a minimum enforced.  In that
+        * time interval we send one report so this time is also our
+        * average time between reports.
+        */
+       t = avg_rtcp_size * n / rtcp_bw;
+       if (t < rtcp_min_time) t = rtcp_min_time;
+
+       /*
+        * To avoid traffic bursts from unintended synchronization with
+        * other sites, we then pick our actual next report interval as a
+        * random number uniformly distributed between 0.5*t and 1.5*t.
+        */
+       t = t * (drand48() + 0.5);
+       t = t / COMPENSATION;
+       return t;
+   }
+
+   void OnExpire(event e,
+                 int    members,
+                 int    senders,
+                 double rtcp_bw,
+                 int    we_sent,
+                 double *avg_rtcp_size,
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 90]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+                 int    *initial,
+                 time_tp   tc,
+                 time_tp   *tp,
+                 int    *pmembers)
+   {
+       /* This function is responsible for deciding whether to send an
+        * RTCP report or BYE packet now, or to reschedule transmission.
+        * It is also responsible for updating the pmembers, initial, tp,
+        * and avg_rtcp_size state variables.  This function should be
+        * called upon expiration of the event timer used by Schedule().
+        */
+
+       double t;     /* Interval */
+       double tn;    /* Next transmit time */
+
+       /* In the case of a BYE, we use "timer reconsideration" to
+        * reschedule the transmission of the BYE if necessary */
+
+       if (TypeOfEvent(e) == EVENT_BYE) {
+           t = rtcp_interval(members,
+                             senders,
+                             rtcp_bw,
+                             we_sent,
+                             *avg_rtcp_size,
+                             *initial);
+           tn = *tp + t;
+           if (tn <= tc) {
+               SendBYEPacket(e);
+               exit(1);
+           } else {
+               Schedule(tn, e);
+           }
+
+       } else if (TypeOfEvent(e) == EVENT_REPORT) {
+           t = rtcp_interval(members,
+                             senders,
+                             rtcp_bw,
+                             we_sent,
+                             *avg_rtcp_size,
+                             *initial);
+           tn = *tp + t;
+           if (tn <= tc) {
+               SendRTCPReport(e);
+               *avg_rtcp_size = (1./16.)*SentPacketSize(e) +
+                   (15./16.)*(*avg_rtcp_size);
+               *tp = tc;
+
+               /* We must redraw the interval.  Don't reuse the
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 91]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+                  one computed above, since its not actually
+                  distributed the same, as we are conditioned
+                  on it being small enough to cause a packet to
+                  be sent */
+
+               t = rtcp_interval(members,
+                                 senders,
+                                 rtcp_bw,
+                                 we_sent,
+                                 *avg_rtcp_size,
+                                 *initial);
+
+               Schedule(t+tc,e);
+               *initial = 0;
+           } else {
+               Schedule(tn, e);
+           }
+           *pmembers = members;
+       }
+   }
+
+   void OnReceive(packet p,
+                  event e,
+                  int *members,
+                  int *pmembers,
+                  int *senders,
+                  double *avg_rtcp_size,
+                  double *tp,
+                  double tc,
+                  double tn)
+   {
+       /* What we do depends on whether we have left the group, and are
+        * waiting to send a BYE (TypeOfEvent(e) == EVENT_BYE) or an RTCP
+        * report.  p represents the packet that was just received.  */
+
+       if (PacketType(p) == PACKET_RTCP_REPORT) {
+           if (NewMember(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
+               AddMember(p);
+               *members += 1;
+           }
+           *avg_rtcp_size = (1./16.)*ReceivedPacketSize(p) +
+               (15./16.)*(*avg_rtcp_size);
+       } else if (PacketType(p) == PACKET_RTP) {
+           if (NewMember(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
+               AddMember(p);
+               *members += 1;
+           }
+           if (NewSender(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 92]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+               AddSender(p);
+               *senders += 1;
+           }
+       } else if (PacketType(p) == PACKET_BYE) {
+           *avg_rtcp_size = (1./16.)*ReceivedPacketSize(p) +
+               (15./16.)*(*avg_rtcp_size);
+
+           if (TypeOfEvent(e) == EVENT_REPORT) {
+               if (NewSender(p) == FALSE) {
+                   RemoveSender(p);
+                   *senders -= 1;
+               }
+
+               if (NewMember(p) == FALSE) {
+                   RemoveMember(p);
+                   *members -= 1;
+               }
+
+               if (*members < *pmembers) {
+                   tn = tc +
+                       (((double) *members)/(*pmembers))*(tn - tc);
+                   *tp = tc -
+                       (((double) *members)/(*pmembers))*(tc - *tp);
+
+                   /* Reschedule the next report for time tn */
+
+                   Reschedule(tn, e);
+                   *pmembers = *members;
+               }
+
+           } else if (TypeOfEvent(e) == EVENT_BYE) {
+               *members += 1;
+           }
+       }
+   }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 93]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+A.8 Estimating the Interarrival Jitter
+
+   The code fragments below implement the algorithm given in Section
+   6.4.1 for calculating an estimate of the statistical variance of the
+   RTP data interarrival time to be inserted in the interarrival jitter
+   field of reception reports.  The inputs are r->ts, the timestamp from
+   the incoming packet, and arrival, the current time in the same units.
+   Here s points to state for the source; s->transit holds the relative
+   transit time for the previous packet, and s->jitter holds the
+   estimated jitter.  The jitter field of the reception report is
+   measured in timestamp units and expressed as an unsigned integer, but
+   the jitter estimate is kept in a floating point.  As each data packet
+   arrives, the jitter estimate is updated:
+
+      int transit = arrival - r->ts;
+      int d = transit - s->transit;
+      s->transit = transit;
+      if (d < 0) d = -d;
+      s->jitter += (1./16.) * ((double)d - s->jitter);
+
+   When a reception report block (to which rr points) is generated for
+   this member, the current jitter estimate is returned:
+
+      rr->jitter = (u_int32) s->jitter;
+
+   Alternatively, the jitter estimate can be kept as an integer, but
+   scaled to reduce round-off error.  The calculation is the same except
+   for the last line:
+
+      s->jitter += d - ((s->jitter + 8) >> 4);
+
+   In this case, the estimate is sampled for the reception report as:
+
+      rr->jitter = s->jitter >> 4;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 94]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+Appendix B - Changes from RFC 1889
+
+   Most of this RFC is identical to RFC 1889.  There are no changes in
+   the packet formats on the wire, only changes to the rules and
+   algorithms governing how the protocol is used.  The biggest change is
+   an enhancement to the scalable timer algorithm for calculating when
+   to send RTCP packets:
+
+   o  The algorithm for calculating the RTCP transmission interval
+      specified in Sections 6.2 and 6.3 and illustrated in Appendix A.7
+      is augmented to include "reconsideration" to minimize transmission
+      in excess of the intended rate when many participants join a
+      session simultaneously, and "reverse reconsideration" to reduce
+      the incidence and duration of false participant timeouts when the
+      number of participants drops rapidly.  Reverse reconsideration is
+      also used to possibly shorten the delay before sending RTCP SR
+      when transitioning from passive receiver to active sender mode.
+
+   o  Section 6.3.7 specifies new rules controlling when an RTCP BYE
+      packet should be sent in order to avoid a flood of packets when
+      many participants leave a session simultaneously.
+
+   o  The requirement to retain state for inactive participants for a
+      period long enough to span typical network partitions was removed
+      from Section 6.2.1.  In a session where many participants join for
+      a brief time and fail to send BYE, this requirement would cause a
+      significant overestimate of the number of participants.  The
+      reconsideration algorithm added in this revision compensates for
+      the large number of new participants joining simultaneously when a
+      partition heals.
+
+   It should be noted that these enhancements only have a significant
+   effect when the number of session participants is large (thousands)
+   and most of the participants join or leave at the same time.  This
+   makes testing in a live network difficult.  However, the algorithm
+   was subjected to a thorough analysis and simulation to verify its
+   performance.  Furthermore, the enhanced algorithm was designed to
+   interoperate with the algorithm in RFC 1889 such that the degree of
+   reduction in excess RTCP bandwidth during a step join is proportional
+   to the fraction of participants that implement the enhanced
+   algorithm.  Interoperation of the two algorithms has been verified
+   experimentally on live networks.
+
+   Other functional changes were:
+
+   o  Section 6.2.1 specifies that implementations may store only a
+      sampling of the participants' SSRC identifiers to allow scaling to
+      very large sessions.  Algorithms are specified in RFC 2762 [21].
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 95]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  In Section 6.2 it is specified that RTCP sender and non-sender
+      bandwidths may be set as separate parameters of the session rather
+      than a strict percentage of the session bandwidth, and may be set
+      to zero.  The requirement that RTCP was mandatory for RTP sessions
+      using IP multicast was relaxed.  However, a clarification was also
+      added that turning off RTCP is NOT RECOMMENDED.
+
+   o  In Sections 6.2, 6.3.1 and Appendix A.7, it is specified that the
+      fraction of participants below which senders get dedicated RTCP
+      bandwidth changes from the fixed 1/4 to a ratio based on the RTCP
+      sender and non-sender bandwidth parameters when those are given.
+      The condition that no bandwidth is dedicated to senders when there
+      are no senders was removed since that is expected to be a
+      transitory state.  It also keeps non-senders from using sender
+      RTCP bandwidth when that is not intended.
+
+   o  Also in Section 6.2 it is specified that the minimum RTCP interval
+      may be scaled to smaller values for high bandwidth sessions, and
+      that the initial RTCP delay may be set to zero for unicast
+      sessions.
+
+   o  Timing out a participant is to be based on inactivity for a number
+      of RTCP report intervals calculated using the receiver RTCP
+      bandwidth fraction even for active senders.
+
+   o  Sections 7.2 and 7.3 specify that translators and mixers should
+      send BYE packets for the sources they are no longer forwarding.
+
+   o  Rule changes for layered encodings are defined in Sections 2.4,
+      6.3.9, 8.3 and 11.  In the last of these, it is noted that the
+      address and port assignment rule conflicts with the SDP
+      specification, RFC 2327 [15], but it is intended that this
+      restriction will be relaxed in a revision of RFC 2327.
+
+   o  The convention for using even/odd port pairs for RTP and RTCP in
+      Section 11 was clarified to refer to destination ports.  The
+      requirement to use an even/odd port pair was removed if the two
+      ports are specified explicitly.  For unicast RTP sessions,
+      distinct port pairs may be used for the two ends (Sections 3, 7.1
+      and 11).
+
+   o  A new Section 10 was added to explain the requirement for
+      congestion control in applications using RTP.
+
+   o  In Section 8.2, the requirement that a new SSRC identifier MUST be
+      chosen whenever the source transport address is changed has been
+      relaxed to say that a new SSRC identifier MAY be chosen.
+      Correspondingly, it was clarified that an implementation MAY
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 96]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      choose to keep packets from the new source address rather than the
+      existing source address when an SSRC collision occurs between two
+      other participants, and SHOULD do so for applications such as
+      telephony in which some sources such as mobile entities may change
+      addresses during the course of an RTP session.
+
+   o  An indentation bug in the RFC 1889 printing of the pseudo-code for
+      the collision detection and resolution algorithm in Section 8.2
+      has been corrected by translating the syntax to pseudo C language,
+      and the algorithm has been modified to remove the restriction that
+      both RTP and RTCP must be sent from the same source port number.
+
+   o  The description of the padding mechanism for RTCP packets was
+      clarified and it is specified that padding MUST only be applied to
+      the last packet of a compound RTCP packet.
+
+   o  In Section A.1, initialization of base_seq was corrected to be seq
+      rather than seq - 1, and the text was corrected to say the bad
+      sequence number plus 1 is stored.  The initialization of max_seq
+      and other variables for the algorithm was separated from the text
+      to make clear that this initialization must be done in addition to
+      calling the init_seq() function (and a few words lost in RFC 1889
+      when processing the document from source to output form were
+      restored).
+
+   o  Clamping of number of packets lost in Section A.3 was corrected to
+      use both positive and negative limits.
+
+   o  The specification of "relative" NTP timestamp in the RTCP SR
+      section now defines these timestamps to be based on the most
+      common system-specific clock, such as system uptime, rather than
+      on session elapsed time which would not be the same for multiple
+      applications started on the same machine at different times.
+
+   Non-functional changes:
+
+   o  It is specified that a receiver MUST ignore packets with payload
+      types it does not understand.
+
+   o  In Fig. 2, the floating point NTP timestamp value was corrected,
+      some missing leading zeros were added in a hex number, and the UTC
+      timezone was specified.
+
+   o  The inconsequence of NTP timestamps wrapping around in the year
+      2036 is explained.
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 97]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   o  The policy for registration of RTCP packet types and SDES types
+      was clarified in a new Section 15, IANA Considerations.  The
+      suggestion that experimenters register the numbers they need and
+      then unregister those which prove to be unneeded has been removed
+      in favor of using APP and PRIV.  Registration of profile names was
+      also specified.
+
+   o  The reference for the UTF-8 character set was changed from an
+      X/Open Preliminary Specification to be RFC 2279.
+
+   o  The reference for RFC 1597 was updated to RFC 1918 and the
+      reference for RFC 2543 was updated to RFC 3261.
+
+   o  The last paragraph of the introduction in RFC 1889, which
+      cautioned implementors to limit deployment in the Internet, was
+      removed because it was deemed no longer relevant.
+
+   o  A non-normative note regarding the use of RTP with Source-Specific
+      Multicast (SSM) was added in Section 6.
+
+   o  The definition of "RTP session" in Section 3 was expanded to
+      acknowledge that a single session may use multiple destination
+      transport addresses (as was always the case for a translator or
+      mixer) and to explain that the distinguishing feature of an RTP
+      session is that each corresponds to a separate SSRC identifier
+      space.  A new definition of "multimedia session" was added to
+      reduce confusion about the word "session".
+
+   o  The meaning of "sampling instant" was explained in more detail as
+      part of the definition of the timestamp field of the RTP header in
+      Section 5.1.
+
+   o  Small clarifications of the text have been made in several places,
+      some in response to questions from readers.  In particular:
+
+      -  In RFC 1889, the first five words of the second sentence of
+         Section 2.2 were lost in processing the document from source to
+         output form, but are now restored.
+
+      -  A definition for "RTP media type" was added in Section 3 to
+         allow the explanation of multiplexing RTP sessions in Section
+         5.2 to be more clear regarding the multiplexing of multiple
+         media.  That section also now explains that multiplexing
+         multiple sources of the same medium based on SSRC identifiers
+         may be appropriate and is the norm for multicast sessions.
+
+      -  The definition for "non-RTP means" was expanded to include
+         examples of other protocols constituting non-RTP means.
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 98]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      -  The description of the session bandwidth parameter is expanded
+         in Section 6.2, including a clarification that the control
+         traffic bandwidth is in addition to the session bandwidth for
+         the data traffic.
+
+      -  The effect of varying packet duration on the jitter calculation
+         was explained in Section 6.4.4.
+
+      -  The method for terminating and padding a sequence of SDES items
+         was clarified in Section 6.5.
+
+      -  IPv6 address examples were added in the description of SDES
+         CNAME in Section 6.5.1, and "example.com" was used in place of
+         other example domain names.
+
+      -  The Security section added a formal reference to IPSEC now that
+         it is available, and says that the confidentiality method
+         defined in this specification is primarily to codify existing
+         practice.  It is RECOMMENDED that stronger encryption
+         algorithms such as Triple-DES be used in place of the default
+         algorithm, and noted that the SRTP profile based on AES will be
+         the correct choice in the future.  A caution about the weakness
+         of the RTP header as an initialization vector was added.  It
+         was also noted that payload-only encryption is necessary to
+         allow for header compression.
+
+      -  The method for partial encryption of RTCP was clarified; in
+         particular, SDES CNAME is carried in only one part when the
+         compound RTCP packet is split.
+
+      -  It is clarified that only one compound RTCP packet should be
+         sent per reporting interval and that if there are too many
+         active sources for the reports to fit in the MTU, then a subset
+         of the sources should be selected round-robin over multiple
+         intervals.
+
+      -  A note was added in Appendix A.1 that packets may be saved
+         during RTP header validation and delivered upon success.
+
+      -  Section 7.3 now explains that a mixer aggregating SDES packets
+         uses more RTCP bandwidth due to longer packets, and a mixer
+         passing through RTCP naturally sends packets at higher than the
+         single source rate, but both behaviors are valid.
+
+      -  Section 13 clarifies that an RTP application may use multiple
+         profiles but typically only one in a given session.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                    [Page 99]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+      -  The terms MUST, SHOULD, MAY, etc. are used as defined in RFC
+         2119.
+
+      -  The bibliography was divided into normative and informative
+         references.
+
+References
+
+Normative References
+
+   [1]  Schulzrinne, H. and S. Casner, "RTP Profile for Audio and Video
+        Conferences with Minimal Control", RFC 3551, July 2003.
+
+   [2]  Bradner, S., "Key Words for Use in RFCs to Indicate Requirement
+        Levels", BCP 14, RFC 2119, March 1997.
+
+   [3]  Postel, J., "Internet Protocol", STD 5, RFC 791, September 1981.
+
+   [4]  Mills, D., "Network Time Protocol (Version 3) Specification,
+        Implementation and Analysis", RFC 1305, March 1992.
+
+   [5]  Yergeau, F., "UTF-8, a Transformation Format of ISO 10646", RFC
+        2279, January 1998.
+
+   [6]  Mockapetris, P., "Domain Names - Concepts and Facilities", STD
+        13, RFC 1034, November 1987.
+
+   [7]  Mockapetris, P., "Domain Names - Implementation and
+        Specification", STD 13, RFC 1035, November 1987.
+
+   [8]  Braden, R., "Requirements for Internet Hosts - Application and
+        Support", STD 3, RFC 1123, October 1989.
+
+   [9]  Resnick, P., "Internet Message Format", RFC 2822, April 2001.
+
+Informative References
+
+   [10] Clark, D. and D. Tennenhouse, "Architectural Considerations for
+        a New Generation of Protocols," in SIGCOMM Symposium on
+        Communications Architectures and Protocols , (Philadelphia,
+        Pennsylvania), pp. 200--208, IEEE Computer Communications
+        Review, Vol. 20(4), September 1990.
+
+   [11] Schulzrinne, H., "Issues in designing a transport protocol for
+        audio and video conferences and other multiparticipant real-time
+        applications." expired Internet Draft, October 1993.
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                   [Page 100]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   [12] Comer, D., Internetworking with TCP/IP , vol. 1.  Englewood
+        Cliffs, New Jersey: Prentice Hall, 1991.
+
+   [13] Rosenberg, J., Schulzrinne, H., Camarillo, G., Johnston, A.,
+        Peterson, J., Sparks, R., Handley, M. and E. Schooler, "SIP:
+        Session Initiation Protocol", RFC 3261, June 2002.
+
+   [14] International Telecommunication Union, "Visual telephone systems
+        and equipment for local area networks which provide a non-
+        guaranteed quality of service", Recommendation H.323,
+        Telecommunication Standardization Sector of ITU, Geneva,
+        Switzerland, July 2003.
+
+   [15] Handley, M. and V. Jacobson, "SDP: Session Description
+        Protocol", RFC 2327, April 1998.
+
+   [16] Schulzrinne, H., Rao, A. and R. Lanphier, "Real Time Streaming
+        Protocol (RTSP)", RFC 2326, April 1998.
+
+   [17] Eastlake 3rd, D., Crocker, S. and J. Schiller, "Randomness
+        Recommendations for Security", RFC 1750, December 1994.
+
+   [18] Bolot, J.-C., Turletti, T. and I. Wakeman, "Scalable Feedback
+        Control for Multicast Video Distribution in the Internet", in
+        SIGCOMM Symposium on Communications Architectures and Protocols,
+        (London, England), pp. 58--67, ACM, August 1994.
+
+   [19] Busse, I., Deffner, B. and H. Schulzrinne, "Dynamic QoS Control
+        of Multimedia Applications Based on RTP", Computer
+        Communications , vol. 19, pp. 49--58, January 1996.
+
+   [20] Floyd, S. and V. Jacobson, "The Synchronization of Periodic
+        Routing Messages", in SIGCOMM Symposium on Communications
+        Architectures and Protocols (D. P. Sidhu, ed.), (San Francisco,
+        California), pp. 33--44, ACM, September 1993.  Also in [34].
+
+   [21] Rosenberg, J. and H. Schulzrinne, "Sampling of the Group
+        Membership in RTP", RFC 2762, February 2000.
+
+   [22] Cadzow, J., Foundations of Digital Signal Processing and Data
+        Analysis New York, New York: Macmillan, 1987.
+
+   [23] Hinden, R. and S. Deering, "Internet Protocol Version 6 (IPv6)
+        Addressing Architecture", RFC 3513, April 2003.
+
+   [24] Rekhter, Y., Moskowitz, B., Karrenberg, D., de Groot, G. and E.
+        Lear, "Address Allocation for Private Internets", RFC 1918,
+        February 1996.
+
+
+
+Schulzrinne, et al.         Standards Track                   [Page 101]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+   [25] Lear, E., Fair, E., Crocker, D. and T. Kessler, "Network 10
+        Considered Harmful (Some Practices Shouldn't be Codified)", RFC
+        1627, July 1994.
+
+   [26] Feller, W., An Introduction to Probability Theory and its
+        Applications, vol. 1.  New York, New York: John Wiley and Sons,
+        third ed., 1968.
+
+   [27] Kent, S. and R. Atkinson, "Security Architecture for the
+        Internet Protocol", RFC 2401, November 1998.
+
+   [28] Baugher, M., Blom, R., Carrara, E., McGrew, D., Naslund, M.,
+        Norrman, K. and D. Oran, "Secure Real-time Transport Protocol",
+        Work in Progress, April 2003.
+
+   [29] Balenson, D., "Privacy Enhancement for Internet Electronic Mail:
+        Part III", RFC 1423, February 1993.
+
+   [30] Voydock, V. and S. Kent, "Security Mechanisms in High-Level
+        Network Protocols", ACM Computing Surveys, vol. 15, pp. 135-171,
+        June 1983.
+
+   [31] Floyd, S., "Congestion Control Principles", BCP 41, RFC 2914,
+        September 2000.
+
+   [32] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April
+        1992.
+
+   [33] Stubblebine, S., "Security Services for Multimedia
+        Conferencing", in 16th National Computer Security Conference,
+        (Baltimore, Maryland), pp. 391--395, September 1993.
+
+   [34] Floyd, S. and V. Jacobson, "The Synchronization of Periodic
+        Routing Messages", IEEE/ACM Transactions on Networking, vol. 2,
+        pp. 122--136, April 1994.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                   [Page 102]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+Authors' Addresses
+
+   Henning Schulzrinne
+   Department of Computer Science
+   Columbia University
+   1214 Amsterdam Avenue
+   New York, NY 10027
+   United States
+
+   EMail: schulzrinne@cs.columbia.edu
+
+
+   Stephen L. Casner
+   Packet Design
+   3400 Hillview Avenue, Building 3
+   Palo Alto, CA 94304
+   United States
+
+   EMail: casner@acm.org
+
+
+   Ron Frederick
+   Blue Coat Systems Inc.
+   650 Almanor Avenue
+   Sunnyvale, CA 94085
+   United States
+
+   EMail: ronf@bluecoat.com
+
+
+   Van Jacobson
+   Packet Design
+   3400 Hillview Avenue, Building 3
+   Palo Alto, CA 94304
+   United States
+
+   EMail: van@packetdesign.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                   [Page 103]
+\f
+RFC 3550                          RTP                          July 2003
+
+
+Full Copyright Statement
+
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+   Funding for the RFC Editor function is currently provided by the
+   Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne, et al.         Standards Track                   [Page 104]
+\f
diff --git a/src/modules/rtp/rfc3551.txt b/src/modules/rtp/rfc3551.txt
new file mode 100644 (file)
index 0000000..c43ff34
--- /dev/null
@@ -0,0 +1,2467 @@
+
+
+
+
+
+
+Network Working Group                                     H. Schulzrinne
+Request for Comments: 3551                           Columbia University
+Obsoletes: 1890                                                S. Casner
+Category: Standards Track                                  Packet Design
+                                                               July 2003
+
+
+              RTP Profile for Audio and Video Conferences
+                          with Minimal Control
+
+Status of this Memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+Abstract
+
+   This document describes a profile called "RTP/AVP" for the use of the
+   real-time transport protocol (RTP), version 2, and the associated
+   control protocol, RTCP, within audio and video multiparticipant
+   conferences with minimal control.  It provides interpretations of
+   generic fields within the RTP specification suitable for audio and
+   video conferences.  In particular, this document defines a set of
+   default mappings from payload type numbers to encodings.
+
+   This document also describes how audio and video data may be carried
+   within RTP.  It defines a set of standard encodings and their names
+   when used within RTP.  The descriptions provide pointers to reference
+   implementations and the detailed standards.  This document is meant
+   as an aid for implementors of audio, video and other real-time
+   multimedia applications.
+
+   This memorandum obsoletes RFC 1890.  It is mostly backwards-
+   compatible except for functions removed because two interoperable
+   implementations were not found.  The additions to RFC 1890 codify
+   existing practice in the use of payload formats under this profile
+   and include new payload formats defined since RFC 1890 was published.
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 1]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+Table of Contents
+
+   1.  Introduction .................................................  3
+       1.1  Terminology .............................................  3
+   2.  RTP and RTCP Packet Forms and Protocol Behavior ..............  4
+   3.  Registering Additional Encodings .............................  6
+   4.  Audio ........................................................  8
+       4.1  Encoding-Independent Rules ..............................  8
+       4.2  Operating Recommendations ...............................  9
+       4.3  Guidelines for Sample-Based Audio Encodings ............. 10
+       4.4  Guidelines for Frame-Based Audio Encodings .............. 11
+       4.5  Audio Encodings ......................................... 12
+            4.5.1   DVI4 ............................................ 13
+            4.5.2   G722 ............................................ 14
+            4.5.3   G723 ............................................ 14
+            4.5.4   G726-40, G726-32, G726-24, and G726-16 .......... 18
+            4.5.5   G728 ............................................ 19
+            4.5.6   G729 ............................................ 20
+            4.5.7   G729D and G729E ................................. 22
+            4.5.8   GSM ............................................. 24
+            4.5.9   GSM-EFR ......................................... 27
+            4.5.10  L8 .............................................. 27
+            4.5.11  L16 ............................................. 27
+            4.5.12  LPC ............................................. 27
+            4.5.13  MPA ............................................. 28
+            4.5.14  PCMA and PCMU ................................... 28
+            4.5.15  QCELP ........................................... 28
+            4.5.16  RED ............................................. 29
+            4.5.17  VDVI ............................................ 29
+   5.  Video ........................................................ 30
+       5.1  CelB .................................................... 30
+       5.2  JPEG .................................................... 30
+       5.3  H261 .................................................... 30
+       5.4  H263 .................................................... 31
+       5.5  H263-1998 ............................................... 31
+       5.6  MPV ..................................................... 31
+       5.7  MP2T .................................................... 31
+       5.8  nv ...................................................... 32
+   6.  Payload Type Definitions ..................................... 32
+   7.  RTP over TCP and Similar Byte Stream Protocols ............... 34
+   8.  Port Assignment .............................................. 34
+   9.  Changes from RFC 1890 ........................................ 35
+   10. Security Considerations ...................................... 38
+   11. IANA Considerations .......................................... 39
+   12. References ................................................... 39
+       12.1 Normative References .................................... 39
+       12.2 Informative References .................................. 39
+   13. Current Locations of Related Resources ....................... 41
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 2]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   14. Acknowledgments .............................................. 42
+   15. Intellectual Property Rights Statement ....................... 43
+   16. Authors' Addresses ........................................... 43
+   17. Full Copyright Statement ..................................... 44
+
+1. Introduction
+
+   This profile defines aspects of RTP left unspecified in the RTP
+   Version 2 protocol definition (RFC 3550) [1].  This profile is
+   intended for the use within audio and video conferences with minimal
+   session control.  In particular, no support for the negotiation of
+   parameters or membership control is provided.  The profile is
+   expected to be useful in sessions where no negotiation or membership
+   control are used (e.g., using the static payload types and the
+   membership indications provided by RTCP), but this profile may also
+   be useful in conjunction with a higher-level control protocol.
+
+   Use of this profile may be implicit in the use of the appropriate
+   applications; there may be no explicit indication by port number,
+   protocol identifier or the like.  Applications such as session
+   directories may use the name for this profile specified in Section
+   11.
+
+   Other profiles may make different choices for the items specified
+   here.
+
+   This document also defines a set of encodings and payload formats for
+   audio and video.  These payload format descriptions are included here
+   only as a matter of convenience since they are too small to warrant
+   separate documents.  Use of these payload formats is NOT REQUIRED to
+   use this profile.  Only the binding of some of the payload formats to
+   static payload type numbers in Tables 4 and 5 is normative.
+
+1.1 Terminology
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in RFC 2119 [2] and
+   indicate requirement levels for implementations compliant with this
+   RTP profile.
+
+   This document defines the term media type as dividing encodings of
+   audio and video content into three classes: audio, video and
+   audio/video (interleaved).
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 3]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+2. RTP and RTCP Packet Forms and Protocol Behavior
+
+   The section "RTP Profiles and Payload Format Specifications" of RFC
+   3550 enumerates a number of items that can be specified or modified
+   in a profile.  This section addresses these items.  Generally, this
+   profile follows the default and/or recommended aspects of the RTP
+   specification.
+
+   RTP data header: The standard format of the fixed RTP data
+      header is used (one marker bit).
+
+   Payload types: Static payload types are defined in Section 6.
+
+   RTP data header additions: No additional fixed fields are
+      appended to the RTP data header.
+
+   RTP data header extensions: No RTP header extensions are
+      defined, but applications operating under this profile MAY use
+      such extensions.  Thus, applications SHOULD NOT assume that the
+      RTP header X bit is always zero and SHOULD be prepared to ignore
+      the header extension.  If a header extension is defined in the
+      future, that definition MUST specify the contents of the first 16
+      bits in such a way that multiple different extensions can be
+      identified.
+
+   RTCP packet types: No additional RTCP packet types are defined
+      by this profile specification.
+
+   RTCP report interval: The suggested constants are to be used for
+      the RTCP report interval calculation.  Sessions operating under
+      this profile MAY specify a separate parameter for the RTCP traffic
+      bandwidth rather than using the default fraction of the session
+      bandwidth.  The RTCP traffic bandwidth MAY be divided into two
+      separate session parameters for those participants which are
+      active data senders and those which are not.  Following the
+      recommendation in the RTP specification [1] that 1/4 of the RTCP
+      bandwidth be dedicated to data senders, the RECOMMENDED default
+      values for these two parameters would be 1.25% and 3.75%,
+      respectively.  For a particular session, the RTCP bandwidth for
+      non-data-senders MAY be set to zero when operating on
+      unidirectional links or for sessions that don't require feedback
+      on the quality of reception.  The RTCP bandwidth for data senders
+      SHOULD be kept non-zero so that sender reports can still be sent
+      for inter-media synchronization and to identify the source by
+      CNAME.  The means by which the one or two session parameters for
+      RTCP bandwidth are specified is beyond the scope of this memo.
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 4]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   SR/RR extension: No extension section is defined for the RTCP SR
+      or RR packet.
+
+   SDES use: Applications MAY use any of the SDES items described
+      in the RTP specification.  While CNAME information MUST be sent
+      every reporting interval, other items SHOULD only be sent every
+      third reporting interval, with NAME sent seven out of eight times
+      within that slot and the remaining SDES items cyclically taking up
+      the eighth slot, as defined in Section 6.2.2 of the RTP
+      specification.  In other words, NAME is sent in RTCP packets 1, 4,
+      7, 10, 13, 16, 19, while, say, EMAIL is used in RTCP packet 22.
+
+   Security: The RTP default security services are also the default
+      under this profile.
+
+   String-to-key mapping: No mapping is specified by this profile.
+
+   Congestion: RTP and this profile may be used in the context of
+      enhanced network service, for example, through Integrated Services
+      (RFC 1633) [4] or Differentiated Services (RFC 2475) [5], or they
+      may be used with best effort service.
+
+      If enhanced service is being used, RTP receivers SHOULD monitor
+      packet loss to ensure that the service that was requested is
+      actually being delivered.  If it is not, then they SHOULD assume
+      that they are receiving best-effort service and behave
+      accordingly.
+
+      If best-effort service is being used, RTP receivers SHOULD monitor
+      packet loss to ensure that the packet loss rate is within
+      acceptable parameters.  Packet loss is considered acceptable if a
+      TCP flow across the same network path and experiencing the same
+      network conditions would achieve an average throughput, measured
+      on a reasonable timescale, that is not less than the RTP flow is
+      achieving.  This condition can be satisfied by implementing
+      congestion control mechanisms to adapt the transmission rate (or
+      the number of layers subscribed for a layered multicast session),
+      or by arranging for a receiver to leave the session if the loss
+      rate is unacceptably high.
+
+      The comparison to TCP cannot be specified exactly, but is intended
+      as an "order-of-magnitude" comparison in timescale and throughput.
+      The timescale on which TCP throughput is measured is the round-
+      trip time of the connection.  In essence, this requirement states
+      that it is not acceptable to deploy an application (using RTP or
+      any other transport protocol) on the best-effort Internet which
+      consumes bandwidth arbitrarily and does not compete fairly with
+      TCP within an order of magnitude.
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 5]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   Underlying protocol: The profile specifies the use of RTP over
+      unicast and multicast UDP as well as TCP.  (This does not preclude
+      the use of these definitions when RTP is carried by other lower-
+      layer protocols.)
+
+   Transport mapping: The standard mapping of RTP and RTCP to
+      transport-level addresses is used.
+
+   Encapsulation: This profile leaves to applications the
+      specification of RTP encapsulation in protocols other than UDP.
+
+3.  Registering Additional Encodings
+
+   This profile lists a set of encodings, each of which is comprised of
+   a particular media data compression or representation plus a payload
+   format for encapsulation within RTP.  Some of those payload formats
+   are specified here, while others are specified in separate RFCs.  It
+   is expected that additional encodings beyond the set listed here will
+   be created in the future and specified in additional payload format
+   RFCs.
+
+   This profile also assigns to each encoding a short name which MAY be
+   used by higher-level control protocols, such as the Session
+   Description Protocol (SDP), RFC 2327 [6], to identify encodings
+   selected for a particular RTP session.
+
+   In some contexts it may be useful to refer to these encodings in the
+   form of a MIME content-type.  To facilitate this, RFC 3555 [7]
+   provides registrations for all of the encodings names listed here as
+   MIME subtype names under the "audio" and "video" MIME types through
+   the MIME registration procedure as specified in RFC 2048 [8].
+
+   Any additional encodings specified for use under this profile (or
+   others) may also be assigned names registered as MIME subtypes with
+   the Internet Assigned Numbers Authority (IANA).  This registry
+   provides a means to insure that the names assigned to the additional
+   encodings are kept unique.  RFC 3555 specifies the information that
+   is required for the registration of RTP encodings.
+
+   In addition to assigning names to encodings, this profile also
+   assigns static RTP payload type numbers to some of them.  However,
+   the payload type number space is relatively small and cannot
+   accommodate assignments for all existing and future encodings.
+   During the early stages of RTP development, it was necessary to use
+   statically assigned payload types because no other mechanism had been
+   specified to bind encodings to payload types.  It was anticipated
+   that non-RTP means beyond the scope of this memo (such as directory
+   services or invitation protocols) would be specified to establish a
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 6]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   dynamic mapping between a payload type and an encoding.  Now,
+   mechanisms for defining dynamic payload type bindings have been
+   specified in the Session Description Protocol (SDP) and in other
+   protocols such as ITU-T Recommendation H.323/H.245.  These mechanisms
+   associate the registered name of the encoding/payload format, along
+   with any additional required parameters, such as the RTP timestamp
+   clock rate and number of channels, with a payload type number.  This
+   association is effective only for the duration of the RTP session in
+   which the dynamic payload type binding is made.  This association
+   applies only to the RTP session for which it is made, thus the
+   numbers can be re-used for different encodings in different sessions
+   so the number space limitation is avoided.
+
+   This profile reserves payload type numbers in the range 96-127
+   exclusively for dynamic assignment.  Applications SHOULD first use
+   values in this range for dynamic payload types.  Those applications
+   which need to define more than 32 dynamic payload types MAY bind
+   codes below 96, in which case it is RECOMMENDED that unassigned
+   payload type numbers be used first.  However, the statically assigned
+   payload types are default bindings and MAY be dynamically bound to
+   new encodings if needed.  Redefining payload types below 96 may cause
+   incorrect operation if an attempt is made to join a session without
+   obtaining session description information that defines the dynamic
+   payload types.
+
+   Dynamic payload types SHOULD NOT be used without a well-defined
+   mechanism to indicate the mapping.  Systems that expect to
+   interoperate with others operating under this profile SHOULD NOT make
+   their own assignments of proprietary encodings to particular, fixed
+   payload types.
+
+   This specification establishes the policy that no additional static
+   payload types will be assigned beyond the ones defined in this
+   document.  Establishing this policy avoids the problem of trying to
+   create a set of criteria for accepting static assignments and
+   encourages the implementation and deployment of the dynamic payload
+   type mechanisms.
+
+   The final set of static payload type assignments is provided in
+   Tables 4 and 5.
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 7]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.  Audio
+
+4.1  Encoding-Independent Rules
+
+   Since the ability to suppress silence is one of the primary
+   motivations for using packets to transmit voice, the RTP header
+   carries both a sequence number and a timestamp to allow a receiver to
+   distinguish between lost packets and periods of time when no data was
+   transmitted.  Discontiguous transmission (silence suppression) MAY be
+   used with any audio payload format.  Receivers MUST assume that
+   senders may suppress silence unless this is restricted by signaling
+   specified elsewhere.  (Even if the transmitter does not suppress
+   silence, the receiver should be prepared to handle periods when no
+   data is present since packets may be lost.)
+
+   Some payload formats (see Sections 4.5.3 and 4.5.6) define a "silence
+   insertion descriptor" or "comfort noise" frame to specify parameters
+   for artificial noise that may be generated during a period of silence
+   to approximate the background noise at the source.  For other payload
+   formats, a generic Comfort Noise (CN) payload format is specified in
+   RFC 3389 [9].  When the CN payload format is used with another
+   payload format, different values in the RTP payload type field
+   distinguish comfort-noise packets from those of the selected payload
+   format.
+
+   For applications which send either no packets or occasional comfort-
+   noise packets during silence, the first packet of a talkspurt, that
+   is, the first packet after a silence period during which packets have
+   not been transmitted contiguously, SHOULD be distinguished by setting
+   the marker bit in the RTP data header to one.  The marker bit in all
+   other packets is zero.  The beginning of a talkspurt MAY be used to
+   adjust the playout delay to reflect changing network delays.
+   Applications without silence suppression MUST set the marker bit to
+   zero.
+
+   The RTP clock rate used for generating the RTP timestamp is
+   independent of the number of channels and the encoding; it usually
+   equals the number of sampling periods per second.  For N-channel
+   encodings, each sampling period (say, 1/8,000 of a second) generates
+   N samples.  (This terminology is standard, but somewhat confusing, as
+   the total number of samples generated per second is then the sampling
+   rate times the channel count.)
+
+   If multiple audio channels are used, channels are numbered left-to-
+   right, starting at one.  In RTP audio packets, information from
+   lower-numbered channels precedes that from higher-numbered channels.
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 8]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   For more than two channels, the convention followed by the AIFF-C
+   audio interchange format SHOULD be followed [3], using the following
+   notation, unless some other convention is specified for a particular
+   encoding or payload format:
+
+      l  left
+      r  right
+      c  center
+      S  surround
+      F  front
+      R  rear
+
+      channels  description  channel
+                                1     2   3   4   5   6
+      _________________________________________________
+      2         stereo          l     r
+      3                         l     r   c
+      4                         l     c   r   S
+      5                        Fl     Fr  Fc  Sl  Sr
+      6                         l     lc  c   r   rc  S
+
+         Note: RFC 1890 defined two conventions for the ordering of four
+         audio channels.  Since the ordering is indicated implicitly by
+         the number of channels, this was ambiguous.  In this revision,
+         the order described as "quadrophonic" has been eliminated to
+         remove the ambiguity.  This choice was based on the observation
+         that quadrophonic consumer audio format did not become popular
+         whereas surround-sound subsequently has.
+
+   Samples for all channels belonging to a single sampling instant MUST
+   be within the same packet.  The interleaving of samples from
+   different channels depends on the encoding.  General guidelines are
+   given in Section 4.3 and 4.4.
+
+   The sampling frequency SHOULD be drawn from the set:  8,000, 11,025,
+   16,000, 22,050, 24,000, 32,000, 44,100 and 48,000 Hz.  (Older Apple
+   Macintosh computers had a native sample rate of 22,254.54 Hz, which
+   can be converted to 22,050 with acceptable quality by dropping 4
+   samples in a 20 ms frame.)  However, most audio encodings are defined
+   for a more restricted set of sampling frequencies.  Receivers SHOULD
+   be prepared to accept multi-channel audio, but MAY choose to only
+   play a single channel.
+
+4.2  Operating Recommendations
+
+   The following recommendations are default operating parameters.
+   Applications SHOULD be prepared to handle other values.  The ranges
+   given are meant to give guidance to application writers, allowing a
+
+
+
+Schulzrinne & Casner        Standards Track                     [Page 9]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   set of applications conforming to these guidelines to interoperate
+   without additional negotiation.  These guidelines are not intended to
+   restrict operating parameters for applications that can negotiate a
+   set of interoperable parameters, e.g., through a conference control
+   protocol.
+
+   For packetized audio, the default packetization interval SHOULD have
+   a duration of 20 ms or one frame, whichever is longer, unless
+   otherwise noted in Table 1 (column "ms/packet").  The packetization
+   interval determines the minimum end-to-end delay; longer packets
+   introduce less header overhead but higher delay and make packet loss
+   more noticeable.  For non-interactive applications such as lectures
+   or for links with severe bandwidth constraints, a higher
+   packetization delay MAY be used.  A receiver SHOULD accept packets
+   representing between 0 and 200 ms of audio data.  (For framed audio
+   encodings, a receiver SHOULD accept packets with a number of frames
+   equal to 200 ms divided by the frame duration, rounded up.)  This
+   restriction allows reasonable buffer sizing for the receiver.
+
+4.3  Guidelines for Sample-Based Audio Encodings
+
+   In sample-based encodings, each audio sample is represented by a
+   fixed number of bits.  Within the compressed audio data, codes for
+   individual samples may span octet boundaries.  An RTP audio packet
+   may contain any number of audio samples, subject to the constraint
+   that the number of bits per sample times the number of samples per
+   packet yields an integral octet count.  Fractional encodings produce
+   less than one octet per sample.
+
+   The duration of an audio packet is determined by the number of
+   samples in the packet.
+
+   For sample-based encodings producing one or more octets per sample,
+   samples from different channels sampled at the same sampling instant
+   SHOULD be packed in consecutive octets.  For example, for a two-
+   channel encoding, the octet sequence is (left channel, first sample),
+   (right channel, first sample), (left channel, second sample), (right
+   channel, second sample), ....  For multi-octet encodings, octets
+   SHOULD be transmitted in network byte order (i.e., most significant
+   octet first).
+
+   The packing of sample-based encodings producing less than one octet
+   per sample is encoding-specific.
+
+   The RTP timestamp reflects the instant at which the first sample in
+   the packet was sampled, that is, the oldest information in the
+   packet.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 10]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.4  Guidelines for Frame-Based Audio Encodings
+
+   Frame-based encodings encode a fixed-length block of audio into
+   another block of compressed data, typically also of fixed length.
+   For frame-based encodings, the sender MAY choose to combine several
+   such frames into a single RTP packet.  The receiver can tell the
+   number of frames contained in an RTP packet, if all the frames have
+   the same length, by dividing the RTP payload length by the audio
+   frame size which is defined as part of the encoding.  This does not
+   work when carrying frames of different sizes unless the frame sizes
+   are relatively prime.  If not, the frames MUST indicate their size.
+
+   For frame-based codecs, the channel order is defined for the whole
+   block.  That is, for two-channel audio, right and left samples SHOULD
+   be coded independently, with the encoded frame for the left channel
+   preceding that for the right channel.
+
+   All frame-oriented audio codecs SHOULD be able to encode and decode
+   several consecutive frames within a single packet.  Since the frame
+   size for the frame-oriented codecs is given, there is no need to use
+   a separate designation for the same encoding, but with different
+   number of frames per packet.
+
+   RTP packets SHALL contain a whole number of frames, with frames
+   inserted according to age within a packet, so that the oldest frame
+   (to be played first) occurs immediately after the RTP packet header.
+   The RTP timestamp reflects the instant at which the first sample in
+   the first frame was sampled, that is, the oldest information in the
+   packet.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 11]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5 Audio Encodings
+
+   name of                              sampling              default
+   encoding  sample/frame  bits/sample      rate  ms/frame  ms/packet
+   __________________________________________________________________
+   DVI4      sample        4                var.                   20
+   G722      sample        8              16,000                   20
+   G723      frame         N/A             8,000        30         30
+   G726-40   sample        5               8,000                   20
+   G726-32   sample        4               8,000                   20
+   G726-24   sample        3               8,000                   20
+   G726-16   sample        2               8,000                   20
+   G728      frame         N/A             8,000       2.5         20
+   G729      frame         N/A             8,000        10         20
+   G729D     frame         N/A             8,000        10         20
+   G729E     frame         N/A             8,000        10         20
+   GSM       frame         N/A             8,000        20         20
+   GSM-EFR   frame         N/A             8,000        20         20
+   L8        sample        8                var.                   20
+   L16       sample        16               var.                   20
+   LPC       frame         N/A             8,000        20         20
+   MPA       frame         N/A              var.      var.
+   PCMA      sample        8                var.                   20
+   PCMU      sample        8                var.                   20
+   QCELP     frame         N/A             8,000        20         20
+   VDVI      sample        var.             var.                   20
+
+   Table 1: Properties of Audio Encodings (N/A: not applicable; var.:
+            variable)
+
+   The characteristics of the audio encodings described in this document
+   are shown in Table 1; they are listed in order of their payload type
+   in Table 4.  While most audio codecs are only specified for a fixed
+   sampling rate, some sample-based algorithms (indicated by an entry of
+   "var." in the sampling rate column of Table 1) may be used with
+   different sampling rates, resulting in different coded bit rates.
+   When used with a sampling rate other than that for which a static
+   payload type is defined, non-RTP means beyond the scope of this memo
+   MUST be used to define a dynamic payload type and MUST indicate the
+   selected RTP timestamp clock rate, which is usually the same as the
+   sampling rate for audio.
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 12]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.1 DVI4
+
+   DVI4 uses an adaptive delta pulse code modulation (ADPCM) encoding
+   scheme that was specified by the Interactive Multimedia Association
+   (IMA) as the "IMA ADPCM wave type".  However, the encoding defined
+   here as DVI4 differs in three respects from the IMA specification:
+
+   o  The RTP DVI4 header contains the predicted value rather than the
+      first sample value contained the IMA ADPCM block header.
+
+   o  IMA ADPCM blocks contain an odd number of samples, since the first
+      sample of a block is contained just in the header (uncompressed),
+      followed by an even number of compressed samples.  DVI4 has an
+      even number of compressed samples only, using the `predict' word
+      from the header to decode the first sample.
+
+   o  For DVI4, the 4-bit samples are packed with the first sample in
+      the four most significant bits and the second sample in the four
+      least significant bits.  In the IMA ADPCM codec, the samples are
+      packed in the opposite order.
+
+   Each packet contains a single DVI block.  This profile only defines
+   the 4-bit-per-sample version, while IMA also specified a 3-bit-per-
+   sample encoding.
+
+   The "header" word for each channel has the following structure:
+
+      int16  predict;  /* predicted value of first sample
+                          from the previous block (L16 format) */
+      u_int8 index;    /* current index into stepsize table */
+      u_int8 reserved; /* set to zero by sender, ignored by receiver */
+
+   Each octet following the header contains two 4-bit samples, thus the
+   number of samples per packet MUST be even because there is no means
+   to indicate a partially filled last octet.
+
+   Packing of samples for multiple channels is for further study.
+
+   The IMA ADPCM algorithm was described in the document IMA Recommended
+   Practices for Enhancing Digital Audio Compatibility in Multimedia
+   Systems (version 3.0).  However, the Interactive Multimedia
+   Association ceased operations in 1997.  Resources for an archived
+   copy of that document and a software implementation of the RTP DVI4
+   encoding are listed in Section 13.
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 13]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.2 G722
+
+   G722 is specified in ITU-T Recommendation G.722, "7 kHz audio-coding
+   within 64 kbit/s".  The G.722 encoder produces a stream of octets,
+   each of which SHALL be octet-aligned in an RTP packet.  The first bit
+   transmitted in the G.722 octet, which is the most significant bit of
+   the higher sub-band sample, SHALL correspond to the most significant
+   bit of the octet in the RTP packet.
+
+   Even though the actual sampling rate for G.722 audio is 16,000 Hz,
+   the RTP clock rate for the G722 payload format is 8,000 Hz because
+   that value was erroneously assigned in RFC 1890 and must remain
+   unchanged for backward compatibility.  The octet rate or sample-pair
+   rate is 8,000 Hz.
+
+4.5.3 G723
+
+   G723 is specified in ITU Recommendation G.723.1, "Dual-rate speech
+   coder for multimedia communications transmitting at 5.3 and 6.3
+   kbit/s".  The G.723.1 5.3/6.3 kbit/s codec was defined by the ITU-T
+   as a mandatory codec for ITU-T H.324 GSTN videophone terminal
+   applications.  The algorithm has a floating point specification in
+   Annex B to G.723.1, a silence compression algorithm in Annex A to
+   G.723.1 and a scalable channel coding scheme for wireless
+   applications in G.723.1 Annex C.
+
+   This Recommendation specifies a coded representation that can be used
+   for compressing the speech signal component of multi-media services
+   at a very low bit rate.  Audio is encoded in 30 ms frames, with an
+   additional delay of 7.5 ms due to look-ahead.  A G.723.1 frame can be
+   one of three sizes:  24 octets (6.3 kb/s frame), 20 octets (5.3 kb/s
+   frame), or 4 octets.  These 4-octet frames are called SID frames
+   (Silence Insertion Descriptor) and are used to specify comfort noise
+   parameters.  There is no restriction on how 4, 20, and 24 octet
+   frames are intermixed.  The least significant two bits of the first
+   octet in the frame determine the frame size and codec type:
+
+         bits  content                      octets/frame
+         00    high-rate speech (6.3 kb/s)            24
+         01    low-rate speech  (5.3 kb/s)            20
+         10    SID frame                               4
+         11    reserved
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 14]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   It is possible to switch between the two rates at any 30 ms frame
+   boundary.  Both (5.3 kb/s and 6.3 kb/s) rates are a mandatory part of
+   the encoder and decoder.  Receivers MUST accept both data rates and
+   MUST accept SID frames unless restriction of these capabilities has
+   been signaled.  The MIME registration for G723 in RFC 3555 [7]
+   specifies parameters that MAY be used with MIME or SDP to restrict to
+   a single data rate or to restrict the use of SID frames.  This coder
+   was optimized to represent speech with near-toll quality at the above
+   rates using a limited amount of complexity.
+
+   The packing of the encoded bit stream into octets and the
+   transmission order of the octets is specified in Rec. G.723.1 and is
+   the same as that produced by the G.723 C code reference
+   implementation.  For the 6.3 kb/s data rate, this packing is
+   illustrated as follows, where the header (HDR) bits are always "0 0"
+   as shown in Fig. 1 to indicate operation at 6.3 kb/s, and the Z bit
+   is always set to zero.  The diagrams show the bit packing in "network
+   byte order", also known as big-endian order.  The bits of each 32-bit
+   word are numbered 0 to 31, with the most significant bit on the left
+   and numbered 0.  The octets (bytes) of each word are transmitted most
+   significant octet first.  The bits of each data field are numbered in
+   the order of the bit stream representation of the encoding (least
+   significant bit first).  The vertical bars indicate the boundaries
+   between field fragments.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 15]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    LPC    |HDR|      LPC      |      LPC      |    ACL0   |LPC|
+   |           |   |               |               |           |   |
+   |0 0 0 0 0 0|0 0|1 1 1 1 0 0 0 0|2 2 1 1 1 1 1 1|0 0 0 0 0 0|2 2|
+   |5 4 3 2 1 0|   |3 2 1 0 9 8 7 6|1 0 9 8 7 6 5 4|5 4 3 2 1 0|3 2|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |  ACL2   |ACL|A| GAIN0 |ACL|ACL|    GAIN0      |    GAIN1      |
+   |         | 1 |C|       | 3 | 2 |               |               |
+   |0 0 0 0 0|0 0|0|0 0 0 0|0 0|0 0|1 1 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
+   |4 3 2 1 0|1 0|6|3 2 1 0|1 0|6 5|1 0 9 8 7 6 5 4|7 6 5 4 3 2 1 0|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | GAIN2 | GAIN1 |     GAIN2     |     GAIN3     | GRID  | GAIN3 |
+   |       |       |               |               |       |       |
+   |0 0 0 0|1 1 0 0|1 1 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0|1 1 0 0|
+   |3 2 1 0|1 0 9 8|1 0 9 8 7 6 5 4|7 6 5 4 3 2 1 0|3 2 1 0|1 0 9 8|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |   MSBPOS    |Z|POS|  MSBPOS   |     POS0      |POS|   POS0    |
+   |             | | 0 |           |               | 1 |           |
+   |0 0 0 0 0 0 0|0|0 0|1 1 1 0 0 0|0 0 0 0 0 0 0 0|0 0|1 1 1 1 1 1|
+   |6 5 4 3 2 1 0| |1 0|2 1 0 9 8 7|9 8 7 6 5 4 3 2|1 0|5 4 3 2 1 0|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     POS1      | POS2  | POS1  |     POS2      | POS3  | POS2  |
+   |               |       |       |               |       |       |
+   |0 0 0 0 0 0 0 0|0 0 0 0|1 1 1 1|1 1 0 0 0 0 0 0|0 0 0 0|1 1 1 1|
+   |9 8 7 6 5 4 3 2|3 2 1 0|3 2 1 0|1 0 9 8 7 6 5 4|3 2 1 0|5 4 3 2|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     POS3      |   PSIG0   |POS|PSIG2|  PSIG1  |  PSIG3  |PSIG2|
+   |               |           | 3 |     |         |         |     |
+   |1 1 0 0 0 0 0 0|0 0 0 0 0 0|1 1|0 0 0|0 0 0 0 0|0 0 0 0 0|0 0 0|
+   |1 0 9 8 7 6 5 4|5 4 3 2 1 0|3 2|2 1 0|4 3 2 1 0|4 3 2 1 0|5 4 3|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                  Figure 1: G.723 (6.3 kb/s) bit packing
+
+   For the 5.3 kb/s data rate, the header (HDR) bits are always "0 1",
+   as shown in Fig. 2, to indicate operation at 5.3 kb/s.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 16]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    LPC    |HDR|      LPC      |      LPC      |   ACL0    |LPC|
+   |           |   |               |               |           |   |
+   |0 0 0 0 0 0|0 1|1 1 1 1 0 0 0 0|2 2 1 1 1 1 1 1|0 0 0 0 0 0|2 2|
+   |5 4 3 2 1 0|   |3 2 1 0 9 8 7 6|1 0 9 8 7 6 5 4|5 4 3 2 1 0|3 2|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |  ACL2   |ACL|A| GAIN0 |ACL|ACL|     GAIN0     |     GAIN1     |
+   |         | 1 |C|       | 3 | 2 |               |               |
+   |0 0 0 0 0|0 0|0|0 0 0 0|0 0|0 0|1 1 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
+   |4 3 2 1 0|1 0|6|3 2 1 0|1 0|6 5|1 0 9 8 7 6 5 4|7 6 5 4 3 2 1 0|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | GAIN2 | GAIN1 |     GAIN2     |    GAIN3      | GRID  | GAIN3 |
+   |       |       |               |               |       |       |
+   |0 0 0 0|1 1 0 0|1 1 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0|1 1 0 0|
+   |3 2 1 0|1 0 9 8|1 0 9 8 7 6 5 4|7 6 5 4 3 2 1 0|4 3 2 1|1 0 9 8|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     POS0      | POS1  | POS0  |     POS1      |     POS2      |
+   |               |       |       |               |               |
+   |0 0 0 0 0 0 0 0|0 0 0 0|1 1 0 0|1 1 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
+   |7 6 5 4 3 2 1 0|3 2 1 0|1 0 9 8|1 0 9 8 7 6 5 4|7 6 5 4 3 2 1 0|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   | POS3  | POS2  |     POS3      | PSIG1 | PSIG0 | PSIG3 | PSIG2 |
+   |       |       |               |       |       |       |       |
+   |0 0 0 0|1 1 0 0|1 1 0 0 0 0 0 0|0 0 0 0|0 0 0 0|0 0 0 0|0 0 0 0|
+   |3 2 1 0|1 0 9 8|1 0 9 8 7 6 5 4|3 2 1 0|3 2 1 0|3 2 1 0|3 2 1 0|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                  Figure 2: G.723 (5.3 kb/s) bit packing
+
+   The packing of G.723.1 SID (silence) frames, which are indicated by
+   the header (HDR) bits having the pattern "1 0", is depicted in Fig.
+   3.
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |    LPC    |HDR|      LPC      |      LPC      |   GAIN    |LPC|
+   |           |   |               |               |           |   |
+   |0 0 0 0 0 0|1 0|1 1 1 1 0 0 0 0|2 2 1 1 1 1 1 1|0 0 0 0 0 0|2 2|
+   |5 4 3 2 1 0|   |3 2 1 0 9 8 7 6|1 0 9 8 7 6 5 4|5 4 3 2 1 0|3 2|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                   Figure 3: G.723 SID mode bit packing
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 17]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.4  G726-40, G726-32, G726-24, and G726-16
+
+   ITU-T Recommendation G.726 describes, among others, the algorithm
+   recommended for conversion of a single 64 kbit/s A-law or mu-law PCM
+   channel encoded at 8,000 samples/sec to and from a 40, 32, 24, or 16
+   kbit/s channel.  The conversion is applied to the PCM stream using an
+   Adaptive Differential Pulse Code Modulation (ADPCM) transcoding
+   technique.  The ADPCM representation consists of a series of
+   codewords with a one-to-one correspondence to the samples in the PCM
+   stream.  The G726 data rates of 40, 32, 24, and 16 kbit/s have
+   codewords of 5, 4, 3, and 2 bits, respectively.
+
+   The 16 and 24 kbit/s encodings do not provide toll quality speech.
+   They are designed for used in overloaded Digital Circuit
+   Multiplication Equipment (DCME).  ITU-T G.726 recommends that the 16
+   and 24 kbit/s encodings should be alternated with higher data rate
+   encodings to provide an average sample size of between 3.5 and 3.7
+   bits per sample.
+
+   The encodings of G.726 are here denoted as G726-40, G726-32, G726-24,
+   and G726-16.  Prior to 1990, G721 described the 32 kbit/s ADPCM
+   encoding, and G723 described the 40, 32, and 16 kbit/s encodings.
+   Thus, G726-32 designates the same algorithm as G721 in RFC 1890.
+
+   A stream of G726 codewords contains no information on the encoding
+   being used, therefore transitions between G726 encoding types are not
+   permitted within a sequence of packed codewords.  Applications MUST
+   determine the encoding type of packed codewords from the RTP payload
+   identifier.
+
+   No payload-specific header information SHALL be included as part of
+   the audio data.  A stream of G726 codewords MUST be packed into
+   octets as follows:  the first codeword is placed into the first octet
+   such that the least significant bit of the codeword aligns with the
+   least significant bit in the octet, the second codeword is then
+   packed so that its least significant bit coincides with the least
+   significant unoccupied bit in the octet.  When a complete codeword
+   cannot be placed into an octet, the bits overlapping the octet
+   boundary are placed into the least significant bits of the next
+   octet.  Packing MUST end with a completely packed final octet.  The
+   number of codewords packed will therefore be a multiple of 8, 2, 8,
+   and 4 for G726-40, G726-32, G726-24, and G726-16, respectively.  An
+   example of the packing scheme for G726-32 codewords is as shown,
+   where bit 7 is the least significant bit of the first octet, and bit
+   A3 is the least significant bit of the first codeword:
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 18]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+          0                   1
+          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+         |B B B B|A A A A|D D D D|C C C C| ...
+         |0 1 2 3|0 1 2 3|0 1 2 3|0 1 2 3|
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+
+   An example of the packing scheme for G726-24 codewords follows, where
+   again bit 7 is the least significant bit of the first octet, and bit
+   A2 is the least significant bit of the first codeword:
+
+          0                   1                   2
+          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+         |C C|B B B|A A A|F|E E E|D D D|C|H H H|G G G|F F| ...
+         |1 2|0 1 2|0 1 2|2|0 1 2|0 1 2|0|0 1 2|0 1 2|0 1|
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+
+   Note that the "little-endian" direction in which samples are packed
+   into octets in the G726-16, -24, -32 and -40 payload formats
+   specified here is consistent with ITU-T Recommendation X.420, but is
+   the opposite of what is specified in ITU-T Recommendation I.366.2
+   Annex E for ATM AAL2 transport.  A second set of RTP payload formats
+   matching the packetization of I.366.2 Annex E and identified by MIME
+   subtypes AAL2-G726-16, -24, -32 and -40 will be specified in a
+   separate document.
+
+4.5.5 G728
+
+   G728 is specified in ITU-T Recommendation G.728, "Coding of speech at
+   16 kbit/s using low-delay code excited linear prediction".
+
+   A G.278 encoder translates 5 consecutive audio samples into a 10-bit
+   codebook index, resulting in a bit rate of 16 kb/s for audio sampled
+   at 8,000 samples per second.  The group of five consecutive samples
+   is called a vector.  Four consecutive vectors, labeled V1 to V4
+   (where V1 is to be played first by the receiver), build one G.728
+   frame.  The four vectors of 40 bits are packed into 5 octets, labeled
+   B1 through B5.  B1 SHALL be placed first in the RTP packet.
+
+   Referring to the figure below, the principle for bit order is
+   "maintenance of bit significance".  Bits from an older vector are
+   more significant than bits from newer vectors.  The MSB of the frame
+   goes to the MSB of B1 and the LSB of the frame goes to LSB of B5.
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 19]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+                   1         2         3        3
+         0         0         0         0        9
+         ++++++++++++++++++++++++++++++++++++++++
+         <---V1---><---V2---><---V3---><---V4---> vectors
+         <--B1--><--B2--><--B3--><--B4--><--B5--> octets
+         <------------- frame 1 ---------------->
+
+   In particular, B1 contains the eight most significant bits of V1,
+   with the MSB of V1 being the MSB of B1.  B2 contains the two least
+   significant bits of V1, the more significant of the two in its MSB,
+   and the six most significant bits of V2.  B1 SHALL be placed first in
+   the RTP packet and B5 last.
+
+4.5.6 G729
+
+   G729 is specified in ITU-T Recommendation G.729, "Coding of speech at
+   8 kbit/s using conjugate structure-algebraic code excited linear
+   prediction (CS-ACELP)".  A reduced-complexity version of the G.729
+   algorithm is specified in Annex A to Rec. G.729.  The speech coding
+   algorithms in the main body of G.729 and in G.729 Annex A are fully
+   interoperable with each other, so there is no need to further
+   distinguish between them.  An implementation that signals or accepts
+   use of G729 payload format may implement either G.729 or G.729A
+   unless restricted by additional signaling specified elsewhere related
+   specifically to the encoding rather than the payload format.  The
+   G.729 and G.729 Annex A codecs were optimized to represent speech
+   with high quality, where G.729 Annex A trades some speech quality for
+   an approximate 50% complexity reduction [10].  See the next Section
+   (4.5.7) for other data rates added in later G.729 Annexes.  For all
+   data rates, the sampling frequency (and RTP timestamp clock rate) is
+   8,000 Hz.
+
+   A voice activity detector (VAD) and comfort noise generator (CNG)
+   algorithm in Annex B of G.729 is RECOMMENDED for digital simultaneous
+   voice and data applications and can be used in conjunction with G.729
+   or G.729 Annex A.  A G.729 or G.729 Annex A frame contains 10 octets,
+   while the G.729 Annex B comfort noise frame occupies 2 octets.
+   Receivers MUST accept comfort noise frames if restriction of their
+   use has not been signaled.  The MIME registration for G729 in RFC
+   3555 [7] specifies a parameter that MAY be used with MIME or SDP to
+   restrict the use of comfort noise frames.
+
+   A G729 RTP packet may consist of zero or more G.729 or G.729 Annex A
+   frames, followed by zero or one G.729 Annex B frames.  The presence
+   of a comfort noise frame can be deduced from the length of the RTP
+   payload.  The default packetization interval is 20 ms (two frames),
+   but in some situations it may be desirable to send 10 ms packets.  An
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 20]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   example would be a transition from speech to comfort noise in the
+   first 10 ms of the packet.  For some applications, a longer
+   packetization interval may be required to reduce the packet rate.
+
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |L|      L1     |    L2   |    L3   |       P1      |P|    C1   |
+      |0|             |         |         |               |0|         |
+      | |0 1 2 3 4 5 6|0 1 2 3 4|0 1 2 3 4|0 1 2 3 4 5 6 7| |0 1 2 3 4|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |       C1      |  S1   | GA1 |  GB1  |    P2   |      C2       |
+      |          1 1 1|       |     |       |         |               |
+      |5 6 7 8 9 0 1 2|0 1 2 3|0 1 2|0 1 2 3|0 1 2 3 4|0 1 2 3 4 5 6 7|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |   C2    |  S2   | GA2 |  GB2  |
+      |    1 1 1|       |     |       |
+      |8 9 0 1 2|0 1 2 3|0 1 2|0 1 2 3|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                    Figure 4: G.729 and G.729A bit packing
+
+   The transmitted parameters of a G.729/G.729A 10-ms frame, consisting
+   of 80 bits, are defined in Recommendation G.729, Table 8/G.729.  The
+   mapping of the these parameters is given below in Fig. 4.  The
+   diagrams show the bit packing in "network byte order", also known as
+   big-endian order.  The bits of each 32-bit word are numbered 0 to 31,
+   with the most significant bit on the left and numbered 0.  The octets
+   (bytes) of each word are transmitted most significant octet first.
+   The bits of each data field are numbered in the order as produced by
+   the G.729 C code reference implementation.
+
+   The packing of the G.729 Annex B comfort noise frame is shown in Fig.
+   5.
+
+          0                   1
+          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |L|  LSF1   |  LSF2 |   GAIN  |R|
+         |S|         |       |         |E|
+         |F|         |       |         |S|
+         |0|0 1 2 3 4|0 1 2 3|0 1 2 3 4|V|    RESV = Reserved (zero)
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                       Figure 5: G.729 Annex B bit packing
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 21]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.7 G729D and G729E
+
+   Annexes D and E to ITU-T Recommendation G.729 provide additional data
+   rates.  Because the data rate is not signaled in the bitstream, the
+   different data rates are given distinct RTP encoding names which are
+   mapped to distinct payload type numbers.  G729D indicates a 6.4
+   kbit/s coding mode (G.729 Annex D, for momentary reduction in channel
+   capacity), while G729E indicates an 11.8 kbit/s mode (G.729 Annex E,
+   for improved performance with a wide range of narrow-band input
+   signals, e.g., music and background noise).  Annex E has two
+   operating modes, backward adaptive and forward adaptive, which are
+   signaled by the first two bits in each frame (the most significant
+   two bits of the first octet).
+
+   The voice activity detector (VAD) and comfort noise generator (CNG)
+   algorithm specified in Annex B of G.729 may be used with Annex D and
+   Annex E frames in addition to G.729 and G.729 Annex A frames.  The
+   algorithm details for the operation of Annexes D and E with the Annex
+   B CNG are specified in G.729 Annexes F and G.  Note that Annexes F
+   and G do not introduce any new encodings.  Receivers MUST accept
+   comfort noise frames if restriction of their use has not been
+   signaled.  The MIME registrations for G729D and G729E in RFC 3555 [7]
+   specify a parameter that MAY be used with MIME or SDP to restrict the
+   use of comfort noise frames.
+
+   For G729D, an RTP packet may consist of zero or more G.729 Annex D
+   frames, followed by zero or one G.729 Annex B frame.  Similarly, for
+   G729E, an RTP packet may consist of zero or more G.729 Annex E
+   frames, followed by zero or one G.729 Annex B frame.  The presence of
+   a comfort noise frame can be deduced from the length of the RTP
+   payload.
+
+   A single RTP packet must contain frames of only one data rate,
+   optionally followed by one comfort noise frame.  The data rate may be
+   changed from packet to packet by changing the payload type number.
+   G.729 Annexes D, E and H describe what the encoding and decoding
+   algorithms must do to accommodate a change in data rate.
+
+   For G729D, the bits of a G.729 Annex D frame are formatted as shown
+   below in Fig. 6 (cf.  Table D.1/G.729).  The frame length is 64 bits.
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 22]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |L|      L1     |    L2   |    L3   |        P1     |     C1    |
+      |0|             |         |         |               |           |
+      | |0 1 2 3 4 5 6|0 1 2 3 4|0 1 2 3 4|0 1 2 3 4 5 6 7|0 1 2 3 4 5|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      | C1  |S1 | GA1 | GB1 |  P2   |        C2       |S2 | GA2 | GB2 |
+      |     |   |     |     |       |                 |   |     |     |
+      |6 7 8|0 1|0 1 2|0 1 2|0 1 2 3|0 1 2 3 4 5 6 7 8|0 1|0 1 2|0 1 2|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                     Figure 6: G.729 Annex D bit packing
+
+   The net bit rate for the G.729 Annex E algorithm is 11.8 kbit/s and a
+   total of 118 bits are used.  Two bits are appended as "don't care"
+   bits to complete an integer number of octets for the frame.  For
+   G729E, the bits of a data frame are formatted as shown in the next
+   two diagrams (cf. Table E.1/G.729).  The fields for the G729E forward
+   adaptive mode are packed as shown in Fig. 7.
+
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |0 0|L|      L1     |    L2   |    L3   |        P1     |P| C0_1|
+      |   |0|             |         |         |               |0|     |
+      |   | |0 1 2 3 4 5 6|0 1 2 3 4|0 1 2 3 4|0 1 2 3 4 5 6 7| |0 1 2|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |       |   C1_1      |     C2_1    |   C3_1      |    C4_1     |
+      |       |             |             |             |             |
+      |3 4 5 6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      | GA1 |  GB1  |    P2   |   C0_2      |     C1_2    |   C2_2    |
+      |     |       |         |             |             |           |
+      |0 1 2|0 1 2 3|0 1 2 3 4|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2 3 4 5|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      | |    C3_2     |     C4_2    | GA2 | GB2   |DC |
+      | |             |             |     |       |   |
+      |6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2|0 1 2 3|0 1|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+         Figure 7: G.729 Annex E (forward adaptive mode) bit packing
+
+   The fields for the G729E backward adaptive mode are packed as shown
+   in Fig. 8.
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 23]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |1 1|       P1      |P|       C0_1              |     C1_1      |
+      |   |               |0|                    1 1 1|               |
+      |   |0 1 2 3 4 5 6 7|0|0 1 2 3 4 5 6 7 8 9 0 1 2|0 1 2 3 4 5 6 7|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |   |  C2_1       | C3_1        | C4_1        |GA1  | GB1   |P2 |
+      |   |             |             |             |     |       |   |
+      |8 9|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2|0 1 2 3|0 1|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     |          C0_2           |       C1_2        |    C2_2   |
+      |     |                    1 1 1|                   |           |
+      |2 3 4|0 1 2 3 4 5 6 7 8 9 0 1 2|0 1 2 3 4 5 6 7 8 9|0 1 2 3 4 5|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      | |    C3_2     |     C4_2    | GA2 | GB2   |DC |
+      | |             |             |     |       |   |
+      |6|0 1 2 3 4 5 6|0 1 2 3 4 5 6|0 1 2|0 1 2 3|0 1|
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+         Figure 8: G.729 Annex E (backward adaptive mode) bit packing
+
+4.5.8 GSM
+
+   GSM (Group Speciale Mobile) denotes the European GSM 06.10 standard
+   for full-rate speech transcoding, ETS 300 961, which is based on
+   RPE/LTP (residual pulse excitation/long term prediction) coding at a
+   rate of 13 kb/s [11,12,13].  The text of the standard can be obtained
+   from:
+
+   ETSI (European Telecommunications Standards Institute)
+   ETSI Secretariat: B.P.152
+   F-06561 Valbonne Cedex
+   France
+   Phone: +33 92 94 42 00
+   Fax:   +33 93 65 47 16
+
+   Blocks of 160 audio samples are compressed into 33 octets, for an
+   effective data rate of 13,200 b/s.
+
+4.5.8.1  General Packaging Issues
+
+   The GSM standard (ETS 300 961) specifies the bit stream produced by
+   the codec, but does not specify how these bits should be packed for
+   transmission.  The packetization specified here has subsequently been
+   adopted in ETSI Technical Specification TS 101 318.  Some software
+   implementations of the GSM codec use a different packing than that
+   specified here.
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 24]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+               field  field name  bits  field  field name  bits
+               ________________________________________________
+               1      LARc[0]     6     39     xmc[22]     3
+               2      LARc[1]     6     40     xmc[23]     3
+               3      LARc[2]     5     41     xmc[24]     3
+               4      LARc[3]     5     42     xmc[25]     3
+               5      LARc[4]     4     43     Nc[2]       7
+               6      LARc[5]     4     44     bc[2]       2
+               7      LARc[6]     3     45     Mc[2]       2
+               8      LARc[7]     3     46     xmaxc[2]    6
+               9      Nc[0]       7     47     xmc[26]     3
+               10     bc[0]       2     48     xmc[27]     3
+               11     Mc[0]       2     49     xmc[28]     3
+               12     xmaxc[0]    6     50     xmc[29]     3
+               13     xmc[0]      3     51     xmc[30]     3
+               14     xmc[1]      3     52     xmc[31]     3
+               15     xmc[2]      3     53     xmc[32]     3
+               16     xmc[3]      3     54     xmc[33]     3
+               17     xmc[4]      3     55     xmc[34]     3
+               18     xmc[5]      3     56     xmc[35]     3
+               19     xmc[6]      3     57     xmc[36]     3
+               20     xmc[7]      3     58     xmc[37]     3
+               21     xmc[8]      3     59     xmc[38]     3
+               22     xmc[9]      3     60     Nc[3]       7
+               23     xmc[10]     3     61     bc[3]       2
+               24     xmc[11]     3     62     Mc[3]       2
+               25     xmc[12]     3     63     xmaxc[3]    6
+               26     Nc[1]       7     64     xmc[39]     3
+               27     bc[1]       2     65     xmc[40]     3
+               28     Mc[1]       2     66     xmc[41]     3
+               29     xmaxc[1]    6     67     xmc[42]     3
+               30     xmc[13]     3     68     xmc[43]     3
+               31     xmc[14]     3     69     xmc[44]     3
+               32     xmc[15]     3     70     xmc[45]     3
+               33     xmc[16]     3     71     xmc[46]     3
+               34     xmc[17]     3     72     xmc[47]     3
+               35     xmc[18]     3     73     xmc[48]     3
+               36     xmc[19]     3     74     xmc[49]     3
+               37     xmc[20]     3     75     xmc[50]     3
+               38     xmc[21]     3     76     xmc[51]     3
+
+                      Table 2: Ordering of GSM variables
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 25]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   Octet  Bit 0   Bit 1   Bit 2   Bit 3   Bit 4   Bit 5   Bit 6   Bit 7
+   _____________________________________________________________________
+       0    1       1       0       1    LARc0.0 LARc0.1 LARc0.2 LARc0.3
+       1 LARc0.4 LARc0.5 LARc1.0 LARc1.1 LARc1.2 LARc1.3 LARc1.4 LARc1.5
+       2 LARc2.0 LARc2.1 LARc2.2 LARc2.3 LARc2.4 LARc3.0 LARc3.1 LARc3.2
+       3 LARc3.3 LARc3.4 LARc4.0 LARc4.1 LARc4.2 LARc4.3 LARc5.0 LARc5.1
+       4 LARc5.2 LARc5.3 LARc6.0 LARc6.1 LARc6.2 LARc7.0 LARc7.1 LARc7.2
+       5  Nc0.0   Nc0.1   Nc0.2   Nc0.3   Nc0.4   Nc0.5   Nc0.6  bc0.0
+       6  bc0.1   Mc0.0   Mc0.1  xmaxc00 xmaxc01 xmaxc02 xmaxc03 xmaxc04
+       7 xmaxc05 xmc0.0  xmc0.1  xmc0.2  xmc1.0  xmc1.1  xmc1.2  xmc2.0
+       8 xmc2.1  xmc2.2  xmc3.0  xmc3.1  xmc3.2  xmc4.0  xmc4.1  xmc4.2
+       9 xmc5.0  xmc5.1  xmc5.2  xmc6.0  xmc6.1  xmc6.2  xmc7.0  xmc7.1
+      10 xmc7.2  xmc8.0  xmc8.1  xmc8.2  xmc9.0  xmc9.1  xmc9.2  xmc10.0
+      11 xmc10.1 xmc10.2 xmc11.0 xmc11.1 xmc11.2 xmc12.0 xmc12.1 xcm12.2
+      12  Nc1.0   Nc1.1   Nc1.2   Nc1.3   Nc1.4   Nc1.5   Nc1.6   bc1.0
+      13  bc1.1   Mc1.0   Mc1.1  xmaxc10 xmaxc11 xmaxc12 xmaxc13 xmaxc14
+      14 xmax15  xmc13.0 xmc13.1 xmc13.2 xmc14.0 xmc14.1 xmc14.2 xmc15.0
+      15 xmc15.1 xmc15.2 xmc16.0 xmc16.1 xmc16.2 xmc17.0 xmc17.1 xmc17.2
+      16 xmc18.0 xmc18.1 xmc18.2 xmc19.0 xmc19.1 xmc19.2 xmc20.0 xmc20.1
+      17 xmc20.2 xmc21.0 xmc21.1 xmc21.2 xmc22.0 xmc22.1 xmc22.2 xmc23.0
+      18 xmc23.1 xmc23.2 xmc24.0 xmc24.1 xmc24.2 xmc25.0 xmc25.1 xmc25.2
+      19  Nc2.0   Nc2.1   Nc2.2   Nc2.3   Nc2.4   Nc2.5   Nc2.6   bc2.0
+      20  bc2.1   Mc2.0   Mc2.1  xmaxc20 xmaxc21 xmaxc22 xmaxc23 xmaxc24
+      21 xmaxc25 xmc26.0 xmc26.1 xmc26.2 xmc27.0 xmc27.1 xmc27.2 xmc28.0
+      22 xmc28.1 xmc28.2 xmc29.0 xmc29.1 xmc29.2 xmc30.0 xmc30.1 xmc30.2
+      23 xmc31.0 xmc31.1 xmc31.2 xmc32.0 xmc32.1 xmc32.2 xmc33.0 xmc33.1
+      24 xmc33.2 xmc34.0 xmc34.1 xmc34.2 xmc35.0 xmc35.1 xmc35.2 xmc36.0
+      25 Xmc36.1 xmc36.2 xmc37.0 xmc37.1 xmc37.2 xmc38.0 xmc38.1 xmc38.2
+      26  Nc3.0   Nc3.1   Nc3.2   Nc3.3   Nc3.4   Nc3.5   Nc3.6   bc3.0
+      27  bc3.1   Mc3.0   Mc3.1  xmaxc30 xmaxc31 xmaxc32 xmaxc33 xmaxc34
+      28 xmaxc35 xmc39.0 xmc39.1 xmc39.2 xmc40.0 xmc40.1 xmc40.2 xmc41.0
+      29 xmc41.1 xmc41.2 xmc42.0 xmc42.1 xmc42.2 xmc43.0 xmc43.1 xmc43.2
+      30 xmc44.0 xmc44.1 xmc44.2 xmc45.0 xmc45.1 xmc45.2 xmc46.0 xmc46.1
+      31 xmc46.2 xmc47.0 xmc47.1 xmc47.2 xmc48.0 xmc48.1 xmc48.2 xmc49.0
+      32 xmc49.1 xmc49.2 xmc50.0 xmc50.1 xmc50.2 xmc51.0 xmc51.1 xmc51.2
+
+                        Table 3: GSM payload format
+
+   In the GSM packing used by RTP, the bits SHALL be packed beginning
+   from the most significant bit.  Every 160 sample GSM frame is coded
+   into one 33 octet (264 bit) buffer.  Every such buffer begins with a
+   4 bit signature (0xD), followed by the MSB encoding of the fields of
+   the frame.  The first octet thus contains 1101 in the 4 most
+   significant bits (0-3) and the 4 most significant bits of F1 (0-3) in
+   the 4 least significant bits (4-7).  The second octet contains the 2
+   least significant bits of F1 in bits 0-1, and F2 in bits 2-7, and so
+   on.  The order of the fields in the frame is described in Table 2.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 26]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.8.2   GSM Variable Names and Numbers
+
+   In the RTP encoding we have the bit pattern described in Table 3,
+   where F.i signifies the ith bit of the field F, bit 0 is the most
+   significant bit, and the bits of every octet are numbered from 0 to 7
+   from most to least significant.
+
+4.5.9 GSM-EFR
+
+   GSM-EFR denotes GSM 06.60 enhanced full rate speech transcoding,
+   specified in ETS 300 726 which is available from ETSI at the address
+   given in Section 4.5.8.  This codec has a frame length of 244 bits.
+   For transmission in RTP, each codec frame is packed into a 31 octet
+   (248 bit) buffer beginning with a 4-bit signature 0xC in a manner
+   similar to that specified here for the original GSM 06.10 codec.  The
+   packing is specified in ETSI Technical Specification TS 101 318.
+
+4.5.10 L8
+
+   L8 denotes linear audio data samples, using 8-bits of precision with
+   an offset of 128, that is, the most negative signal is encoded as
+   zero.
+
+4.5.11 L16
+
+   L16 denotes uncompressed audio data samples, using 16-bit signed
+   representation with 65,535 equally divided steps between minimum and
+   maximum signal level, ranging from -32,768 to 32,767.  The value is
+   represented in two's complement notation and transmitted in network
+   byte order (most significant byte first).
+
+   The MIME registration for L16 in RFC 3555 [7] specifies parameters
+   that MAY be used with MIME or SDP to indicate that analog pre-
+   emphasis was applied to the signal before quantization or to indicate
+   that a multiple-channel audio stream follows a different channel
+   ordering convention than is specified in Section 4.1.
+
+4.5.12 LPC
+
+   LPC designates an experimental linear predictive encoding contributed
+   by Ron Frederick, which is based on an implementation written by Ron
+   Zuckerman posted to the Usenet group comp.dsp on June 26, 1992.  The
+   codec generates 14 octets for every frame.  The framesize is set to
+   20 ms, resulting in a bit rate of 5,600 b/s.
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 27]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.13 MPA
+
+   MPA denotes MPEG-1 or MPEG-2 audio encapsulated as elementary
+   streams.  The encoding is defined in ISO standards ISO/IEC 11172-3
+   and 13818-3.  The encapsulation is specified in RFC 2250 [14].
+
+   The encoding may be at any of three levels of complexity, called
+   Layer I, II and III.  The selected layer as well as the sampling rate
+   and channel count are indicated in the payload.  The RTP timestamp
+   clock rate is always 90,000, independent of the sampling rate.
+   MPEG-1 audio supports sampling rates of 32, 44.1, and 48 kHz (ISO/IEC
+   11172-3, section 1.1; "Scope").  MPEG-2 supports sampling rates of
+   16, 22.05 and 24 kHz.  The number of samples per frame is fixed, but
+   the frame size will vary with the sampling rate and bit rate.
+
+   The MIME registration for MPA in RFC 3555 [7] specifies parameters
+   that MAY be used with MIME or SDP to restrict the selection of layer,
+   channel count, sampling rate, and bit rate.
+
+4.5.14 PCMA and PCMU
+
+   PCMA and PCMU are specified in ITU-T Recommendation G.711.  Audio
+   data is encoded as eight bits per sample, after logarithmic scaling.
+   PCMU denotes mu-law scaling, PCMA A-law scaling.  A detailed
+   description is given by Jayant and Noll [15].  Each G.711 octet SHALL
+   be octet-aligned in an RTP packet.  The sign bit of each G.711 octet
+   SHALL correspond to the most significant bit of the octet in the RTP
+   packet (i.e., assuming the G.711 samples are handled as octets on the
+   host machine, the sign bit SHALL be the most significant bit of the
+   octet as defined by the host machine format).  The 56 kb/s and 48
+   kb/s modes of G.711 are not applicable to RTP, since PCMA and PCMU
+   MUST always be transmitted as 8-bit samples.
+
+   See Section 4.1 regarding silence suppression.
+
+4.5.15 QCELP
+
+   The Electronic Industries Association (EIA) & Telecommunications
+   Industry Association (TIA) standard IS-733, "TR45: High Rate Speech
+   Service Option for Wideband Spread Spectrum Communications Systems",
+   defines the QCELP audio compression algorithm for use in wireless
+   CDMA applications.  The QCELP CODEC compresses each 20 milliseconds
+   of 8,000 Hz, 16-bit sampled input speech into one of four different
+   size output frames:  Rate 1 (266 bits), Rate 1/2 (124 bits), Rate 1/4
+   (54 bits) or Rate 1/8 (20 bits).  For typical speech patterns, this
+   results in an average output of 6.8 kb/s for normal mode and 4.7 kb/s
+   for reduced rate mode.  The packetization of the QCELP audio codec is
+   described in [16].
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 28]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+4.5.16 RED
+
+   The redundant audio payload format "RED" is specified by RFC 2198
+   [17].  It defines a means by which multiple redundant copies of an
+   audio packet may be transmitted in a single RTP stream.  Each packet
+   in such a stream contains, in addition to the audio data for that
+   packetization interval, a (more heavily compressed) copy of the data
+   from a previous packetization interval.  This allows an approximation
+   of the data from lost packets to be recovered upon decoding of a
+   subsequent packet, giving much improved sound quality when compared
+   with silence substitution for lost packets.
+
+4.5.17 VDVI
+
+   VDVI is a variable-rate version of DVI4, yielding speech bit rates of
+   between 10 and 25 kb/s.  It is specified for single-channel operation
+   only.  Samples are packed into octets starting at the most-
+   significant bit.  The last octet is padded with 1 bits if the last
+   sample does not fill the last octet.  This padding is distinct from
+   the valid codewords.  The receiver needs to detect the padding
+   because there is no explicit count of samples in the packet.
+
+   It uses the following encoding:
+
+            DVI4 codeword  VDVI bit pattern
+            _______________________________
+                        0  00
+                        1  010
+                        2  1100
+                        3  11100
+                        4  111100
+                        5  1111100
+                        6  11111100
+                        7  11111110
+                        8  10
+                        9  011
+                       10  1101
+                       11  11101
+                       12  111101
+                       13  1111101
+                       14  11111101
+                       15  11111111
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 29]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+5.  Video
+
+   The following sections describe the video encodings that are defined
+   in this memo and give their abbreviated names used for
+   identification.  These video encodings and their payload types are
+   listed in Table 5.
+
+   All of these video encodings use an RTP timestamp frequency of 90,000
+   Hz, the same as the MPEG presentation time stamp frequency.  This
+   frequency yields exact integer timestamp increments for the typical
+   24 (HDTV), 25 (PAL), and 29.97 (NTSC) and 30 Hz (HDTV) frame rates
+   and 50, 59.94 and 60 Hz field rates.  While 90 kHz is the RECOMMENDED
+   rate for future video encodings used within this profile, other rates
+   MAY be used.  However, it is not sufficient to use the video frame
+   rate (typically between 15 and 30 Hz) because that does not provide
+   adequate resolution for typical synchronization requirements when
+   calculating the RTP timestamp corresponding to the NTP timestamp in
+   an RTCP SR packet.  The timestamp resolution MUST also be sufficient
+   for the jitter estimate contained in the receiver reports.
+
+   For most of these video encodings, the RTP timestamp encodes the
+   sampling instant of the video image contained in the RTP data packet.
+   If a video image occupies more than one packet, the timestamp is the
+   same on all of those packets.  Packets from different video images
+   are distinguished by their different timestamps.
+
+   Most of these video encodings also specify that the marker bit of the
+   RTP header SHOULD be set to one in the last packet of a video frame
+   and otherwise set to zero.  Thus, it is not necessary to wait for a
+   following packet with a different timestamp to detect that a new
+   frame should be displayed.
+
+5.1  CelB
+
+   The CELL-B encoding is a proprietary encoding proposed by Sun
+   Microsystems.  The byte stream format is described in RFC 2029 [18].
+
+5.2 JPEG
+
+   The encoding is specified in ISO Standards 10918-1 and 10918-2.  The
+   RTP payload format is as specified in RFC 2435 [19].
+
+5.3 H261
+
+   The encoding is specified in ITU-T Recommendation H.261, "Video codec
+   for audiovisual services at p x 64 kbit/s".  The packetization and
+   RTP-specific properties are described in RFC 2032 [20].
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 30]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+5.4 H263
+
+   The encoding is specified in the 1996 version of ITU-T Recommendation
+   H.263, "Video coding for low bit rate communication".  The
+   packetization and RTP-specific properties are described in RFC 2190
+   [21].  The H263-1998 payload format is RECOMMENDED over this one for
+   use by new implementations.
+
+5.5 H263-1998
+
+   The encoding is specified in the 1998 version of ITU-T Recommendation
+   H.263, "Video coding for low bit rate communication".  The
+   packetization and RTP-specific properties are described in RFC 2429
+   [22].  Because the 1998 version of H.263 is a superset of the 1996
+   syntax, this payload format can also be used with the 1996 version of
+   H.263, and is RECOMMENDED for this use by new implementations.  This
+   payload format does not replace RFC 2190, which continues to be used
+   by existing implementations, and may be required for backward
+   compatibility in new implementations.  Implementations using the new
+   features of the 1998 version of H.263 MUST use the payload format
+   described in RFC 2429.
+
+5.6 MPV
+
+   MPV designates the use of MPEG-1 and MPEG-2 video encoding elementary
+   streams as specified in ISO Standards ISO/IEC 11172 and 13818-2,
+   respectively.  The RTP payload format is as specified in RFC 2250
+   [14], Section 3.
+
+   The MIME registration for MPV in RFC 3555 [7] specifies a parameter
+   that MAY be used with MIME or SDP to restrict the selection of the
+   type of MPEG video.
+
+5.7 MP2T
+
+   MP2T designates the use of MPEG-2 transport streams, for either audio
+   or video.  The RTP payload format is described in RFC 2250 [14],
+   Section 2.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 31]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+5.8 nv
+
+   The encoding is implemented in the program `nv', version 4, developed
+   at Xerox PARC by Ron Frederick.  Further information is available
+   from the author:
+
+   Ron Frederick
+   Blue Coat Systems Inc.
+   650 Almanor Avenue
+   Sunnyvale, CA 94085
+   United States
+   EMail: ronf@bluecoat.com
+
+6.  Payload Type Definitions
+
+   Tables 4 and 5 define this profile's static payload type values for
+   the PT field of the RTP data header.  In addition, payload type
+   values in the range 96-127 MAY be defined dynamically through a
+   conference control protocol, which is beyond the scope of this
+   document.  For example, a session directory could specify that for a
+   given session, payload type 96 indicates PCMU encoding, 8,000 Hz
+   sampling rate, 2 channels.  Entries in Tables 4 and 5 with payload
+   type "dyn" have no static payload type assigned and are only used
+   with a dynamic payload type.  Payload type 2 was assigned to G721 in
+   RFC 1890 and to its equivalent successor G726-32 in draft versions of
+   this specification, but its use is now deprecated and that static
+   payload type is marked reserved due to conflicting use for the
+   payload formats G726-32 and AAL2-G726-32 (see Section 4.5.4).
+   Payload type 13 indicates the Comfort Noise (CN) payload format
+   specified in RFC 3389 [9].  Payload type 19 is marked "reserved"
+   because some draft versions of this specification assigned that
+   number to an earlier version of the comfort noise payload format.
+   The payload type range 72-76 is marked "reserved" so that RTCP and
+   RTP packets can be reliably distinguished (see Section "Summary of
+   Protocol Constants" of the RTP protocol specification).
+
+   The payload types currently defined in this profile are assigned to
+   exactly one of three categories or media types:  audio only, video
+   only and those combining audio and video.  The media types are marked
+   in Tables 4 and 5 as "A", "V" and "AV", respectively.  Payload types
+   of different media types SHALL NOT be interleaved or multiplexed
+   within a single RTP session, but multiple RTP sessions MAY be used in
+   parallel to send multiple media types.  An RTP source MAY change
+   payload types within the same media type during a session.  See the
+   section "Multiplexing RTP Sessions" of RFC 3550 for additional
+   explanation.
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 32]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+               PT   encoding    media type  clock rate   channels
+                    name                    (Hz)
+               ___________________________________________________
+               0    PCMU        A            8,000       1
+               1    reserved    A
+               2    reserved    A
+               3    GSM         A            8,000       1
+               4    G723        A            8,000       1
+               5    DVI4        A            8,000       1
+               6    DVI4        A           16,000       1
+               7    LPC         A            8,000       1
+               8    PCMA        A            8,000       1
+               9    G722        A            8,000       1
+               10   L16         A           44,100       2
+               11   L16         A           44,100       1
+               12   QCELP       A            8,000       1
+               13   CN          A            8,000       1
+               14   MPA         A           90,000       (see text)
+               15   G728        A            8,000       1
+               16   DVI4        A           11,025       1
+               17   DVI4        A           22,050       1
+               18   G729        A            8,000       1
+               19   reserved    A
+               20   unassigned  A
+               21   unassigned  A
+               22   unassigned  A
+               23   unassigned  A
+               dyn  G726-40     A            8,000       1
+               dyn  G726-32     A            8,000       1
+               dyn  G726-24     A            8,000       1
+               dyn  G726-16     A            8,000       1
+               dyn  G729D       A            8,000       1
+               dyn  G729E       A            8,000       1
+               dyn  GSM-EFR     A            8,000       1
+               dyn  L8          A            var.        var.
+               dyn  RED         A                        (see text)
+               dyn  VDVI        A            var.        1
+
+               Table 4: Payload types (PT) for audio encodings
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 33]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+               PT      encoding    media type  clock rate
+                       name                    (Hz)
+               _____________________________________________
+               24      unassigned  V
+               25      CelB        V           90,000
+               26      JPEG        V           90,000
+               27      unassigned  V
+               28      nv          V           90,000
+               29      unassigned  V
+               30      unassigned  V
+               31      H261        V           90,000
+               32      MPV         V           90,000
+               33      MP2T        AV          90,000
+               34      H263        V           90,000
+               35-71   unassigned  ?
+               72-76   reserved    N/A         N/A
+               77-95   unassigned  ?
+               96-127  dynamic     ?
+               dyn     H263-1998   V           90,000
+
+               Table 5: Payload types (PT) for video and combined
+                        encodings
+
+   Session participants agree through mechanisms beyond the scope of
+   this specification on the set of payload types allowed in a given
+   session.  This set MAY, for example, be defined by the capabilities
+   of the applications used, negotiated by a conference control protocol
+   or established by agreement between the human participants.
+
+   Audio applications operating under this profile SHOULD, at a minimum,
+   be able to send and/or receive payload types 0 (PCMU) and 5 (DVI4).
+   This allows interoperability without format negotiation and ensures
+   successful negotiation with a conference control protocol.
+
+7.  RTP over TCP and Similar Byte Stream Protocols
+
+   Under special circumstances, it may be necessary to carry RTP in
+   protocols offering a byte stream abstraction, such as TCP, possibly
+   multiplexed with other data.  The application MUST define its own
+   method of delineating RTP and RTCP packets (RTSP [23] provides an
+   example of such an encapsulation specification).
+
+8.  Port Assignment
+
+   As specified in the RTP protocol definition, RTP data SHOULD be
+   carried on an even UDP port number and the corresponding RTCP packets
+   SHOULD be carried on the next higher (odd) port number.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 34]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   Applications operating under this profile MAY use any such UDP port
+   pair.  For example, the port pair MAY be allocated randomly by a
+   session management program.  A single fixed port number pair cannot
+   be required because multiple applications using this profile are
+   likely to run on the same host, and there are some operating systems
+   that do not allow multiple processes to use the same UDP port with
+   different multicast addresses.
+
+   However, port numbers 5004 and 5005 have been registered for use with
+   this profile for those applications that choose to use them as the
+   default pair.  Applications that operate under multiple profiles MAY
+   use this port pair as an indication to select this profile if they
+   are not subject to the constraint of the previous paragraph.
+   Applications need not have a default and MAY require that the port
+   pair be explicitly specified.  The particular port numbers were
+   chosen to lie in the range above 5000 to accommodate port number
+   allocation practice within some versions of the Unix operating
+   system, where port numbers below 1024 can only be used by privileged
+   processes and port numbers between 1024 and 5000 are automatically
+   assigned by the operating system.
+
+9.  Changes from RFC 1890
+
+   This RFC revises RFC 1890.  It is mostly backwards-compatible with
+   RFC 1890 except for functions removed because two interoperable
+   implementations were not found.  The additions to RFC 1890 codify
+   existing practice in the use of payload formats under this profile.
+   Since this profile may be used without using any of the payload
+   formats listed here, the addition of new payload formats in this
+   revision does not affect backwards compatibility.  The changes are
+   listed below, categorized into functional and non-functional changes.
+
+   Functional changes:
+
+   o  Section 11, "IANA Considerations" was added to specify the
+      registration of the name for this profile.  That appendix also
+      references a new Section 3 "Registering Additional Encodings"
+      which establishes a policy that no additional registration of
+      static payload types for this profile will be made beyond those
+      added in this revision and included in Tables 4 and 5.  Instead,
+      additional encoding names may be registered as MIME subtypes for
+      binding to dynamic payload types.  Non-normative references were
+      added to RFC 3555 [7] where MIME subtypes for all the listed
+      payload formats are registered, some with optional parameters for
+      use of the payload formats.
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 35]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   o  Static payload types 4, 16, 17 and 34 were added to incorporate
+      IANA registrations made since the publication of RFC 1890, along
+      with the corresponding payload format descriptions for G723 and
+      H263.
+
+   o  Following working group discussion, static payload types 12 and 18
+      were added along with the corresponding payload format
+      descriptions for QCELP and G729.  Static payload type 13 was
+      assigned to the Comfort Noise (CN) payload format defined in RFC
+      3389.  Payload type 19 was marked reserved because it had been
+      temporarily allocated to an earlier version of Comfort Noise
+      present in some draft revisions of this document.
+
+   o  The payload format for G721 was renamed to G726-32 following the
+      ITU-T renumbering, and the payload format description for G726 was
+      expanded to include the -16, -24 and -40 data rates.  Because of
+      confusion regarding draft revisions of this document, some
+      implementations of these G726 payload formats packed samples into
+      octets starting with the most significant bit rather than the
+      least significant bit as specified here.  To partially resolve
+      this incompatibility, new payload formats named AAL2-G726-16, -24,
+      -32 and -40 will be specified in a separate document (see note in
+      Section 4.5.4), and use of static payload type 2 is deprecated as
+      explained in Section 6.
+
+   o  Payload formats G729D and G729E were added following the ITU-T
+      addition of Annexes D and E to Recommendation G.729.  Listings
+      were added for payload formats GSM-EFR, RED, and H263-1998
+      published in other documents subsequent to RFC 1890.  These
+      additional payload formats are referenced only by dynamic payload
+      type numbers.
+
+   o  The descriptions of the payload formats for G722, G728, GSM, VDVI
+      were expanded.
+
+   o  The payload format for 1016 audio was removed and its static
+      payload type assignment 1 was marked "reserved" because two
+      interoperable implementations were not found.
+
+   o  Requirements for congestion control were added in Section 2.
+
+   o  This profile follows the suggestion in the revised RTP spec that
+      RTCP bandwidth may be specified separately from the session
+      bandwidth and separately for active senders and passive receivers.
+
+   o  The mapping of a user pass-phrase string into an encryption key
+      was deleted from Section 2 because two interoperable
+      implementations were not found.
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 36]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   o  The "quadrophonic" sample ordering convention for four-channel
+      audio was removed to eliminate an ambiguity as noted in Section
+      4.1.
+
+   Non-functional changes:
+
+   o  In Section 4.1, it is now explicitly stated that silence
+      suppression is allowed for all audio payload formats.  (This has
+      always been the case and derives from a fundamental aspect of
+      RTP's design and the motivations for packet audio, but was not
+      explicit stated before.)  The use of comfort noise is also
+      explained.
+
+   o  In Section 4.1, the requirement level for setting of the marker
+      bit on the first packet after silence for audio was changed from
+      "is" to "SHOULD be", and clarified that the marker bit is set only
+      when packets are intentionally not sent.
+
+   o  Similarly, text was added to specify that the marker bit SHOULD be
+      set to one on the last packet of a video frame, and that video
+      frames are distinguished by their timestamps.
+
+   o  RFC references are added for payload formats published after RFC
+      1890.
+
+   o  The security considerations and full copyright sections were
+      added.
+
+   o  According to Peter Hoddie of Apple, only pre-1994 Macintosh used
+      the 22254.54 rate and none the 11127.27 rate, so the latter was
+      dropped from the discussion of suggested sampling frequencies.
+
+   o  Table 1 was corrected to move some values from the "ms/packet"
+      column to the "default ms/packet" column where they belonged.
+
+   o  Since the Interactive Multimedia Association ceased operations, an
+      alternate resource was provided for a referenced IMA document.
+
+   o  A note has been added for G722 to clarify a discrepancy between
+      the actual sampling rate and the RTP timestamp clock rate.
+
+   o  Small clarifications of the text have been made in several places,
+      some in response to questions from readers.  In particular:
+
+      -  A definition for "media type" is given in Section 1.1 to allow
+         the explanation of multiplexing RTP sessions in Section 6 to be
+         more clear regarding the multiplexing of multiple media.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 37]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+      -  The explanation of how to determine the number of audio frames
+         in a packet from the length was expanded.
+
+      -  More description of the allocation of bandwidth to SDES items
+         is given.
+
+      -  A note was added that the convention for the order of channels
+         specified in Section 4.1 may be overridden by a particular
+         encoding or payload format specification.
+
+      -  The terms MUST, SHOULD, MAY, etc. are used as defined in RFC
+         2119.
+
+   o  A second author for this document was added.
+
+10. Security Considerations
+
+   Implementations using the profile defined in this specification are
+   subject to the security considerations discussed in the RTP
+   specification [1].  This profile does not specify any different
+   security services.  The primary function of this profile is to list a
+   set of data compression encodings for audio and video media.
+
+   Confidentiality of the media streams is achieved by encryption.
+   Because the data compression used with the payload formats described
+   in this profile is applied end-to-end, encryption may be performed
+   after compression so there is no conflict between the two operations.
+
+   A potential denial-of-service threat exists for data encodings using
+   compression techniques that have non-uniform receiver-end
+   computational load.  The attacker can inject pathological datagrams
+   into the stream which are complex to decode and cause the receiver to
+   be overloaded.
+
+   As with any IP-based protocol, in some circumstances a receiver may
+   be overloaded simply by the receipt of too many packets, either
+   desired or undesired.  Network-layer authentication MAY be used to
+   discard packets from undesired sources, but the processing cost of
+   the authentication itself may be too high.  In a multicast
+   environment, source pruning is implemented in IGMPv3 (RFC 3376) [24]
+   and in multicast routing protocols to allow a receiver to select
+   which sources are allowed to reach it.
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 38]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+11. IANA Considerations
+
+   The RTP specification establishes a registry of profile names for use
+   by higher-level control protocols, such as the Session Description
+   Protocol (SDP), RFC 2327 [6], to refer to transport methods.  This
+   profile registers the name "RTP/AVP".
+
+   Section 3 establishes the policy that no additional registration of
+   static RTP payload types for this profile will be made beyond those
+   added in this document revision and included in Tables 4 and 5.  IANA
+   may reference that section in declining to accept any additional
+   registration requests.  In Tables 4 and 5, note that types 1 and 2
+   have been marked reserved and the set of "dyn" payload types included
+   has been updated.  These changes are explained in Sections 6 and 9.
+
+12.  References
+
+12.1 Normative References
+
+   [1]  Schulzrinne, H., Casner, S., Frederick, R. and V. Jacobson,
+        "RTP:  A Transport Protocol for Real-Time Applications", RFC
+        3550, July 2003.
+
+   [2]  Bradner, S., "Key Words for Use in RFCs to Indicate Requirement
+        Levels", BCP 14, RFC 2119, March 1997.
+
+   [3]  Apple Computer, "Audio Interchange File Format AIFF-C", August
+        1991.  (also ftp://ftp.sgi.com/sgi/aiff-c.9.26.91.ps.Z).
+
+12.2 Informative References
+
+   [4]  Braden, R., Clark, D. and S. Shenker, "Integrated Services in
+        the Internet Architecture: an Overview", RFC 1633, June 1994.
+
+   [5]  Blake, S., Black, D., Carlson, M., Davies, E., Wang, Z. and W.
+        Weiss, "An Architecture for Differentiated Service", RFC 2475,
+        December 1998.
+
+   [6]  Handley, M. and V. Jacobson, "SDP: Session Description
+        Protocol", RFC 2327, April 1998.
+
+   [7]  Casner, S. and P. Hoschka, "MIME Type Registration of RTP
+        Payload Types", RFC 3555, July 2003.
+
+   [8]  Freed, N., Klensin, J. and J. Postel, "Multipurpose Internet
+        Mail Extensions (MIME) Part Four: Registration Procedures", BCP
+        13, RFC 2048, November 1996.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 39]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   [9]  Zopf, R., "Real-time Transport Protocol (RTP) Payload for
+        Comfort Noise (CN)", RFC 3389, September 2002.
+
+   [10] Deleam, D. and J.-P. Petit, "Real-time implementations of the
+        recent ITU-T low bit rate speech coders on the TI TMS320C54X
+        DSP: results, methodology, and applications", in Proc. of
+        International Conference on Signal Processing, Technology, and
+        Applications (ICSPAT) , (Boston, Massachusetts), pp. 1656--1660,
+        October 1996.
+
+   [11] Mouly, M. and M.-B. Pautet, The GSM system for mobile
+        communications Lassay-les-Chateaux, France: Europe Media
+        Duplication, 1993.
+
+   [12] Degener, J., "Digital Speech Compression", Dr. Dobb's Journal,
+        December 1994.
+
+   [13] Redl, S., Weber, M. and M. Oliphant, An Introduction to GSM
+        Boston: Artech House, 1995.
+
+   [14] Hoffman, D., Fernando, G., Goyal, V. and M. Civanlar, "RTP
+        Payload Format for MPEG1/MPEG2 Video", RFC 2250, January 1998.
+
+   [15] Jayant, N. and P. Noll, Digital Coding of Waveforms--Principles
+        and Applications to Speech and Video Englewood Cliffs, New
+        Jersey: Prentice-Hall, 1984.
+
+   [16] McKay, K., "RTP Payload Format for PureVoice(tm) Audio", RFC
+        2658, August 1999.
+
+   [17] Perkins, C., Kouvelas, I., Hodson, O., Hardman, V., Handley, M.,
+        Bolot, J.-C., Vega-Garcia, A. and S. Fosse-Parisis, "RTP Payload
+        for Redundant Audio Data", RFC 2198, September 1997.
+
+   [18] Speer, M. and D. Hoffman, "RTP Payload Format of Sun's CellB
+        Video Encoding", RFC 2029, October 1996.
+
+   [19] Berc, L., Fenner, W., Frederick, R., McCanne, S. and P. Stewart,
+        "RTP Payload Format for JPEG-Compressed Video", RFC 2435,
+        October 1998.
+
+   [20] Turletti, T. and C. Huitema, "RTP Payload Format for H.261 Video
+        Streams", RFC 2032, October 1996.
+
+   [21] Zhu, C., "RTP Payload Format for H.263 Video Streams", RFC 2190,
+        September 1997.
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 40]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   [22] Bormann, C., Cline, L., Deisher, G., Gardos, T., Maciocco, C.,
+        Newell, D., Ott, J., Sullivan, G., Wenger, S. and C. Zhu, "RTP
+        Payload Format for the 1998 Version of ITU-T Rec. H.263 Video
+        (H.263+)", RFC 2429, October 1998.
+
+   [23] Schulzrinne, H., Rao, A. and R. Lanphier, "Real Time Streaming
+        Protocol (RTSP)", RFC 2326, April 1998.
+
+   [24] Cain, B., Deering, S., Kouvelas, I., Fenner, B. and A.
+        Thyagarajan, "Internet Group Management Protocol, Version 3",
+        RFC 3376, October 2002.
+
+13. Current Locations of Related Resources
+
+   Note:  Several sections below refer to the ITU-T Software Tool
+   Library (STL).  It is available from the ITU Sales Service, Place des
+   Nations, CH-1211 Geneve 20, Switzerland (also check
+   http://www.itu.int).  The ITU-T STL is covered by a license defined
+   in ITU-T Recommendation G.191, "Software tools for speech and audio
+   coding standardization".
+
+   DVI4
+
+   An archived copy of the document IMA Recommended Practices for
+   Enhancing Digital Audio Compatibility in Multimedia Systems (version
+   3.0), which describes the IMA ADPCM algorithm, is available at:
+
+      http://www.cs.columbia.edu/~hgs/audio/dvi/
+
+   An implementation is available from Jack Jansen at
+
+      ftp://ftp.cwi.nl/local/pub/audio/adpcm.shar
+
+   G722
+
+   An implementation of the G.722 algorithm is available as part of the
+   ITU-T STL, described above.
+
+   G723
+
+   The reference C code implementation defining the G.723.1 algorithm
+   and its Annexes A, B, and C are available as an integral part of
+   Recommendation G.723.1 from the ITU Sales Service, address listed
+   above.  Both the algorithm and C code are covered by a specific
+   license.  The ITU-T Secretariat should be contacted to obtain such
+   licensing information.
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 41]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+   G726
+
+   G726 is specified in the ITU-T Recommendation G.726, "40, 32, 24, and
+   16 kb/s Adaptive Differential Pulse Code Modulation (ADPCM)".  An
+   implementation of the G.726 algorithm is available as part of the
+   ITU-T STL, described above.
+
+   G729
+
+   The reference C code implementation defining the G.729 algorithm and
+   its Annexes A through I are available as an integral part of
+   Recommendation G.729 from the ITU Sales Service, listed above.  Annex
+   I contains the integrated C source code for all G.729 operating
+   modes.  The G.729 algorithm and associated C code are covered by a
+   specific license.  The contact information for obtaining the license
+   is available from the ITU-T Secretariat.
+
+   GSM
+
+   A reference implementation was written by Carsten Bormann and Jutta
+   Degener (then at TU Berlin, Germany).  It is available at
+
+      http://www.dmn.tzi.org/software/gsm/
+
+   Although the RPE-LTP algorithm is not an ITU-T standard, there is a C
+   code implementation of the RPE-LTP algorithm available as part of the
+   ITU-T STL.  The STL implementation is an adaptation of the TU Berlin
+   version.
+
+   LPC
+
+   An implementation is available at
+
+      ftp://parcftp.xerox.com/pub/net-research/lpc.tar.Z
+
+   PCMU, PCMA
+
+   An implementation of these algorithms is available as part of the
+   ITU-T STL, described above.
+
+14. Acknowledgments
+
+   The comments and careful review of Simao Campos, Richard Cox and AVT
+   Working Group participants are gratefully acknowledged.  The GSM
+   description was adopted from the IMTC Voice over IP Forum Service
+   Interoperability Implementation Agreement (January 1997).  Fred Burg
+   and Terry Lyons helped with the G.729 description.
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 42]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+15. Intellectual Property Rights Statement
+
+   The IETF takes no position regarding the validity or scope of any
+   intellectual property or other rights that might be claimed to
+   pertain to the implementation or use of the technology described in
+   this document or the extent to which any license under such rights
+   might or might not be available; neither does it represent that it
+   has made any effort to identify any such rights.  Information on the
+   IETF's procedures with respect to rights in standards-track and
+   standards-related documentation can be found in BCP-11.  Copies of
+   claims of rights made available for publication and any assurances of
+   licenses to be made available, or the result of an attempt made to
+   obtain a general license or permission for the use of such
+   proprietary rights by implementors or users of this specification can
+   be obtained from the IETF Secretariat.
+
+   The IETF invites any interested party to bring to its attention any
+   copyrights, patents or patent applications, or other proprietary
+   rights which may cover technology that may be required to practice
+   this standard.  Please address the information to the IETF Executive
+   Director.
+
+16. Authors' Addresses
+
+   Henning Schulzrinne
+   Department of Computer Science
+   Columbia University
+   1214 Amsterdam Avenue
+   New York, NY 10027
+   United States
+
+   EMail: schulzrinne@cs.columbia.edu
+
+
+   Stephen L. Casner
+   Packet Design
+   3400 Hillview Avenue, Building 3
+   Palo Alto, CA 94304
+   United States
+
+   EMail: casner@acm.org
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 43]
+\f
+RFC 3551                    RTP A/V Profile                    July 2003
+
+
+17. Full Copyright Statement
+
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+   Funding for the RFC Editor function is currently provided by the
+   Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Schulzrinne & Casner        Standards Track                    [Page 44]
+\f
diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c
new file mode 100644 (file)
index 0000000..5a33ebc
--- /dev/null
@@ -0,0 +1,392 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "rtp.h"
+
+pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size) {
+    pa_assert(c);
+    pa_assert(fd >= 0);
+
+    c->fd = fd;
+    c->sequence = (uint16_t) (rand()*rand());
+    c->timestamp = 0;
+    c->ssrc = ssrc ? ssrc : (uint32_t) (rand()*rand());
+    c->payload = payload & 127;
+    c->frame_size = frame_size;
+
+    pa_memchunk_reset(&c->memchunk);
+
+    return c;
+}
+
+#define MAX_IOVECS 16
+
+int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q) {
+    struct iovec iov[MAX_IOVECS];
+    pa_memblock* mb[MAX_IOVECS];
+    int iov_idx = 1;
+    size_t n = 0;
+
+    pa_assert(c);
+    pa_assert(size > 0);
+    pa_assert(q);
+
+    if (pa_memblockq_get_length(q) < size)
+        return 0;
+
+    for (;;) {
+        int r;
+        pa_memchunk chunk;
+
+        pa_memchunk_reset(&chunk);
+
+        if ((r = pa_memblockq_peek(q, &chunk)) >= 0) {
+
+            size_t k = n + chunk.length > size ? size - n : chunk.length;
+
+            pa_assert(chunk.memblock);
+
+            iov[iov_idx].iov_base = ((uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index);
+            iov[iov_idx].iov_len = k;
+            mb[iov_idx] = chunk.memblock;
+            iov_idx ++;
+
+            n += k;
+            pa_memblockq_drop(q, k);
+        }
+
+        pa_assert(n % c->frame_size == 0);
+
+        if (r < 0 || n >= size || iov_idx >= MAX_IOVECS) {
+            uint32_t header[3];
+            struct msghdr m;
+            int k, i;
+
+            if (n > 0) {
+                header[0] = htonl(((uint32_t) 2 << 30) | ((uint32_t) c->payload << 16) | ((uint32_t) c->sequence));
+                header[1] = htonl(c->timestamp);
+                header[2] = htonl(c->ssrc);
+
+                iov[0].iov_base = (void*)header;
+                iov[0].iov_len = sizeof(header);
+
+                m.msg_name = NULL;
+                m.msg_namelen = 0;
+                m.msg_iov = iov;
+                m.msg_iovlen = iov_idx;
+                m.msg_control = NULL;
+                m.msg_controllen = 0;
+                m.msg_flags = 0;
+
+                k = sendmsg(c->fd, &m, MSG_DONTWAIT);
+
+                for (i = 1; i < iov_idx; i++) {
+                    pa_memblock_release(mb[i]);
+                    pa_memblock_unref(mb[i]);
+                }
+
+                c->sequence++;
+            } else
+                k = 0;
+
+            c->timestamp += n/c->frame_size;
+
+            if (k < 0) {
+                if (errno != EAGAIN && errno != EINTR) /* If the queue is full, just ignore it */
+                    pa_log("sendmsg() failed: %s", pa_cstrerror(errno));
+                return -1;
+            }
+
+            if (r < 0 || pa_memblockq_get_length(q) < size)
+                break;
+
+            n = 0;
+            iov_idx = 1;
+        }
+    }
+
+    return 0;
+}
+
+pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame_size) {
+    pa_assert(c);
+
+    c->fd = fd;
+    c->frame_size = frame_size;
+
+    pa_memchunk_reset(&c->memchunk);
+    return c;
+}
+
+int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
+    int size;
+    struct msghdr m;
+    struct iovec iov;
+    uint32_t header;
+    int cc;
+    ssize_t r;
+
+    pa_assert(c);
+    pa_assert(chunk);
+
+    pa_memchunk_reset(chunk);
+
+    if (ioctl(c->fd, FIONREAD, &size) < 0) {
+        pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (size <= 0)
+        return 0;
+
+    if (c->memchunk.length < (unsigned) size) {
+        size_t l;
+
+        if (c->memchunk.memblock)
+            pa_memblock_unref(c->memchunk.memblock);
+
+        l = PA_MAX((size_t) size, pa_mempool_block_size_max(pool));
+
+        c->memchunk.memblock = pa_memblock_new(pool, l);
+        c->memchunk.index = 0;
+        c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock);
+    }
+
+    pa_assert(c->memchunk.length >= (size_t) size);
+
+    chunk->memblock = pa_memblock_ref(c->memchunk.memblock);
+    chunk->index = c->memchunk.index;
+
+    iov.iov_base = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index;
+    iov.iov_len = size;
+
+    m.msg_name = NULL;
+    m.msg_namelen = 0;
+    m.msg_iov = &iov;
+    m.msg_iovlen = 1;
+    m.msg_control = NULL;
+    m.msg_controllen = 0;
+    m.msg_flags = 0;
+
+    r = recvmsg(c->fd, &m, 0);
+    pa_memblock_release(chunk->memblock);
+
+    if (r != size) {
+        if (r < 0 && errno != EAGAIN && errno != EINTR)
+            pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
+
+        goto fail;
+    }
+
+    if (size < 12) {
+        pa_log_warn("RTP packet too short.");
+        goto fail;
+    }
+
+    memcpy(&header, iov.iov_base, sizeof(uint32_t));
+    memcpy(&c->timestamp, (uint8_t*) iov.iov_base + 4, sizeof(uint32_t));
+    memcpy(&c->ssrc, (uint8_t*) iov.iov_base + 8, sizeof(uint32_t));
+
+    header = ntohl(header);
+    c->timestamp = ntohl(c->timestamp);
+    c->ssrc = ntohl(c->ssrc);
+
+    if ((header >> 30) != 2) {
+        pa_log_warn("Unsupported RTP version.");
+        goto fail;
+    }
+
+    if ((header >> 29) & 1) {
+        pa_log_warn("RTP padding not supported.");
+        goto fail;
+    }
+
+    if ((header >> 28) & 1) {
+        pa_log_warn("RTP header extensions not supported.");
+        goto fail;
+    }
+
+    cc = (header >> 24) & 0xF;
+    c->payload = (header >> 16) & 127;
+    c->sequence = header & 0xFFFF;
+
+    if (12 + cc*4 > size) {
+        pa_log_warn("RTP packet too short. (CSRC)");
+        goto fail;
+    }
+
+    chunk->index += 12 + cc*4;
+    chunk->length = size - 12 + cc*4;
+
+    if (chunk->length % c->frame_size != 0) {
+        pa_log_warn("Bad RTP packet size.");
+        goto fail;
+    }
+
+    c->memchunk.index = chunk->index + chunk->length;
+    c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock) - c->memchunk.index;
+
+    if (c->memchunk.length <= 0) {
+        pa_memblock_unref(c->memchunk.memblock);
+        pa_memchunk_reset(&c->memchunk);
+    }
+
+    return 0;
+
+fail:
+    if (chunk->memblock)
+        pa_memblock_unref(chunk->memblock);
+
+    return -1;
+}
+
+uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss) {
+    pa_assert(ss);
+
+    if (ss->format == PA_SAMPLE_ULAW && ss->rate == 8000 && ss->channels == 1)
+        return 0;
+    if (ss->format == PA_SAMPLE_ALAW && ss->rate == 8000 && ss->channels == 1)
+        return 8;
+    if (ss->format == PA_SAMPLE_S16BE && ss->rate == 44100 && ss->channels == 2)
+        return 10;
+    if (ss->format == PA_SAMPLE_S16BE && ss->rate == 44100 && ss->channels == 1)
+        return 11;
+
+    return 127;
+}
+
+pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec *ss) {
+    pa_assert(ss);
+
+    switch (payload) {
+        case 0:
+            ss->channels = 1;
+            ss->format = PA_SAMPLE_ULAW;
+            ss->rate = 8000;
+            break;
+
+        case 8:
+            ss->channels = 1;
+            ss->format = PA_SAMPLE_ALAW;
+            ss->rate = 8000;
+            break;
+
+        case 10:
+            ss->channels = 2;
+            ss->format = PA_SAMPLE_S16BE;
+            ss->rate = 44100;
+            break;
+
+        case 11:
+            ss->channels = 1;
+            ss->format = PA_SAMPLE_S16BE;
+            ss->rate = 44100;
+            break;
+
+        default:
+            return NULL;
+    }
+
+    return ss;
+}
+
+pa_sample_spec *pa_rtp_sample_spec_fixup(pa_sample_spec * ss) {
+    pa_assert(ss);
+
+    if (!pa_rtp_sample_spec_valid(ss))
+        ss->format = PA_SAMPLE_S16BE;
+
+    pa_assert(pa_rtp_sample_spec_valid(ss));
+    return ss;
+}
+
+int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) {
+    pa_assert(ss);
+
+    if (!pa_sample_spec_valid(ss))
+        return 0;
+
+    return
+        ss->format == PA_SAMPLE_U8 ||
+        ss->format == PA_SAMPLE_ALAW ||
+        ss->format == PA_SAMPLE_ULAW ||
+        ss->format == PA_SAMPLE_S16BE;
+}
+
+void pa_rtp_context_destroy(pa_rtp_context *c) {
+    pa_assert(c);
+
+    pa_assert_se(pa_close(c->fd) == 0);
+
+    if (c->memchunk.memblock)
+        pa_memblock_unref(c->memchunk.memblock);
+}
+
+const char* pa_rtp_format_to_string(pa_sample_format_t f) {
+    switch (f) {
+        case PA_SAMPLE_S16BE:
+            return "L16";
+        case PA_SAMPLE_U8:
+            return "L8";
+        case PA_SAMPLE_ALAW:
+            return "PCMA";
+        case PA_SAMPLE_ULAW:
+            return "PCMU";
+        default:
+            return NULL;
+    }
+}
+
+pa_sample_format_t pa_rtp_string_to_format(const char *s) {
+    pa_assert(s);
+
+    if (!(strcmp(s, "L16")))
+        return PA_SAMPLE_S16BE;
+    else if (!strcmp(s, "L8"))
+        return PA_SAMPLE_U8;
+    else if (!strcmp(s, "PCMA"))
+        return PA_SAMPLE_ALAW;
+    else if (!strcmp(s, "PCMU"))
+        return PA_SAMPLE_ULAW;
+    else
+        return PA_SAMPLE_INVALID;
+}
diff --git a/src/modules/rtp/rtp.h b/src/modules/rtp/rtp.h
new file mode 100644 (file)
index 0000000..a2728f0
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef foortphfoo
+#define foortphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_rtp_context {
+    int fd;
+    uint16_t sequence;
+    uint32_t timestamp;
+    uint32_t ssrc;
+    uint8_t payload;
+    size_t frame_size;
+
+    pa_memchunk memchunk;
+} pa_rtp_context;
+
+pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size);
+int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q);
+
+pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame_size);
+int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool);
+
+void pa_rtp_context_destroy(pa_rtp_context *c);
+
+pa_sample_spec* pa_rtp_sample_spec_fixup(pa_sample_spec *ss);
+int pa_rtp_sample_spec_valid(const pa_sample_spec *ss);
+
+uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss);
+pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec *ss);
+
+const char* pa_rtp_format_to_string(pa_sample_format_t f);
+pa_sample_format_t pa_rtp_string_to_format(const char *s);
+
+#endif
diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c
new file mode 100644 (file)
index 0000000..5d9b58f
--- /dev/null
@@ -0,0 +1,221 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <time.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "sap.h"
+#include "sdp.h"
+
+#define MIME_TYPE "application/sdp"
+
+pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data) {
+    pa_assert(c);
+    pa_assert(fd >= 0);
+    pa_assert(sdp_data);
+
+    c->fd = fd;
+    c->sdp_data = sdp_data;
+    c->msg_id_hash = (uint16_t) (rand()*rand());
+
+    return c;
+}
+
+void pa_sap_context_destroy(pa_sap_context *c) {
+    pa_assert(c);
+
+    pa_close(c->fd);
+    pa_xfree(c->sdp_data);
+}
+
+int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye) {
+    uint32_t header;
+    struct sockaddr_storage sa_buf;
+    struct sockaddr *sa = (struct sockaddr*) &sa_buf;
+    socklen_t salen = sizeof(sa_buf);
+    struct iovec iov[4];
+    struct msghdr m;
+    int k;
+
+    if (getsockname(c->fd, sa, &salen) < 0) {
+        pa_log("getsockname() failed: %s\n", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+
+    header = htonl(((uint32_t) 1 << 29) |
+                   (sa->sa_family == AF_INET6 ? (uint32_t) 1 << 28 : 0) |
+                   (goodbye ? (uint32_t) 1 << 26 : 0) |
+                   (c->msg_id_hash));
+
+    iov[0].iov_base = &header;
+    iov[0].iov_len = sizeof(header);
+
+    iov[1].iov_base = sa->sa_family == AF_INET ? (void*) &((struct sockaddr_in*) sa)->sin_addr : (void*) &((struct sockaddr_in6*) sa)->sin6_addr;
+    iov[1].iov_len = sa->sa_family == AF_INET ? 4 : 16;
+
+    iov[2].iov_base = (char*) MIME_TYPE;
+    iov[2].iov_len = sizeof(MIME_TYPE);
+
+    iov[3].iov_base = c->sdp_data;
+    iov[3].iov_len = strlen(c->sdp_data);
+
+    m.msg_name = NULL;
+    m.msg_namelen = 0;
+    m.msg_iov = iov;
+    m.msg_iovlen = 4;
+    m.msg_control = NULL;
+    m.msg_controllen = 0;
+    m.msg_flags = 0;
+
+    if ((k = sendmsg(c->fd, &m, MSG_DONTWAIT)) < 0)
+        pa_log_warn("sendmsg() failed: %s\n", pa_cstrerror(errno));
+
+    return k;
+}
+
+pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) {
+    pa_assert(c);
+    pa_assert(fd >= 0);
+
+    c->fd = fd;
+    c->sdp_data = NULL;
+    return c;
+}
+
+int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye) {
+    struct msghdr m;
+    struct iovec iov;
+    int size, k;
+    char *buf = NULL, *e;
+    uint32_t header;
+    int six, ac;
+    ssize_t r;
+
+    pa_assert(c);
+    pa_assert(goodbye);
+
+    if (ioctl(c->fd, FIONREAD, &size) < 0) {
+        pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    buf = pa_xnew(char, size+1);
+    buf[size] = 0;
+
+    iov.iov_base = buf;
+    iov.iov_len = size;
+
+    m.msg_name = NULL;
+    m.msg_namelen = 0;
+    m.msg_iov = &iov;
+    m.msg_iovlen = 1;
+    m.msg_control = NULL;
+    m.msg_controllen = 0;
+    m.msg_flags = 0;
+
+    if ((r = recvmsg(c->fd, &m, 0)) != size) {
+        pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch");
+        goto fail;
+    }
+
+    if (size < 4) {
+        pa_log_warn("SAP packet too short.");
+        goto fail;
+    }
+
+    memcpy(&header, buf, sizeof(uint32_t));
+    header = ntohl(header);
+
+    if (header >> 29 != 1) {
+        pa_log_warn("Unsupported SAP version.");
+        goto fail;
+    }
+
+    if ((header >> 25) & 1) {
+        pa_log_warn("Encrypted SAP not supported.");
+        goto fail;
+    }
+
+    if ((header >> 24) & 1) {
+        pa_log_warn("Compressed SAP not supported.");
+        goto fail;
+    }
+
+    six = (header >> 28) & 1;
+    ac = (header >> 16) & 0xFF;
+
+    k = 4 + (six ? 16 : 4) + ac*4;
+    if (size < k) {
+        pa_log_warn("SAP packet too short (AD).");
+        goto fail;
+    }
+
+    e = buf + k;
+    size -= k;
+
+    if ((unsigned) size >= sizeof(MIME_TYPE) && !strcmp(e, MIME_TYPE)) {
+        e += sizeof(MIME_TYPE);
+        size -= sizeof(MIME_TYPE);
+    } else if ((unsigned) size < sizeof(PA_SDP_HEADER)-1 || strncmp(e, PA_SDP_HEADER, sizeof(PA_SDP_HEADER)-1)) {
+        pa_log_warn("Invalid SDP header.");
+        goto fail;
+    }
+
+    if (c->sdp_data)
+        pa_xfree(c->sdp_data);
+
+    c->sdp_data = pa_xstrndup(e, size);
+    pa_xfree(buf);
+
+    *goodbye = !!((header >> 26) & 1);
+
+    return 0;
+
+fail:
+    pa_xfree(buf);
+
+    return -1;
+}
diff --git a/src/modules/rtp/sap.h b/src/modules/rtp/sap.h
new file mode 100644 (file)
index 0000000..69c757c
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef foosaphfoo
+#define foosaphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_sap_context {
+    int fd;
+    char *sdp_data;
+
+    uint16_t msg_id_hash;
+} pa_sap_context;
+
+pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data);
+void pa_sap_context_destroy(pa_sap_context *c);
+
+int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye);
+
+pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd);
+int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye);
+
+#endif
diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c
new file mode 100644 (file)
index 0000000..cef9043
--- /dev/null
@@ -0,0 +1,259 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <time.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "sdp.h"
+#include "rtp.h"
+
+char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss) {
+    uint32_t ntp;
+    char buf_src[64], buf_dst[64], un[64];
+    const char *u, *f, *a;
+
+    pa_assert(src);
+    pa_assert(dst);
+    pa_assert(af == AF_INET || af == AF_INET6);
+
+    pa_assert_se(f = pa_rtp_format_to_string(ss->format));
+
+    if (!(u = pa_get_user_name(un, sizeof(un))))
+        u = "-";
+
+    ntp = time(NULL) + 2208988800U;
+
+    pa_assert_se(a = inet_ntop(af, src, buf_src, sizeof(buf_src)));
+    pa_assert_se(a = inet_ntop(af, dst, buf_dst, sizeof(buf_dst)));
+
+    return pa_sprintf_malloc(
+            PA_SDP_HEADER
+            "o=%s %lu 0 IN %s %s\n"
+            "s=%s\n"
+            "c=IN %s %s\n"
+            "t=%lu 0\n"
+            "a=recvonly\n"
+            "m=audio %u RTP/AVP %i\n"
+            "a=rtpmap:%i %s/%u/%u\n"
+            "a=type:broadcast\n",
+            u, (unsigned long) ntp, af == AF_INET ? "IP4" : "IP6", buf_src,
+            name,
+            af == AF_INET ? "IP4" : "IP6", buf_dst,
+            (unsigned long) ntp,
+            port, payload,
+            payload, f, ss->rate, ss->channels);
+}
+
+static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {
+    unsigned rate, channels;
+    pa_assert(ss);
+    pa_assert(c);
+
+    if (pa_startswith(c, "L16/")) {
+        ss->format = PA_SAMPLE_S16BE;
+        c += 4;
+    } else if (pa_startswith(c, "L8/")) {
+        ss->format = PA_SAMPLE_U8;
+        c += 3;
+    } else if (pa_startswith(c, "PCMA/")) {
+        ss->format = PA_SAMPLE_ALAW;
+        c += 5;
+    } else if (pa_startswith(c, "PCMU/")) {
+        ss->format = PA_SAMPLE_ULAW;
+        c += 5;
+    } else
+        return NULL;
+
+    if (sscanf(c, "%u/%u", &rate, &channels) == 2) {
+        ss->rate = rate;
+        ss->channels = channels;
+    } else if (sscanf(c, "%u", &rate) == 2) {
+        ss->rate = rate;
+        ss->channels = 1;
+    } else
+        return NULL;
+
+    if (!pa_sample_spec_valid(ss))
+        return NULL;
+
+    return ss;
+}
+
+pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
+    uint16_t port = 0;
+    pa_bool_t ss_valid = FALSE;
+
+    pa_assert(t);
+    pa_assert(i);
+
+    i->origin = i->session_name = NULL;
+    i->salen = 0;
+    i->payload = 255;
+
+    if (!pa_startswith(t, PA_SDP_HEADER)) {
+        pa_log("Failed to parse SDP data: invalid header.");
+        goto fail;
+    }
+
+    t += sizeof(PA_SDP_HEADER)-1;
+
+    while (*t) {
+        size_t l;
+
+        l = strcspn(t, "\n");
+
+        if (l <= 2) {
+            pa_log("Failed to parse SDP data: line too short: >%s<.", t);
+            goto fail;
+        }
+
+        if (pa_startswith(t, "o="))
+            i->origin = pa_xstrndup(t+2, l-2);
+        else if (pa_startswith(t, "s="))
+            i->session_name = pa_xstrndup(t+2, l-2);
+        else if (pa_startswith(t, "c=IN IP4 ")) {
+            char a[64];
+            size_t k;
+
+            k = l-8 > sizeof(a) ? sizeof(a) : l-8;
+
+            pa_strlcpy(a, t+9, k);
+            a[strcspn(a, "/")] = 0;
+
+            if (inet_pton(AF_INET, a, &((struct sockaddr_in*) &i->sa)->sin_addr) <= 0) {
+                pa_log("Failed to parse SDP data: bad address: >%s<.", a);
+                goto fail;
+            }
+
+            ((struct sockaddr_in*) &i->sa)->sin_family = AF_INET;
+            ((struct sockaddr_in*) &i->sa)->sin_port = 0;
+            i->salen = sizeof(struct sockaddr_in);
+        } else if (pa_startswith(t, "c=IN IP6 ")) {
+            char a[64];
+            size_t k;
+
+            k = l-8 > sizeof(a) ? sizeof(a) : l-8;
+
+            pa_strlcpy(a, t+9, k);
+            a[strcspn(a, "/")] = 0;
+
+            if (inet_pton(AF_INET6, a, &((struct sockaddr_in6*) &i->sa)->sin6_addr) <= 0) {
+                pa_log("Failed to parse SDP data: bad address: >%s<.", a);
+                goto fail;
+            }
+
+            ((struct sockaddr_in6*) &i->sa)->sin6_family = AF_INET6;
+            ((struct sockaddr_in6*) &i->sa)->sin6_port = 0;
+            i->salen = sizeof(struct sockaddr_in6);
+        } else if (pa_startswith(t, "m=audio ")) {
+
+            if (i->payload > 127) {
+                int _port, _payload;
+
+                if (sscanf(t+8, "%i RTP/AVP %i", &_port, &_payload) == 2) {
+
+                    if (_port <= 0 || _port > 0xFFFF) {
+                        pa_log("Failed to parse SDP data: invalid port %i.", _port);
+                        goto fail;
+                    }
+
+                    if (_payload < 0 || _payload > 127) {
+                        pa_log("Failed to parse SDP data: invalid payload %i.", _payload);
+                        goto fail;
+                    }
+
+                    port = (uint16_t) _port;
+                    i->payload = (uint8_t) _payload;
+
+                    if (pa_rtp_sample_spec_from_payload(i->payload, &i->sample_spec))
+                        ss_valid = TRUE;
+                }
+            }
+        } else if (pa_startswith(t, "a=rtpmap:")) {
+
+            if (i->payload <= 127) {
+                char c[64];
+                int _payload;
+
+                if (sscanf(t+9, "%i %64c", &_payload, c) == 2) {
+
+                    if (_payload < 0 || _payload > 127) {
+                        pa_log("Failed to parse SDP data: invalid payload %i.", _payload);
+                        goto fail;
+                    }
+                    if (_payload == i->payload) {
+
+                        c[strcspn(c, "\n")] = 0;
+
+                        if (parse_sdp_sample_spec(&i->sample_spec, c))
+                            ss_valid = TRUE;
+                    }
+                }
+            }
+        }
+
+        t += l;
+
+        if (*t == '\n')
+            t++;
+    }
+
+    if (!i->origin || (!is_goodbye && (!i->salen || i->payload > 127 || !ss_valid || port == 0))) {
+        pa_log("Failed to parse SDP data: missing data.");
+        goto fail;
+    }
+
+    if (((struct sockaddr*) &i->sa)->sa_family == AF_INET)
+        ((struct sockaddr_in*) &i->sa)->sin_port = htons(port);
+    else
+        ((struct sockaddr_in6*) &i->sa)->sin6_port = htons(port);
+
+    return i;
+
+fail:
+    pa_xfree(i->origin);
+    pa_xfree(i->session_name);
+
+    return NULL;
+}
+
+void pa_sdp_info_destroy(pa_sdp_info *i) {
+    pa_assert(i);
+
+    pa_xfree(i->origin);
+    pa_xfree(i->session_name);
+}
diff --git a/src/modules/rtp/sdp.h b/src/modules/rtp/sdp.h
new file mode 100644 (file)
index 0000000..933a602
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef foosdphfoo
+#define foosdphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <pulse/sample.h>
+
+#define PA_SDP_HEADER "v=0\n"
+
+typedef struct pa_sdp_info {
+    char *origin;
+    char *session_name;
+
+    struct sockaddr_storage sa;
+    socklen_t salen;
+
+    pa_sample_spec sample_spec;
+    uint8_t payload;
+} pa_sdp_info;
+
+char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss);
+
+pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *info, int is_goodbye);
+
+void pa_sdp_info_destroy(pa_sdp_info *i);
+
+#endif
diff --git a/src/pulse/.gitignore b/src/pulse/.gitignore
new file mode 100644 (file)
index 0000000..6702033
--- /dev/null
@@ -0,0 +1 @@
+version.h
diff --git a/src/pulse/Makefile b/src/pulse/Makefile
new file mode 100644 (file)
index 0000000..7c8875f
--- /dev/null
@@ -0,0 +1,13 @@
+# This is a dirty trick just to ease compilation with emacs
+#
+# This file is not intended to be distributed or anything
+#
+# So: don't touch it, even better ignore it!
+
+all:
+       $(MAKE) -C ..
+
+clean:
+       $(MAKE) -C .. clean
+
+.PHONY: all clean
diff --git a/src/pulse/browser.c b/src/pulse/browser.c
new file mode 100644 (file)
index 0000000..1a3f657
--- /dev/null
@@ -0,0 +1,464 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <avahi-client/lookup.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/error.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/avahi-wrap.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
+
+#include "browser.h"
+
+#define SERVICE_TYPE_SINK "_pulse-sink._tcp."
+#define SERVICE_TYPE_SOURCE "_pulse-source._tcp."
+#define SERVICE_TYPE_SERVER "_pulse-server._tcp."
+
+struct pa_browser {
+    PA_REFCNT_DECLARE;
+
+    pa_mainloop_api *mainloop;
+    AvahiPoll* avahi_poll;
+
+    pa_browse_cb_t callback;
+    void *userdata;
+
+    pa_browser_error_cb_t error_callback;
+    void *error_userdata;
+
+    AvahiClient *client;
+    AvahiServiceBrowser *server_browser, *sink_browser, *source_browser;
+
+};
+
+static int map_to_opcode(const char *type, int new) {
+
+    if (avahi_domain_equal(type, SERVICE_TYPE_SINK))
+        return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK;
+    else if (avahi_domain_equal(type, SERVICE_TYPE_SOURCE))
+        return new ? PA_BROWSE_NEW_SOURCE : PA_BROWSE_REMOVE_SOURCE;
+    else if (avahi_domain_equal(type, SERVICE_TYPE_SERVER))
+        return new ? PA_BROWSE_NEW_SERVER : PA_BROWSE_REMOVE_SERVER;
+
+    return -1;
+}
+
+static void resolve_callback(
+        AvahiServiceResolver *r,
+        AvahiIfIndex interface,
+        AvahiProtocol protocol,
+        AvahiResolverEvent event,
+        const char *name,
+        const char *type,
+        const char *domain,
+        const char *host_name,
+        const AvahiAddress *aa,
+        uint16_t port,
+        AvahiStringList *txt,
+        AvahiLookupResultFlags flags,
+        void *userdata) {
+
+    pa_browser *b = userdata;
+    pa_browse_info i;
+    char ip[256], a[256];
+    int opcode;
+    int device_found = 0;
+    uint32_t cookie;
+    pa_sample_spec ss;
+    int ss_valid = 0;
+    char *key = NULL, *value = NULL;
+
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) >= 1);
+
+    memset(&i, 0, sizeof(i));
+    i.name = name;
+
+    if (event != AVAHI_RESOLVER_FOUND)
+        goto fail;
+
+    if (!b->callback)
+        goto fail;
+
+    opcode = map_to_opcode(type, 1);
+    pa_assert(opcode >= 0);
+
+    if (aa->proto == AVAHI_PROTO_INET)
+        pa_snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
+    else {
+        pa_assert(aa->proto == AVAHI_PROTO_INET6);
+        pa_snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port);
+    }
+    i.server = a;
+
+
+    while (txt) {
+
+        if (avahi_string_list_get_pair(txt, &key, &value, NULL) < 0)
+            break;
+
+        if (!strcmp(key, "device")) {
+            device_found = 1;
+            pa_xfree((char*) i.device);
+            i.device = value;
+            value = NULL;
+        } else if (!strcmp(key, "server-version")) {
+            pa_xfree((char*) i.server_version);
+            i.server_version = value;
+            value = NULL;
+        } else if (!strcmp(key, "user-name")) {
+            pa_xfree((char*) i.user_name);
+            i.user_name = value;
+            value = NULL;
+        } else if (!strcmp(key, "fqdn")) {
+            size_t l;
+
+            pa_xfree((char*) i.fqdn);
+            i.fqdn = value;
+            value = NULL;
+
+            l = strlen(a);
+            pa_assert(l+1 <= sizeof(a));
+            strncat(a, " ", sizeof(a)-l-1);
+            strncat(a, i.fqdn, sizeof(a)-l-2);
+        } else if (!strcmp(key, "cookie")) {
+
+            if (pa_atou(value, &cookie) < 0)
+                goto fail;
+
+            i.cookie = &cookie;
+        } else if (!strcmp(key, "description")) {
+            pa_xfree((char*) i.description);
+            i.description = value;
+            value = NULL;
+        } else if (!strcmp(key, "channels")) {
+            uint32_t ch;
+
+            if (pa_atou(value, &ch) < 0 || ch <= 0 || ch > 255)
+                goto fail;
+
+            ss.channels = (uint8_t) ch;
+            ss_valid |= 1;
+
+        } else if (!strcmp(key, "rate")) {
+            if (pa_atou(value, &ss.rate) < 0)
+                goto fail;
+            ss_valid |= 2;
+        } else if (!strcmp(key, "format")) {
+
+            if ((ss.format = pa_parse_sample_format(value)) == PA_SAMPLE_INVALID)
+                goto fail;
+
+            ss_valid |= 4;
+        }
+
+        pa_xfree(key);
+        pa_xfree(value);
+        key = value = NULL;
+
+        txt = avahi_string_list_get_next(txt);
+    }
+
+    /* No device txt record was sent for a sink or source service */
+    if (opcode != PA_BROWSE_NEW_SERVER && !device_found)
+        goto fail;
+
+    if (ss_valid == 7)
+        i.sample_spec = &ss;
+
+    b->callback(b, opcode, &i, b->userdata);
+
+fail:
+    pa_xfree((void*) i.device);
+    pa_xfree((void*) i.fqdn);
+    pa_xfree((void*) i.server_version);
+    pa_xfree((void*) i.user_name);
+    pa_xfree((void*) i.description);
+
+    pa_xfree(key);
+    pa_xfree(value);
+
+    avahi_service_resolver_free(r);
+}
+
+static void handle_failure(pa_browser *b) {
+    const char *e = NULL;
+
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) >= 1);
+
+    if (b->sink_browser)
+        avahi_service_browser_free(b->sink_browser);
+    if (b->source_browser)
+        avahi_service_browser_free(b->source_browser);
+    if (b->server_browser)
+        avahi_service_browser_free(b->server_browser);
+
+    b->sink_browser = b->source_browser = b->server_browser = NULL;
+
+    if (b->client) {
+        e = avahi_strerror(avahi_client_errno(b->client));
+        avahi_client_free(b->client);
+    }
+
+    b->client = NULL;
+
+    if (b->error_callback)
+        b->error_callback(b, e, b->error_userdata);
+}
+
+static void browse_callback(
+        AvahiServiceBrowser *sb,
+        AvahiIfIndex interface,
+        AvahiProtocol protocol,
+        AvahiBrowserEvent event,
+        const char *name,
+        const char *type,
+        const char *domain,
+        AvahiLookupResultFlags flags,
+        void *userdata) {
+
+    pa_browser *b = userdata;
+
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) >= 1);
+
+    switch (event) {
+        case AVAHI_BROWSER_NEW: {
+
+            if (!avahi_service_resolver_new(
+                          b->client,
+                          interface,
+                          protocol,
+                          name,
+                          type,
+                          domain,
+                          AVAHI_PROTO_UNSPEC,
+                          0,
+                          resolve_callback,
+                          b))
+                handle_failure(b);
+
+            break;
+        }
+
+        case AVAHI_BROWSER_REMOVE: {
+
+            if (b->callback) {
+                pa_browse_info i;
+                int opcode;
+
+                memset(&i, 0, sizeof(i));
+                i.name = name;
+
+                opcode = map_to_opcode(type, 0);
+                pa_assert(opcode >= 0);
+
+                b->callback(b, opcode, &i, b->userdata);
+            }
+            break;
+        }
+
+        case AVAHI_BROWSER_FAILURE: {
+            handle_failure(b);
+            break;
+        }
+
+        default:
+            ;
+    }
+}
+
+static void client_callback(AvahiClient *s, AvahiClientState state, void *userdata) {
+    pa_browser *b = userdata;
+
+    pa_assert(s);
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) >= 1);
+
+    if (state == AVAHI_CLIENT_FAILURE)
+        handle_failure(b);
+}
+
+static void browser_free(pa_browser *b);
+
+
+PA_WARN_REFERENCE(pa_browser_new, "libpulse-browse is being phased out.");
+
+pa_browser *pa_browser_new(pa_mainloop_api *mainloop) {
+    return pa_browser_new_full(mainloop, PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, NULL);
+}
+
+PA_WARN_REFERENCE(pa_browser_new_full, "libpulse-browse is being phased out.");
+
+pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string) {
+    pa_browser *b;
+    int error;
+
+    pa_assert(mainloop);
+
+    if (flags & ~(PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES) || flags == 0)
+        return NULL;
+
+    b = pa_xnew(pa_browser, 1);
+    b->mainloop = mainloop;
+    PA_REFCNT_INIT(b);
+    b->callback = NULL;
+    b->userdata = NULL;
+    b->error_callback = NULL;
+    b->error_userdata = NULL;
+    b->sink_browser = b->source_browser = b->server_browser = NULL;
+
+    b->avahi_poll = pa_avahi_poll_new(mainloop);
+
+    if (!(b->client = avahi_client_new(b->avahi_poll, 0, client_callback, b, &error))) {
+        if (error_string)
+            *error_string = avahi_strerror(error);
+        goto fail;
+    }
+
+    if ((flags & PA_BROWSE_FOR_SERVERS) &&
+        !(b->server_browser = avahi_service_browser_new(
+                  b->client,
+                  AVAHI_IF_UNSPEC,
+                  AVAHI_PROTO_INET,
+                  SERVICE_TYPE_SERVER,
+                  NULL,
+                  0,
+                  browse_callback,
+                  b))) {
+
+        if (error_string)
+            *error_string = avahi_strerror(avahi_client_errno(b->client));
+        goto fail;
+    }
+
+    if ((flags & PA_BROWSE_FOR_SINKS) &&
+        !(b->sink_browser = avahi_service_browser_new(
+                  b->client,
+                  AVAHI_IF_UNSPEC,
+                  AVAHI_PROTO_UNSPEC,
+                  SERVICE_TYPE_SINK,
+                  NULL,
+                  0,
+                  browse_callback,
+                  b))) {
+
+        if (error_string)
+            *error_string = avahi_strerror(avahi_client_errno(b->client));
+        goto fail;
+    }
+
+    if ((flags & PA_BROWSE_FOR_SOURCES) &&
+        !(b->source_browser = avahi_service_browser_new(
+                  b->client,
+                  AVAHI_IF_UNSPEC,
+                  AVAHI_PROTO_UNSPEC,
+                  SERVICE_TYPE_SOURCE,
+                  NULL,
+                  0,
+                  browse_callback,
+                  b))) {
+
+        if (error_string)
+            *error_string = avahi_strerror(avahi_client_errno(b->client));
+        goto fail;
+    }
+
+    return b;
+
+fail:
+    if (b)
+        browser_free(b);
+
+    return NULL;
+}
+
+static void browser_free(pa_browser *b) {
+    pa_assert(b);
+    pa_assert(b->mainloop);
+
+    if (b->sink_browser)
+        avahi_service_browser_free(b->sink_browser);
+    if (b->source_browser)
+        avahi_service_browser_free(b->source_browser);
+    if (b->server_browser)
+        avahi_service_browser_free(b->server_browser);
+
+    if (b->client)
+        avahi_client_free(b->client);
+
+    if (b->avahi_poll)
+        pa_avahi_poll_free(b->avahi_poll);
+
+    pa_xfree(b);
+}
+
+PA_WARN_REFERENCE(pa_browser_ref, "libpulse-browse is being phased out.");
+
+pa_browser *pa_browser_ref(pa_browser *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) >= 1);
+
+    PA_REFCNT_INC(b);
+    return b;
+}
+
+PA_WARN_REFERENCE(pa_browser_unref, "libpulse-browse is being phased out.");
+
+void pa_browser_unref(pa_browser *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) >= 1);
+
+    if (PA_REFCNT_DEC(b) <= 0)
+        browser_free(b);
+}
+
+PA_WARN_REFERENCE(pa_browser_set_callback, "libpulse-browse is being phased out.");
+
+void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) >= 1);
+
+    b->callback = cb;
+    b->userdata = userdata;
+}
+
+PA_WARN_REFERENCE(pa_browser_set_error_callback, "libpulse-browse is being phased out.");
+
+void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) >= 1);
+
+    b->error_callback = cb;
+    b->error_userdata = userdata;
+}
diff --git a/src/pulse/browser.h b/src/pulse/browser.h
new file mode 100644 (file)
index 0000000..c4e0a17
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef foobrowserhfoo
+#define foobrowserhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/cdecl.h>
+
+/** \file
+ * An abstract interface for Zeroconf browsing of PulseAudio servers */
+
+PA_C_DECL_BEGIN
+
+/** An opaque Zeroconf service browser object */
+typedef struct pa_browser pa_browser;
+
+/** Opcodes for pa_browser_cb_t callbacks */
+typedef enum pa_browse_opcode {
+    PA_BROWSE_NEW_SERVER = 0, /**< New server found */
+    PA_BROWSE_NEW_SINK,       /**< New sink found */
+    PA_BROWSE_NEW_SOURCE,     /**< New source found */
+    PA_BROWSE_REMOVE_SERVER,  /**< Server disappeared */
+    PA_BROWSE_REMOVE_SINK,    /**< Sink disappeared */
+    PA_BROWSE_REMOVE_SOURCE   /**< Source disappeared */
+} pa_browse_opcode_t;
+
+typedef enum pa_browse_flags {
+    PA_BROWSE_FOR_SERVERS = 1, /**< Browse for servers */
+    PA_BROWSE_FOR_SINKS = 2, /**< Browse for sinks */
+    PA_BROWSE_FOR_SOURCES = 4 /** Browse for sources */
+} pa_browse_flags_t;
+
+/** Create a new browser object on the specified main loop */
+pa_browser *pa_browser_new(pa_mainloop_api *mainloop);
+
+/** Same pa_browser_new, but pass additional flags parameter. */
+pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string);
+
+/** Increase reference counter of the specified browser object */
+pa_browser *pa_browser_ref(pa_browser *z);
+
+/** Decrease reference counter of the specified browser object */
+void pa_browser_unref(pa_browser *z);
+
+/** Information about a sink/source/server found with Zeroconf */
+typedef struct pa_browse_info {
+    const char *name;  /**< Unique service name; always available */
+
+    const char *server; /**< Server name; always available */
+    const char *server_version; /**< Server version string; optional */
+    const char *user_name; /**< User name of the server process; optional */
+    const char *fqdn; /* Server version; optional */
+    const uint32_t *cookie;  /* Server cookie; optional */
+
+    const char *device; /* Device name; always available when this information is of a sink/source */
+    const char *description;  /* Device description; optional */
+    const pa_sample_spec *sample_spec;  /* Sample specification of the device; optional */
+} pa_browse_info;
+
+/** Callback prototype */
+typedef void (*pa_browse_cb_t)(pa_browser *z, pa_browse_opcode_t c, const pa_browse_info *i, void *userdata);
+
+/** Set the callback pointer for the browser object */
+void pa_browser_set_callback(pa_browser *z, pa_browse_cb_t cb, void *userdata);
+
+/** Callback prototype for errors */
+typedef void (*pa_browser_error_cb_t)(pa_browser *z, const char *error_string, void *userdata);
+
+/** Set a callback function that is called whenever the browser object
+ * becomes invalid due to an error. After this function has been
+ * called the browser object has become invalid and should be
+ * freed. */
+void pa_browser_set_error_callback(pa_browser *z, pa_browser_error_cb_t, void *userdata);
+
+PA_C_DECL_END
+
+#endif
similarity index 76%
rename from polyp/cdecl.h
rename to src/pulse/cdecl.h
index d51ae026679afd2ed4d94451ec32bb1a037de5bf..8c5b2d0faf9bb6f76e56801d2ab3d6b3a0db5347 100644 (file)
@@ -1,23 +1,23 @@
-#ifndef foocdeclhfoo
-#define foocdeclhfoo
-
-/* $Id$ */
+#ifndef foopulsecdeclhfoo
+#define foopulsecdeclhfoo
 
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
new file mode 100644 (file)
index 0000000..7348b32
--- /dev/null
@@ -0,0 +1,559 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "channelmap.h"
+
+const char *const table[PA_CHANNEL_POSITION_MAX] = {
+    [PA_CHANNEL_POSITION_MONO] = "mono",
+
+    [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
+    [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
+    [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
+
+    [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
+    [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
+    [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
+
+    [PA_CHANNEL_POSITION_LFE] = "lfe",
+
+    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
+    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
+
+    [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
+    [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right",
+
+    [PA_CHANNEL_POSITION_AUX0] = "aux0",
+    [PA_CHANNEL_POSITION_AUX1] = "aux1",
+    [PA_CHANNEL_POSITION_AUX2] = "aux2",
+    [PA_CHANNEL_POSITION_AUX3] = "aux3",
+    [PA_CHANNEL_POSITION_AUX4] = "aux4",
+    [PA_CHANNEL_POSITION_AUX5] = "aux5",
+    [PA_CHANNEL_POSITION_AUX6] = "aux6",
+    [PA_CHANNEL_POSITION_AUX7] = "aux7",
+    [PA_CHANNEL_POSITION_AUX8] = "aux8",
+    [PA_CHANNEL_POSITION_AUX9] = "aux9",
+    [PA_CHANNEL_POSITION_AUX10] = "aux10",
+    [PA_CHANNEL_POSITION_AUX11] = "aux11",
+    [PA_CHANNEL_POSITION_AUX12] = "aux12",
+    [PA_CHANNEL_POSITION_AUX13] = "aux13",
+    [PA_CHANNEL_POSITION_AUX14] = "aux14",
+    [PA_CHANNEL_POSITION_AUX15] = "aux15",
+    [PA_CHANNEL_POSITION_AUX16] = "aux16",
+    [PA_CHANNEL_POSITION_AUX17] = "aux17",
+    [PA_CHANNEL_POSITION_AUX18] = "aux18",
+    [PA_CHANNEL_POSITION_AUX19] = "aux19",
+    [PA_CHANNEL_POSITION_AUX20] = "aux20",
+    [PA_CHANNEL_POSITION_AUX21] = "aux21",
+    [PA_CHANNEL_POSITION_AUX22] = "aux22",
+    [PA_CHANNEL_POSITION_AUX23] = "aux23",
+    [PA_CHANNEL_POSITION_AUX24] = "aux24",
+    [PA_CHANNEL_POSITION_AUX25] = "aux25",
+    [PA_CHANNEL_POSITION_AUX26] = "aux26",
+    [PA_CHANNEL_POSITION_AUX27] = "aux27",
+    [PA_CHANNEL_POSITION_AUX28] = "aux28",
+    [PA_CHANNEL_POSITION_AUX29] = "aux29",
+    [PA_CHANNEL_POSITION_AUX30] = "aux30",
+    [PA_CHANNEL_POSITION_AUX31] = "aux31",
+
+    [PA_CHANNEL_POSITION_TOP_CENTER] = "top-center",
+
+    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center",
+    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left",
+    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right",
+
+    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center",
+    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left",
+    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right"
+};
+
+const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = {
+    [PA_CHANNEL_POSITION_MONO] = "Mono",
+
+    [PA_CHANNEL_POSITION_FRONT_CENTER] = "Front Center",
+    [PA_CHANNEL_POSITION_FRONT_LEFT] = "Front Left",
+    [PA_CHANNEL_POSITION_FRONT_RIGHT] = "Front Right",
+
+    [PA_CHANNEL_POSITION_REAR_CENTER] = "Rear Center",
+    [PA_CHANNEL_POSITION_REAR_LEFT] = "Rear Left",
+    [PA_CHANNEL_POSITION_REAR_RIGHT] = "Rear Right",
+
+    [PA_CHANNEL_POSITION_LFE] = "Low Frequency Emmiter",
+
+    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "Front Left-of-center",
+    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "Front Right-of-center",
+
+    [PA_CHANNEL_POSITION_SIDE_LEFT] = "Side Left",
+    [PA_CHANNEL_POSITION_SIDE_RIGHT] = "Side Right",
+
+    [PA_CHANNEL_POSITION_AUX0] = "Auxiliary 0",
+    [PA_CHANNEL_POSITION_AUX1] = "Auxiliary 1",
+    [PA_CHANNEL_POSITION_AUX2] = "Auxiliary 2",
+    [PA_CHANNEL_POSITION_AUX3] = "Auxiliary 3",
+    [PA_CHANNEL_POSITION_AUX4] = "Auxiliary 4",
+    [PA_CHANNEL_POSITION_AUX5] = "Auxiliary 5",
+    [PA_CHANNEL_POSITION_AUX6] = "Auxiliary 6",
+    [PA_CHANNEL_POSITION_AUX7] = "Auxiliary 7",
+    [PA_CHANNEL_POSITION_AUX8] = "Auxiliary 8",
+    [PA_CHANNEL_POSITION_AUX9] = "Auxiliary 9",
+    [PA_CHANNEL_POSITION_AUX10] = "Auxiliary 10",
+    [PA_CHANNEL_POSITION_AUX11] = "Auxiliary 11",
+    [PA_CHANNEL_POSITION_AUX12] = "Auxiliary 12",
+    [PA_CHANNEL_POSITION_AUX13] = "Auxiliary 13",
+    [PA_CHANNEL_POSITION_AUX14] = "Auxiliary 14",
+    [PA_CHANNEL_POSITION_AUX15] = "Auxiliary 15",
+    [PA_CHANNEL_POSITION_AUX16] = "Auxiliary 16",
+    [PA_CHANNEL_POSITION_AUX17] = "Auxiliary 17",
+    [PA_CHANNEL_POSITION_AUX18] = "Auxiliary 18",
+    [PA_CHANNEL_POSITION_AUX19] = "Auxiliary 19",
+    [PA_CHANNEL_POSITION_AUX20] = "Auxiliary 20",
+    [PA_CHANNEL_POSITION_AUX21] = "Auxiliary 21",
+    [PA_CHANNEL_POSITION_AUX22] = "Auxiliary 22",
+    [PA_CHANNEL_POSITION_AUX23] = "Auxiliary 23",
+    [PA_CHANNEL_POSITION_AUX24] = "Auxiliary 24",
+    [PA_CHANNEL_POSITION_AUX25] = "Auxiliary 25",
+    [PA_CHANNEL_POSITION_AUX26] = "Auxiliary 26",
+    [PA_CHANNEL_POSITION_AUX27] = "Auxiliary 27",
+    [PA_CHANNEL_POSITION_AUX28] = "Auxiliary 28",
+    [PA_CHANNEL_POSITION_AUX29] = "Auxiliary 29",
+    [PA_CHANNEL_POSITION_AUX30] = "Auxiliary 30",
+    [PA_CHANNEL_POSITION_AUX31] = "Auxiliary 31",
+
+    [PA_CHANNEL_POSITION_TOP_CENTER] = "Top Center",
+
+    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "Top Front Center",
+    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "Top Front Left",
+    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "Top Front Right",
+
+    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "Top Rear Center",
+    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "Top Rear left",
+    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "Top Rear Right"
+};
+
+pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
+    unsigned c;
+    pa_assert(m);
+
+    m->channels = 0;
+
+    for (c = 0; c < PA_CHANNELS_MAX; c++)
+        m->map[c] = PA_CHANNEL_POSITION_INVALID;
+
+    return m;
+}
+
+pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {
+    pa_assert(m);
+
+    pa_channel_map_init(m);
+
+    m->channels = 1;
+    m->map[0] = PA_CHANNEL_POSITION_MONO;
+    return m;
+}
+
+pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
+    pa_assert(m);
+
+    pa_channel_map_init(m);
+
+    m->channels = 2;
+    m->map[0] = PA_CHANNEL_POSITION_LEFT;
+    m->map[1] = PA_CHANNEL_POSITION_RIGHT;
+    return m;
+}
+
+pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
+    pa_assert(m);
+    pa_assert(channels > 0);
+    pa_assert(channels <= PA_CHANNELS_MAX);
+
+    pa_channel_map_init(m);
+
+    m->channels = channels;
+
+    switch (def) {
+        case PA_CHANNEL_MAP_AIFF:
+
+            /* This is somewhat compatible with RFC3551 */
+
+            switch (channels) {
+                case 1:
+                    m->map[0] = PA_CHANNEL_POSITION_MONO;
+                    return m;
+
+                case 6:
+                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_SIDE_LEFT;
+                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+                    m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+                    m->map[4] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+                    m->map[5] = PA_CHANNEL_POSITION_LFE;
+                    return m;
+
+                case 5:
+                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+                    m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
+                    m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
+                    /* Fall through */
+
+                case 2:
+                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+                    return m;
+
+                case 3:
+                    m->map[0] = PA_CHANNEL_POSITION_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_RIGHT;
+                    m->map[2] = PA_CHANNEL_POSITION_CENTER;
+                    return m;
+
+                case 4:
+                    m->map[0] = PA_CHANNEL_POSITION_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_CENTER;
+                    m->map[2] = PA_CHANNEL_POSITION_RIGHT;
+                    m->map[3] = PA_CHANNEL_POSITION_LFE;
+                    return m;
+
+                default:
+                    return NULL;
+            }
+
+        case PA_CHANNEL_MAP_ALSA:
+
+            switch (channels) {
+                case 1:
+                    m->map[0] = PA_CHANNEL_POSITION_MONO;
+                    return m;
+
+                case 8:
+                    m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+                    m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+                    /* Fall through */
+
+                case 6:
+                    m->map[5] = PA_CHANNEL_POSITION_LFE;
+                    /* Fall through */
+
+                case 5:
+                    m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+                    /* Fall through */
+
+                case 4:
+                    m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+                    m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+                    /* Fall through */
+
+                case 2:
+                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+                    return m;
+
+                default:
+                    return NULL;
+            }
+
+        case PA_CHANNEL_MAP_AUX: {
+            unsigned i;
+
+            if (channels >= PA_CHANNELS_MAX)
+                return NULL;
+
+            for (i = 0; i < channels; i++)
+                m->map[i] = PA_CHANNEL_POSITION_AUX0 + i;
+
+            return m;
+        }
+
+        case PA_CHANNEL_MAP_WAVEEX:
+
+            switch (channels) {
+                case 1:
+                    m->map[0] = PA_CHANNEL_POSITION_MONO;
+                    return m;
+
+                case 18:
+                    m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+                    m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+                    m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+                    /* Fall through */
+
+                case 15:
+                    m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
+                    m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
+                    m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
+                    /* Fall through */
+
+                case 12:
+                    m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER;
+                    /* Fall through */
+
+                case 11:
+                    m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT;
+                    m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+                    /* Fall through */
+
+                case 9:
+                    m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER;
+                    /* Fall through */
+
+                case 8:
+                    m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+                    m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+                    /* Fall through */
+
+                case 6:
+                    m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
+                    m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
+                    /* Fall through */
+
+                case 4:
+                    m->map[3] = PA_CHANNEL_POSITION_LFE;
+                    /* Fall through */
+
+                case 3:
+                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+                    /* Fall through */
+
+                case 2:
+                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+                    return m;
+
+                default:
+                    return NULL;
+            }
+
+        case PA_CHANNEL_MAP_OSS:
+
+            switch (channels) {
+                case 1:
+                    m->map[0] = PA_CHANNEL_POSITION_MONO;
+                    return m;
+
+                case 8:
+                    m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT;
+                    m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT;
+                    /* Fall through */
+
+                case 6:
+                    m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT;
+                    m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+                    /* Fall through */
+
+                case 4:
+                    m->map[3] = PA_CHANNEL_POSITION_LFE;
+                    /* Fall through */
+
+                case 3:
+                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+                    /* Fall through */
+
+                case 2:
+                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+                    return m;
+
+                default:
+                    return NULL;
+            }
+
+
+        default:
+            return NULL;
+    }
+}
+
+pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
+    unsigned c;
+
+    pa_assert(m);
+    pa_assert(channels > 0);
+    pa_assert(channels <= PA_CHANNELS_MAX);
+
+    pa_channel_map_init(m);
+
+    for (c = channels; c > 0; c--) {
+
+        if (pa_channel_map_init_auto(m, c, def)) {
+            unsigned i = 0;
+
+            for (; c < channels; c++) {
+
+                m->map[c] = PA_CHANNEL_POSITION_AUX0 + i;
+                i++;
+            }
+
+            m->channels = channels;
+
+            return m;
+        }
+    }
+
+    return NULL;
+}
+
+const char* pa_channel_position_to_string(pa_channel_position_t pos) {
+
+    if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
+        return NULL;
+
+    return table[pos];
+}
+
+const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) {
+    if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
+        return NULL;
+
+    return pretty_table[pos];
+}
+
+int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
+    unsigned c;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    if (a->channels != b->channels)
+        return 0;
+
+    for (c = 0; c < a->channels; c++)
+        if (a->map[c] != b->map[c])
+            return 0;
+
+    return 1;
+}
+
+char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
+    unsigned channel;
+    int first = 1;
+    char *e;
+
+    pa_assert(s);
+    pa_assert(l > 0);
+    pa_assert(map);
+
+    *(e = s) = 0;
+
+    for (channel = 0; channel < map->channels && l > 1; channel++) {
+        l -= pa_snprintf(e, l, "%s%s",
+                      first ? "" : ",",
+                      pa_channel_position_to_string(map->map[channel]));
+
+        e = strchr(e, 0);
+        first = 0;
+    }
+
+    return s;
+}
+
+pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
+    const char *state;
+    pa_channel_map map;
+    char *p;
+
+    pa_assert(rmap);
+    pa_assert(s);
+
+    memset(&map, 0, sizeof(map));
+
+    if (strcmp(s, "stereo") == 0) {
+        map.channels = 2;
+        map.map[0] = PA_CHANNEL_POSITION_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_RIGHT;
+        goto finish;
+    }
+
+    state = NULL;
+    map.channels = 0;
+
+    while ((p = pa_split(s, ",", &state))) {
+
+        if (map.channels >= PA_CHANNELS_MAX) {
+            pa_xfree(p);
+            return NULL;
+        }
+
+        /* Some special aliases */
+        if (strcmp(p, "left") == 0)
+            map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT;
+        else if (strcmp(p, "right") == 0)
+            map.map[map.channels++] = PA_CHANNEL_POSITION_RIGHT;
+        else if (strcmp(p, "center") == 0)
+            map.map[map.channels++] = PA_CHANNEL_POSITION_CENTER;
+        else if (strcmp(p, "subwoofer") == 0)
+            map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER;
+        else {
+            pa_channel_position_t i;
+
+            for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
+                if (strcmp(p, table[i]) == 0) {
+                    map.map[map.channels++] = i;
+                    break;
+                }
+
+            if (i >= PA_CHANNEL_POSITION_MAX) {
+                pa_xfree(p);
+                return NULL;
+            }
+        }
+
+        pa_xfree(p);
+    }
+
+finish:
+
+    if (!pa_channel_map_valid(&map))
+        return NULL;
+
+    *rmap = map;
+    return rmap;
+}
+
+int pa_channel_map_valid(const pa_channel_map *map) {
+    unsigned c;
+
+    pa_assert(map);
+
+    if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX)
+        return 0;
+
+    for (c = 0; c < map->channels; c++) {
+
+        if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX)
+            return 0;
+
+    }
+
+    return 1;
+}
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
new file mode 100644 (file)
index 0000000..2551eae
--- /dev/null
@@ -0,0 +1,204 @@
+#ifndef foochannelmaphfoo
+#define foochannelmaphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+
+/** \page channelmap Channel Maps
+ *
+ * \section overv_sec Overview
+ *
+ * Channel maps provide a way to associate channels in a stream with a
+ * specific speaker position. This relieves applications of having to
+ * make sure their channel order is identical to the final output.
+ *
+ * \section init_sec Initialisation
+ *
+ * A channel map consists of an array of \ref pa_channel_position values,
+ * one for each channel. This array is stored together with a channel count
+ * in a pa_channel_map structure.
+ *
+ * Before filling the structure, the application must initialise it using
+ * pa_channel_map_init(). There are also a number of convenience functions
+ * for standard channel mappings:
+ *
+ * \li pa_channel_map_init_mono() - Create a channel map with only mono audio.
+ * \li pa_channel_map_init_stereo() - Create a standard stereo mapping.
+ * \li pa_channel_map_init_auto() - Create a standard channel map for up to
+ *                                  six channels.
+ *
+ * \section conv_sec Convenience Functions
+ *
+ * The library contains a number of convenience functions for dealing with
+ * channel maps:
+ *
+ * \li pa_channel_map_valid() - Tests if a channel map is valid.
+ * \li pa_channel_map_equal() - Tests if two channel maps are identical.
+ * \li pa_channel_map_snprint() - Creates a textual description of a channel
+ *                                map.
+ */
+
+/** \file
+ * Constants and routines for channel mapping handling */
+
+PA_C_DECL_BEGIN
+
+/** A list of channel labels */
+typedef enum pa_channel_position {
+    PA_CHANNEL_POSITION_INVALID = -1,
+    PA_CHANNEL_POSITION_MONO = 0,
+
+    PA_CHANNEL_POSITION_LEFT,
+    PA_CHANNEL_POSITION_RIGHT,
+    PA_CHANNEL_POSITION_CENTER,
+
+    PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT,
+    PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT,
+    PA_CHANNEL_POSITION_FRONT_CENTER = PA_CHANNEL_POSITION_CENTER,
+
+    PA_CHANNEL_POSITION_REAR_CENTER,
+    PA_CHANNEL_POSITION_REAR_LEFT,
+    PA_CHANNEL_POSITION_REAR_RIGHT,
+
+    PA_CHANNEL_POSITION_LFE,
+    PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE,
+
+    PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+    PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+
+    PA_CHANNEL_POSITION_SIDE_LEFT,
+    PA_CHANNEL_POSITION_SIDE_RIGHT,
+
+    PA_CHANNEL_POSITION_AUX0,
+    PA_CHANNEL_POSITION_AUX1,
+    PA_CHANNEL_POSITION_AUX2,
+    PA_CHANNEL_POSITION_AUX3,
+    PA_CHANNEL_POSITION_AUX4,
+    PA_CHANNEL_POSITION_AUX5,
+    PA_CHANNEL_POSITION_AUX6,
+    PA_CHANNEL_POSITION_AUX7,
+    PA_CHANNEL_POSITION_AUX8,
+    PA_CHANNEL_POSITION_AUX9,
+    PA_CHANNEL_POSITION_AUX10,
+    PA_CHANNEL_POSITION_AUX11,
+    PA_CHANNEL_POSITION_AUX12,
+    PA_CHANNEL_POSITION_AUX13,
+    PA_CHANNEL_POSITION_AUX14,
+    PA_CHANNEL_POSITION_AUX15,
+    PA_CHANNEL_POSITION_AUX16,
+    PA_CHANNEL_POSITION_AUX17,
+    PA_CHANNEL_POSITION_AUX18,
+    PA_CHANNEL_POSITION_AUX19,
+    PA_CHANNEL_POSITION_AUX20,
+    PA_CHANNEL_POSITION_AUX21,
+    PA_CHANNEL_POSITION_AUX22,
+    PA_CHANNEL_POSITION_AUX23,
+    PA_CHANNEL_POSITION_AUX24,
+    PA_CHANNEL_POSITION_AUX25,
+    PA_CHANNEL_POSITION_AUX26,
+    PA_CHANNEL_POSITION_AUX27,
+    PA_CHANNEL_POSITION_AUX28,
+    PA_CHANNEL_POSITION_AUX29,
+    PA_CHANNEL_POSITION_AUX30,
+    PA_CHANNEL_POSITION_AUX31,
+
+    PA_CHANNEL_POSITION_TOP_CENTER,
+
+    PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
+    PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
+    PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
+
+    PA_CHANNEL_POSITION_TOP_REAR_LEFT,
+    PA_CHANNEL_POSITION_TOP_REAR_RIGHT,
+    PA_CHANNEL_POSITION_TOP_REAR_CENTER,
+
+    PA_CHANNEL_POSITION_MAX
+} pa_channel_position_t;
+
+/** A list of channel mapping definitions for pa_channel_map_init_auto() */
+typedef enum pa_channel_map_def {
+    PA_CHANNEL_MAP_AIFF,   /**< The mapping from RFC3551, which is based on AIFF-C */
+    PA_CHANNEL_MAP_ALSA,   /**< The default mapping used by ALSA */
+    PA_CHANNEL_MAP_AUX,    /**< Only aux channels */
+    PA_CHANNEL_MAP_WAVEEX, /**< Microsoft's WAVEFORMATEXTENSIBLE mapping */
+    PA_CHANNEL_MAP_OSS,    /**< The default channel mapping used by OSS as defined in the OSS 4.0 API specs */
+
+    PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF /**< The default channel map */
+} pa_channel_map_def_t;
+
+/** A channel map which can be used to attach labels to specific
+ * channels of a stream. These values are relevant for conversion and
+ * mixing of streams */
+typedef struct pa_channel_map {
+    uint8_t channels; /**< Number of channels */
+    pa_channel_position_t map[PA_CHANNELS_MAX]; /**< Channel labels */
+} pa_channel_map;
+
+/** Initialize the specified channel map and return a pointer to it */
+pa_channel_map* pa_channel_map_init(pa_channel_map *m);
+
+/** Initialize the specified channel map for monoaural audio and return a pointer to it */
+pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m);
+
+/** Initialize the specified channel map for stereophonic audio and return a pointer to it */
+pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m);
+
+/** Initialize the specified channel map for the specified number of
+ * channels using default labels and return a pointer to it. This call
+ * will fail (return NULL) if there is no default channel map known for this
+ * specific number of channels and mapping. */
+pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def);
+
+/** Similar to pa_channel_map_init_auto() but instead of failing if no
+ * default mapping is known with the specified parameters it will
+ * synthesize a mapping based on a known mapping with fewer channels
+ * and fill up the rest with AUX0...AUX31 channels  \since 0.9.11 */
+pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def);
+
+/** Return a text label for the specified channel position */
+const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE;
+
+/** Return a human readable text label for the specified channel position. \since 0.9.7 */
+const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos);
+
+/** The maximum length of strings returned by pa_channel_map_snprint() */
+#define PA_CHANNEL_MAP_SNPRINT_MAX 336
+
+/** Make a humand readable string from the specified channel map */
+char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);
+
+/** Parse a channel position list into a channel map structure. */
+pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s);
+
+/** Compare two channel maps. Return 1 if both match. */
+int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) PA_GCC_PURE;
+
+/** Return non-zero of the specified channel map is considered valid */
+int pa_channel_map_valid(const pa_channel_map *map) PA_GCC_PURE;
+
+PA_C_DECL_END
+
+#endif
similarity index 58%
rename from polyp/client-conf-x11.c
rename to src/pulse/client-conf-x11.c
index 383aa64e71d008ecca05532b5c4b3039162ea032..393a7cd38ff70fce9690131669ea4750063b2e9a 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
 
-  polypaudio is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
 
-  polypaudio is distributed in the hope that it will be useful, but
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-13071
   USA.
 ***/
 #endif
 
 #include <string.h>
-#include <assert.h>
 
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
 
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/x11prop.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
 #include "client-conf-x11.h"
-#include "x11prop.h"
-#include "log.h"
-#include "xmalloc.h"
-#include "util.h"
 
-int pa_client_conf_from_x11(struct pa_client_conf *c, const char *dname) {
+int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) {
     Display *d = NULL;
     int ret = -1;
     char t[1024];
 
-    if (!dname && !getenv("DISPLAY"))
+    pa_assert(c);
+
+    if (!dname && !(dname = getenv("DISPLAY")))
         goto finish;
-    
+
+    if (*dname == 0)
+        goto finish;
+
     if (!(d = XOpenDisplay(dname))) {
-        pa_log(__FILE__": XOpenDisplay() failed\n");
+        pa_log("XOpenDisplay() failed");
         goto finish;
     }
 
-    if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) {
+    if (pa_x11_get_prop(d, "PULSE_SERVER", t, sizeof(t))) {
         pa_xfree(c->default_server);
         c->default_server = pa_xstrdup(t);
     }
 
-    if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) {
+    if (pa_x11_get_prop(d, "PULSE_SINK", t, sizeof(t))) {
         pa_xfree(c->default_sink);
         c->default_sink = pa_xstrdup(t);
     }
 
-    if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) {
+    if (pa_x11_get_prop(d, "PULSE_SOURCE", t, sizeof(t))) {
         pa_xfree(c->default_source);
         c->default_source = pa_xstrdup(t);
     }
 
-    if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) {
+    if (pa_x11_get_prop(d, "PULSE_COOKIE", t, sizeof(t))) {
         uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
 
         if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie)) {
-            pa_log(__FILE__": failed to parse cookie data\n");
+            pa_log("failed to parse cookie data");
             goto finish;
         }
 
-        assert(sizeof(cookie) == sizeof(c->cookie));
+        pa_assert(sizeof(cookie) == sizeof(c->cookie));
         memcpy(c->cookie, cookie, sizeof(cookie));
 
-        c->cookie_valid = 1;
+        c->cookie_valid = TRUE;
 
         pa_xfree(c->cookie_file);
         c->cookie_file = NULL;
@@ -87,5 +94,5 @@ finish:
         XCloseDisplay(d);
 
     return ret;
-    
+
 }
similarity index 68%
rename from polyp/client-conf-x11.h
rename to src/pulse/client-conf-x11.h
index e65e8202b89decfe075ce822b7eec6e879ced7a0..f2f40e0358f12a9078599e3ad87b1f1cf6760e24 100644 (file)
@@ -1,23 +1,23 @@
 #ifndef fooclientconfx11hfoo
 #define fooclientconfx11hfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
 
-  polypaudio is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
 
-  polypaudio is distributed in the hope that it will be useful, but
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
@@ -26,6 +26,6 @@
 
 /* Load client configuration data from the specified X11 display,
  * overwriting the current settings in *c */
-int pa_client_conf_from_x11(struct pa_client_conf *c, const char *display);
+int pa_client_conf_from_x11(pa_client_conf *c, const char *display);
 
 #endif
similarity index 59%
rename from polyp/client-conf.c
rename to src/pulse/client-conf.c
index 4906383d1efdb25ee24532cacdccf34fa71859a5..2ead871f4a5d3fe01b33e6edf740ad058f769801 100644 (file)
@@ -1,74 +1,79 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
 
-  polypaudio is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
 
-  polypaudio is distributed in the hope that it will be useful, but
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <stdlib.h>
-#include <assert.h>
 #include <unistd.h>
 #include <errno.h>
 #include <string.h>
 
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/conf-parser.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/authkey.h>
+
 #include "client-conf.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "conf-parser.h"
-#include "util.h"
-#include "authkey.h"
-
-#ifndef DEFAULT_CONFIG_DIR
-#define DEFAULT_CONFIG_DIR "/etc/polypaudio"
-#endif
 
-#define DEFAULT_CLIENT_CONFIG_FILE DEFAULT_CONFIG_DIR"/client.conf"
-#define DEFAULT_CLIENT_CONFIG_FILE_USER ".polypaudio/client.conf"
+#define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "client.conf"
+#define DEFAULT_CLIENT_CONFIG_FILE_USER "client.conf"
 
-#define ENV_CLIENT_CONFIG_FILE "POLYP_CLIENTCONFIG"
-#define ENV_DEFAULT_SINK "POLYP_SINK"
-#define ENV_DEFAULT_SOURCE "POLYP_SOURCE"
-#define ENV_DEFAULT_SERVER "POLYP_SERVER"
-#define ENV_DAEMON_BINARY "POLYP_BINARY"
-#define ENV_COOKIE_FILE "POLYP_COOKIE"
+#define ENV_CLIENT_CONFIG_FILE "PULSE_CLIENTCONFIG"
+#define ENV_DEFAULT_SINK "PULSE_SINK"
+#define ENV_DEFAULT_SOURCE "PULSE_SOURCE"
+#define ENV_DEFAULT_SERVER "PULSE_SERVER"
+#define ENV_DAEMON_BINARY "PULSE_BINARY"
+#define ENV_COOKIE_FILE "PULSE_COOKIE"
 
-static const struct pa_client_conf default_conf = {
+static const pa_client_conf default_conf = {
     .daemon_binary = NULL,
     .extra_arguments = NULL,
     .default_sink = NULL,
     .default_source = NULL,
     .default_server = NULL,
-    .autospawn = 0,
+    .autospawn = TRUE,
+    .disable_shm = FALSE,
     .cookie_file = NULL,
-    .cookie_valid = 0
+    .cookie_valid = FALSE,
 };
 
-struct pa_client_conf *pa_client_conf_new(void) {
-    struct pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
-    
-    c->daemon_binary = pa_xstrdup(POLYPAUDIO_BINARY);
+pa_client_conf *pa_client_conf_new(void) {
+    pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
+
+    c->daemon_binary = pa_xstrdup(PA_BINARY);
     c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5");
     c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE);
-    
+
     return c;
 }
 
-void pa_client_conf_free(struct pa_client_conf *c) {
-    assert(c);
+void pa_client_conf_free(pa_client_conf *c) {
+    pa_assert(c);
     pa_xfree(c->daemon_binary);
     pa_xfree(c->extra_arguments);
     pa_xfree(c->default_sink);
@@ -77,13 +82,14 @@ void pa_client_conf_free(struct pa_client_conf *c) {
     pa_xfree(c->cookie_file);
     pa_xfree(c);
 }
-int pa_client_conf_load(struct pa_client_conf *c, const char *filename) {
+
+int pa_client_conf_load(pa_client_conf *c, const char *filename) {
     FILE *f = NULL;
     char *fn = NULL;
     int r = -1;
 
     /* Prepare the configuration parse table */
-    struct pa_config_item table[] = {
+    pa_config_item table[] = {
         { "daemon-binary",          pa_config_parse_string,  NULL },
         { "extra-arguments",        pa_config_parse_string,  NULL },
         { "default-sink",           pa_config_parse_string,  NULL },
@@ -91,6 +97,7 @@ int pa_client_conf_load(struct pa_client_conf *c, const char *filename) {
         { "default-server",         pa_config_parse_string,  NULL },
         { "autospawn",              pa_config_parse_bool,    NULL },
         { "cookie-file",            pa_config_parse_string,  NULL },
+        { "disable-shm",            pa_config_parse_bool,    NULL },
         { NULL,                     NULL,                    NULL },
     };
 
@@ -101,34 +108,41 @@ int pa_client_conf_load(struct pa_client_conf *c, const char *filename) {
     table[4].data = &c->default_server;
     table[5].data = &c->autospawn;
     table[6].data = &c->cookie_file;
+    table[7].data = &c->disable_shm;
+
+    if (filename) {
 
-    f = filename ?
-        fopen((fn = pa_xstrdup(filename)), "r") :
-        pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn);
+        if (!(f = fopen(filename, "r"))) {
+            pa_log("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+            goto finish;
+        }
 
-    if (!f && errno != EINTR) {
-        pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno));
-        goto finish;
+        fn = pa_xstrdup(fn);
+
+    } else {
+
+        if (!(f = pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn)))
+            if (errno != ENOENT)
+                goto finish;
     }
-    
+
     r = f ? pa_config_parse(fn, f, table, NULL) : 0;
 
     if (!r)
         r = pa_client_conf_load_cookie(c);
-    
 
 finish:
     pa_xfree(fn);
 
     if (f)
         fclose(f);
-    
+
     return r;
 }
 
-int pa_client_conf_env(struct pa_client_conf *c) {
+int pa_client_conf_env(pa_client_conf *c) {
     char *e;
-    
+
     if ((e = getenv(ENV_DEFAULT_SINK))) {
         pa_xfree(c->default_sink);
         c->default_sink = pa_xstrdup(e);
@@ -143,7 +157,7 @@ int pa_client_conf_env(struct pa_client_conf *c) {
         pa_xfree(c->default_server);
         c->default_server = pa_xstrdup(e);
     }
-    
+
     if ((e = getenv(ENV_DAEMON_BINARY))) {
         pa_xfree(c->daemon_binary);
         c->daemon_binary = pa_xstrdup(e);
@@ -155,22 +169,21 @@ int pa_client_conf_env(struct pa_client_conf *c) {
 
         return pa_client_conf_load_cookie(c);
     }
-    
+
     return 0;
 }
 
-int pa_client_conf_load_cookie(struct pa_client_conf* c) {
-    assert(c);
-
-    c->cookie_valid = 0;
+int pa_client_conf_load_cookie(pa_client_conf* c) {
+    pa_assert(c);
 
     if (!c->cookie_file)
         return -1;
 
+    c->cookie_valid = FALSE;
+
     if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0)
         return -1;
 
-    c->cookie_valid = 1;
+    c->cookie_valid = TRUE;
     return 0;
 }
-
similarity index 60%
rename from polyp/client-conf.h
rename to src/pulse/client-conf.h
index b462503938abf17db47620689a8afe9399d05695..699279aaba161a4e6efd62913a1c54571588a905 100644 (file)
@@ -1,52 +1,52 @@
 #ifndef fooclientconfhfoo
 #define fooclientconfhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
 
-  polypaudio is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
 
-  polypaudio is distributed in the hope that it will be useful, but
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "native-common.h"
+#include <pulsecore/native-common.h>
 
-/* A structure containing configuration data for polypaudio clients. */
+/* A structure containing configuration data for PulseAudio clients. */
 
-struct pa_client_conf {
+typedef struct pa_client_conf {
     char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file;
-    int autospawn;
+    pa_bool_t autospawn, disable_shm;
     uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
-    int cookie_valid; /* non-zero, when cookie is valid */
-};
+    pa_bool_t cookie_valid; /* non-zero, when cookie is valid */
+} pa_client_conf;
 
 /* Create a new configuration data object and reset it to defaults */
-struct pa_client_conf *pa_client_conf_new(void);
-void pa_client_conf_free(struct pa_client_conf *c);
+pa_client_conf *pa_client_conf_new(void);
+void pa_client_conf_free(pa_client_conf *c);
 
 /* Load the configuration data from the speicified file, overwriting
  * the current settings in *c. When the filename is NULL, the
  * default client configuration file name is used. */
-int pa_client_conf_load(struct pa_client_conf *c, const char *filename);
+int pa_client_conf_load(pa_client_conf *c, const char *filename);
 
 /* Load the configuration data from the environment of the current
    process, overwriting the current settings in *c. */
-int pa_client_conf_env(struct pa_client_conf *c);
+int pa_client_conf_env(pa_client_conf *c);
 
 /* Load cookie data from c->cookie_file into c->cookie */
-int pa_client_conf_load_cookie(struct pa_client_conf* c);
+int pa_client_conf_load_cookie(pa_client_conf* c);
 
 #endif
diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in
new file mode 100644 (file)
index 0000000..749e968
--- /dev/null
@@ -0,0 +1,32 @@
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+## Configuration file for PulseAudio clients. See pulse-client.conf(5) for
+## more information. Default values a commented out.  Use either ; or # for
+## commenting.
+
+; default-sink =
+; default-source =
+; default-server =
+
+; autospawn = yes
+; daemon-binary = @PA_BINARY@
+; extra-arguments = --log-target=syslog --exit-idle-time=5
+
+; cookie-file =
+
+; disable-shm = no
diff --git a/src/pulse/context.c b/src/pulse/context.c
new file mode 100644 (file)
index 0000000..f56cb24
--- /dev/null
@@ -0,0 +1,1232 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <locale.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include <pulse/version.h>
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+#include <pulse/util.h>
+
+#include <pulsecore/winsock.h>
+#include <pulsecore/core-error.h>
+
+#include <pulsecore/native-common.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/dynarray.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/proplist-util.h>
+
+#include "internal.h"
+
+#include "client-conf.h"
+
+#ifdef HAVE_X11
+#include "client-conf-x11.h"
+#endif
+
+#include "context.h"
+
+#define AUTOSPAWN_LOCK "autospawn.lock"
+
+static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
+    [PA_COMMAND_REQUEST] = pa_command_request,
+    [PA_COMMAND_OVERFLOW] = pa_command_overflow_or_underflow,
+    [PA_COMMAND_UNDERFLOW] = pa_command_overflow_or_underflow,
+    [PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed,
+    [PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed,
+    [PA_COMMAND_PLAYBACK_STREAM_MOVED] = pa_command_stream_moved,
+    [PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved,
+    [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
+    [PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
+    [PA_COMMAND_STARTED] = pa_command_stream_started,
+    [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
+};
+
+static void unlock_autospawn_lock_file(pa_context *c) {
+    pa_assert(c);
+
+    if (c->autospawn_lock_fd >= 0) {
+        char *lf;
+
+        if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
+            pa_log_warn("Cannot unlock autospawn because runtime path is no more.");
+
+        pa_unlock_lockfile(lf, c->autospawn_lock_fd);
+        pa_xfree(lf);
+
+        c->autospawn_lock_fd = -1;
+    }
+}
+
+static void context_free(pa_context *c);
+
+pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
+    return pa_context_new_with_proplist(mainloop, name, NULL);
+}
+
+static void reset_callbacks(pa_context *c) {
+    pa_assert(c);
+
+    c->state_callback = NULL;
+    c->state_userdata = NULL;
+
+    c->subscribe_callback = NULL;
+    c->subscribe_userdata = NULL;
+}
+
+pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
+    pa_context *c;
+
+    pa_assert(mainloop);
+
+    if (!name && !pa_proplist_contains(p, PA_PROP_APPLICATION_NAME))
+        return NULL;
+
+    c = pa_xnew(pa_context, 1);
+    PA_REFCNT_INIT(c);
+
+    c->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
+
+    if (name)
+        pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+
+    c->mainloop = mainloop;
+    c->client = NULL;
+    c->pstream = NULL;
+    c->pdispatch = NULL;
+    c->playback_streams = pa_dynarray_new();
+    c->record_streams = pa_dynarray_new();
+    c->client_index = PA_INVALID_INDEX;
+
+    PA_LLIST_HEAD_INIT(pa_stream, c->streams);
+    PA_LLIST_HEAD_INIT(pa_operation, c->operations);
+
+    c->error = PA_OK;
+    c->state = PA_CONTEXT_UNCONNECTED;
+    c->ctag = 0;
+    c->csyncid = 0;
+
+    reset_callbacks(c);
+
+    c->is_local = FALSE;
+    c->server_list = NULL;
+    c->server = NULL;
+    c->autospawn_lock_fd = -1;
+    memset(&c->spawn_api, 0, sizeof(c->spawn_api));
+    c->do_autospawn = FALSE;
+    c->do_shm = FALSE;
+
+#ifndef MSG_NOSIGNAL
+#ifdef SIGPIPE
+    pa_check_signal_is_blocked(SIGPIPE);
+#endif
+#endif
+
+    c->conf = pa_client_conf_new();
+#ifdef HAVE_X11
+    pa_client_conf_from_x11(c->conf, NULL);
+#endif
+    pa_client_conf_load(c->conf, NULL);
+    pa_client_conf_env(c->conf);
+
+    if (!(c->mempool = pa_mempool_new(!c->conf->disable_shm))) {
+
+        if (!c->conf->disable_shm)
+            c->mempool = pa_mempool_new(0);
+
+        if (!c->mempool) {
+            context_free(c);
+            return NULL;
+        }
+    }
+
+    return c;
+}
+
+static void context_unlink(pa_context *c) {
+    pa_stream *s;
+
+    pa_assert(c);
+
+    s = c->streams ? pa_stream_ref(c->streams) : NULL;
+    while (s) {
+        pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
+        pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
+        pa_stream_unref(s);
+        s = n;
+    }
+
+    while (c->operations)
+        pa_operation_cancel(c->operations);
+
+    if (c->pdispatch) {
+        pa_pdispatch_unref(c->pdispatch);
+        c->pdispatch = NULL;
+    }
+
+    if (c->pstream) {
+        pa_pstream_unlink(c->pstream);
+        pa_pstream_unref(c->pstream);
+        c->pstream = NULL;
+    }
+
+    if (c->client) {
+        pa_socket_client_unref(c->client);
+        c->client = NULL;
+    }
+
+    reset_callbacks(c);
+}
+
+static void context_free(pa_context *c) {
+    pa_assert(c);
+
+    context_unlink(c);
+
+    unlock_autospawn_lock_file(c);
+
+    if (c->record_streams)
+        pa_dynarray_free(c->record_streams, NULL, NULL);
+    if (c->playback_streams)
+        pa_dynarray_free(c->playback_streams, NULL, NULL);
+
+    if (c->mempool)
+        pa_mempool_free(c->mempool);
+
+    if (c->conf)
+        pa_client_conf_free(c->conf);
+
+    pa_strlist_free(c->server_list);
+
+    if (c->proplist)
+        pa_proplist_free(c->proplist);
+
+    pa_xfree(c->server);
+    pa_xfree(c);
+}
+
+pa_context* pa_context_ref(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_REFCNT_INC(c);
+    return c;
+}
+
+void pa_context_unref(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (PA_REFCNT_DEC(c) <= 0)
+        context_free(c);
+}
+
+void pa_context_set_state(pa_context *c, pa_context_state_t st) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (c->state == st)
+        return;
+
+    pa_context_ref(c);
+
+    c->state = st;
+
+    if (c->state_callback)
+        c->state_callback(c, c->state_userdata);
+
+    if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
+        context_unlink(c);
+
+    pa_context_unref(c);
+}
+
+int pa_context_set_error(pa_context *c, int error) {
+    pa_assert(error >= 0);
+    pa_assert(error < PA_ERR_MAX);
+
+    if (c)
+        c->error = error;
+
+    return error;
+}
+
+void pa_context_fail(pa_context *c, int error) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_set_error(c, error);
+    pa_context_set_state(c, PA_CONTEXT_FAILED);
+}
+
+static void pstream_die_callback(pa_pstream *p, void *userdata) {
+    pa_context *c = userdata;
+
+    pa_assert(p);
+    pa_assert(c);
+
+    pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED);
+}
+
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
+    pa_context *c = userdata;
+
+    pa_assert(p);
+    pa_assert(packet);
+    pa_assert(c);
+
+    pa_context_ref(c);
+
+    if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0)
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+
+    pa_context_unref(c);
+}
+
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+
+    pa_assert(p);
+    pa_assert(chunk);
+    pa_assert(chunk->memblock);
+    pa_assert(chunk->length);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if ((s = pa_dynarray_get(c->record_streams, channel))) {
+
+        pa_assert(seek == PA_SEEK_RELATIVE);
+        pa_assert(offset == 0);
+
+        pa_memblockq_seek(s->record_memblockq, offset, seek);
+        pa_memblockq_push_align(s->record_memblockq, chunk);
+
+        if (s->read_callback) {
+            size_t l;
+
+            if ((l = pa_memblockq_get_length(s->record_memblockq)) > 0)
+                s->read_callback(s, l, s->read_userdata);
+        }
+    }
+
+    pa_context_unref(c);
+}
+
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail) {
+    uint32_t err;
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (command == PA_COMMAND_ERROR) {
+        pa_assert(t);
+
+        if (pa_tagstruct_getu32(t, &err) < 0) {
+            pa_context_fail(c, PA_ERR_PROTOCOL);
+            return -1;
+        }
+
+    } else if (command == PA_COMMAND_TIMEOUT)
+        err = PA_ERR_TIMEOUT;
+    else {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return -1;
+    }
+
+    if (err == PA_OK) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return -1;
+    }
+
+    if (err >= PA_ERR_MAX)
+        err = PA_ERR_UNKNOWN;
+
+    if (fail) {
+        pa_context_fail(c, err);
+        return -1;
+    }
+
+    pa_context_set_error(c, err);
+
+    return 0;
+}
+
+static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+
+    pa_assert(pd);
+    pa_assert(c);
+    pa_assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME);
+
+    pa_context_ref(c);
+
+    if (command != PA_COMMAND_REPLY) {
+        pa_context_handle_error(c, command, t, TRUE);
+        goto finish;
+    }
+
+    switch(c->state) {
+        case PA_CONTEXT_AUTHORIZING: {
+            pa_tagstruct *reply;
+            pa_bool_t shm_on_remote;
+
+            if (pa_tagstruct_getu32(t, &c->version) < 0 ||
+                !pa_tagstruct_eof(t)) {
+                pa_context_fail(c, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            /* Minimum supported version */
+            if (c->version < 8) {
+                pa_context_fail(c, PA_ERR_VERSION);
+                goto finish;
+            }
+
+            /* Starting with protocol version 13 the MSB of the version
+               tag reflects if shm is available for this connection or
+               not. */
+            if (c->version >= 13) {
+                shm_on_remote = !!(c->version & 0x80000000U);
+                c->version &= 0x7FFFFFFFU;
+            }
+
+            pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
+
+            /* Enable shared memory support if possible */
+            if (c->do_shm)
+                if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
+                    c->do_shm = FALSE;
+
+            if (c->do_shm) {
+
+                /* Only enable SHM if both sides are owned by the same
+                 * user. This is a security measure because otherwise
+                 * data private to the user might leak. */
+
+#ifdef HAVE_CREDS
+                const pa_creds *creds;
+                if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
+                    c->do_shm = FALSE;
+#endif
+            }
+
+            pa_log_debug("Negotiated SHM: %s", pa_yes_no(c->do_shm));
+            pa_pstream_enable_shm(c->pstream, c->do_shm);
+
+            reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
+
+            if (c->version >= 13) {
+                pa_init_proplist(c->proplist);
+                pa_tagstruct_put_proplist(reply, c->proplist);
+            } else
+                pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
+
+            pa_pstream_send_tagstruct(c->pstream, reply);
+            pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
+
+            pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
+            break;
+        }
+
+        case PA_CONTEXT_SETTING_NAME :
+
+            if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
+                                      c->client_index == PA_INVALID_INDEX)) ||
+                !pa_tagstruct_eof(t)) {
+                pa_context_fail(c, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            pa_context_set_state(c, PA_CONTEXT_READY);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+finish:
+    pa_context_unref(c);
+}
+
+static void setup_context(pa_context *c, pa_iochannel *io) {
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(io);
+
+    pa_context_ref(c);
+
+    pa_assert(!c->pstream);
+    c->pstream = pa_pstream_new(c->mainloop, io, c->mempool);
+
+    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);
+
+    pa_assert(!c->pdispatch);
+    c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
+
+    if (!c->conf->cookie_valid)
+        pa_log_info("No cookie loaded. Attempting to connect without.");
+
+    t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
+
+    c->do_shm =
+        pa_mempool_is_shared(c->mempool) &&
+        c->is_local;
+
+    pa_log_debug("SHM possible: %s", pa_yes_no(c->do_shm));
+
+    /* Starting with protocol version 13 we use the MSB of the version
+     * tag for informing the other side if we could do SHM or not */
+    pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION | (c->do_shm ? 0x80000000U : 0));
+    pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie));
+
+#ifdef HAVE_CREDS
+{
+    pa_creds ucred;
+
+    if (pa_iochannel_creds_supported(io))
+        pa_iochannel_creds_enable(io);
+
+    ucred.uid = getuid();
+    ucred.gid = getgid();
+
+    pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred);
+}
+#else
+    pa_pstream_send_tagstruct(c->pstream, t);
+#endif
+
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
+
+    pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);
+
+    pa_context_unref(c);
+}
+
+static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata);
+
+#ifndef OS_IS_WIN32
+
+static int context_connect_spawn(pa_context *c) {
+    pid_t pid;
+    int status, r;
+    int fds[2] = { -1, -1} ;
+    pa_iochannel *io;
+
+    if (getuid() == 0)
+        return -1;
+
+    pa_context_ref(c);
+
+    if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+        pa_log_error("socketpair(): %s", pa_cstrerror(errno));
+        pa_context_fail(c, PA_ERR_INTERNAL);
+        goto fail;
+    }
+
+    pa_make_fd_cloexec(fds[0]);
+
+    pa_make_socket_low_delay(fds[0]);
+    pa_make_socket_low_delay(fds[1]);
+
+    if (c->spawn_api.prefork)
+        c->spawn_api.prefork();
+
+    if ((pid = fork()) < 0) {
+        pa_log_error("fork(): %s", pa_cstrerror(errno));
+        pa_context_fail(c, PA_ERR_INTERNAL);
+
+        if (c->spawn_api.postfork)
+            c->spawn_api.postfork();
+
+        goto fail;
+    } else if (!pid) {
+        /* Child */
+
+        char t[128];
+        const char *state = NULL;
+#define MAX_ARGS 64
+        const char * argv[MAX_ARGS+1];
+        int n;
+        char *f;
+
+        pa_close_all(fds[1], -1);
+
+        f = pa_sprintf_malloc("%i", fds[1]);
+        pa_set_env("PULSE_PASSED_FD", f);
+        pa_xfree(f);
+
+        if (c->spawn_api.atfork)
+            c->spawn_api.atfork();
+
+        /* Setup argv */
+
+        n = 0;
+
+        argv[n++] = c->conf->daemon_binary;
+        argv[n++] = "--daemonize=yes";
+
+        pa_snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]);
+        argv[n++] = strdup(t);
+
+        while (n < MAX_ARGS) {
+            char *a;
+
+            if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
+                break;
+
+            argv[n++] = a;
+        }
+
+        argv[n++] = NULL;
+
+        execv(argv[0], (char * const *) argv);
+        _exit(1);
+#undef MAX_ARGS
+    }
+
+    /* Parent */
+
+    pa_assert_se(pa_close(fds[1]) == 0);
+    fds[1] = -1;
+
+    r = waitpid(pid, &status, 0);
+
+    if (c->spawn_api.postfork)
+        c->spawn_api.postfork();
+
+    if (r < 0) {
+        pa_log("waitpid(): %s", pa_cstrerror(errno));
+        pa_context_fail(c, PA_ERR_INTERNAL);
+        goto fail;
+    } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+        goto fail;
+    }
+
+    c->is_local = TRUE;
+
+    unlock_autospawn_lock_file(c);
+
+    io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
+    setup_context(c, io);
+
+    pa_context_unref(c);
+
+    return 0;
+
+fail:
+    pa_close_pipe(fds);
+
+    unlock_autospawn_lock_file(c);
+
+    pa_context_unref(c);
+
+    return -1;
+}
+
+#endif /* OS_IS_WIN32 */
+
+static int try_next_connection(pa_context *c) {
+    char *u = NULL;
+    int r = -1;
+
+    pa_assert(c);
+    pa_assert(!c->client);
+
+    for (;;) {
+        pa_xfree(u);
+        u = NULL;
+
+        c->server_list = pa_strlist_pop(c->server_list, &u);
+
+        if (!u) {
+
+#ifndef OS_IS_WIN32
+            if (c->do_autospawn) {
+                r = context_connect_spawn(c);
+                goto finish;
+            }
+#endif
+
+            pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+            goto finish;
+        }
+
+        pa_log_debug("Trying to connect to %s...", u);
+
+        pa_xfree(c->server);
+        c->server = pa_xstrdup(u);
+
+        if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
+            continue;
+
+        c->is_local = !!pa_socket_client_is_local(c->client);
+        pa_socket_client_set_callback(c->client, on_connection, c);
+        break;
+    }
+
+    r = 0;
+
+finish:
+    pa_xfree(u);
+
+    return r;
+}
+
+static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
+    pa_context *c = userdata;
+    int saved_errno = errno;
+
+    pa_assert(client);
+    pa_assert(c);
+    pa_assert(c->state == PA_CONTEXT_CONNECTING);
+
+    pa_context_ref(c);
+
+    pa_socket_client_unref(client);
+    c->client = NULL;
+
+    if (!io) {
+        /* Try the item in the list */
+        if (saved_errno == ECONNREFUSED ||
+            saved_errno == ETIMEDOUT ||
+            saved_errno == EHOSTUNREACH) {
+            try_next_connection(c);
+            goto finish;
+        }
+
+        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+        goto finish;
+    }
+
+    unlock_autospawn_lock_file(c);
+    setup_context(c, io);
+
+finish:
+    pa_context_unref(c);
+}
+
+
+static char *get_legacy_runtime_dir(void) {
+    char *p, u[128];
+    struct stat st;
+
+    if (!pa_get_user_name(u, sizeof(u)))
+        return NULL;
+
+    p = pa_sprintf_malloc("/tmp/pulse-%s", u);
+
+    if (stat(p, &st) < 0) {
+        pa_xfree(p);
+        return NULL;
+    }
+
+    if (st.st_uid != getuid()) {
+        pa_xfree(p);
+        return NULL;
+    }
+
+    return p;
+}
+
+int pa_context_connect(
+        pa_context *c,
+        const char *server,
+        pa_context_flags_t flags,
+        const pa_spawn_api *api) {
+
+    int r = -1;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(c, !(flags & ~PA_CONTEXT_NOAUTOSPAWN), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID);
+
+    if (!server)
+        server = c->conf->default_server;
+
+    pa_context_ref(c);
+
+    pa_assert(!c->server_list);
+
+    if (server) {
+        if (!(c->server_list = pa_strlist_parse(server))) {
+            pa_context_fail(c, PA_ERR_INVALIDSERVER);
+            goto finish;
+        }
+    } else {
+        char *d, *ufn;
+        static char *legacy_dir;
+
+        /* Prepend in reverse order */
+
+        if ((d = getenv("DISPLAY"))) {
+            char *e;
+            d = pa_xstrdup(d);
+            if ((e = strchr(d, ':')))
+                *e = 0;
+
+            if (*d)
+                c->server_list = pa_strlist_prepend(c->server_list, d);
+
+            pa_xfree(d);
+        }
+
+        c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost");
+        c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost");
+
+        /* The system wide instance */
+        c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
+
+        /* The old per-user instance path. This is supported only to ease upgrades */
+        if ((legacy_dir = get_legacy_runtime_dir())) {
+            char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir);
+            c->server_list = pa_strlist_prepend(c->server_list, p);
+            pa_xfree(p);
+            pa_xfree(legacy_dir);
+        }
+
+        /* The per-user instance */
+        if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
+            c->server_list = pa_strlist_prepend(c->server_list, ufn);
+            pa_xfree(ufn);
+        }
+
+        /* Wrap the connection attempts in a single transaction for sane autospawn locking */
+        if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
+            char *lf;
+
+            if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
+                pa_context_fail(c, PA_ERR_ACCESS);
+                goto finish;
+            }
+
+            pa_assert(c->autospawn_lock_fd <= 0);
+            c->autospawn_lock_fd = pa_lock_lockfile(lf);
+            pa_xfree(lf);
+
+            if (api)
+                c->spawn_api = *api;
+
+            c->do_autospawn = TRUE;
+        }
+    }
+
+    pa_context_set_state(c, PA_CONTEXT_CONNECTING);
+    r = try_next_connection(c);
+
+finish:
+    pa_context_unref(c);
+
+    return r;
+}
+
+void pa_context_disconnect(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (PA_CONTEXT_IS_GOOD(c->state))
+        pa_context_set_state(c, PA_CONTEXT_TERMINATED);
+}
+
+pa_context_state_t pa_context_get_state(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    return c->state;
+}
+
+int pa_context_errno(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    return c->error;
+}
+
+void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+        return;
+
+    c->state_callback = cb;
+    c->state_userdata = userdata;
+}
+
+int pa_context_is_pending(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE);
+
+    return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
+        (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
+        c->client;
+}
+
+static void set_dispatch_callbacks(pa_operation *o);
+
+static void pdispatch_drain_callback(PA_GCC_UNUSED pa_pdispatch*pd, void *userdata) {
+    set_dispatch_callbacks(userdata);
+}
+
+static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata) {
+    set_dispatch_callbacks(userdata);
+}
+
+static void set_dispatch_callbacks(pa_operation *o) {
+    int done = 1;
+
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+    pa_assert(o->context);
+    pa_assert(PA_REFCNT_VALUE(o->context) >= 1);
+    pa_assert(o->context->state == PA_CONTEXT_READY);
+
+    pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL);
+    pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL);
+
+    if (pa_pdispatch_is_pending(o->context->pdispatch)) {
+        pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o);
+        done = 0;
+    }
+
+    if (pa_pstream_is_pending(o->context->pstream)) {
+        pa_pstream_set_drain_callback(o->context->pstream, pstream_drain_callback, o);
+        done = 0;
+    }
+
+    if (done) {
+        if (o->callback) {
+            pa_context_notify_cb_t cb = (pa_context_notify_cb_t) o->callback;
+            cb(o->context, o->userdata);
+        }
+
+        pa_operation_done(o);
+        pa_operation_unref(o);
+    }
+}
+
+pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_context_is_pending(c), PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+    set_dispatch_callbacks(pa_operation_ref(o));
+
+    return o;
+}
+
+void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int success = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        success = 0;
+    } else if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
+        cb(o->context, success, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, cb, userdata);
+
+    t = pa_tagstruct_command(c, command, &tag);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT,  pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT,  pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+int pa_context_is_local(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1);
+
+    return !!c->is_local;
+}
+
+pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(name);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    if (c->version >= 13) {
+        pa_proplist *p = pa_proplist_new();
+
+        pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+        o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
+        pa_proplist_free(p);
+    } else {
+        pa_tagstruct *t;
+        uint32_t tag;
+
+        o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+        t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
+        pa_tagstruct_puts(t, name);
+        pa_pstream_send_tagstruct(c->pstream, t);
+        pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT,  pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+    }
+
+    return o;
+}
+
+const char* pa_get_library_version(void) {
+    return PACKAGE_VERSION;
+}
+
+const char* pa_context_get_server(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (!c->server)
+        return NULL;
+
+    if (*c->server == '{') {
+        char *e = strchr(c->server+1, '}');
+        return e ? e+1 : c->server;
+    }
+
+    return c->server;
+}
+
+uint32_t pa_context_get_protocol_version(PA_GCC_UNUSED pa_context *c) {
+    return PA_PROTOCOL_VERSION;
+}
+
+uint32_t pa_context_get_server_protocol_version(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
+    return c->version;
+}
+
+pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag) {
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(tag);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, command);
+    pa_tagstruct_putu32(t, *tag = c->ctag++);
+
+    return t;
+}
+
+uint32_t pa_context_get_index(pa_context *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(c, c->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+
+    return c->client_index;
+}
+
+pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_UPDATE_CLIENT_PROPLIST, &tag);
+    pa_tagstruct_putu32(t, (uint32_t) mode);
+    pa_tagstruct_put_proplist(t, p);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    /* Please note that we don't update c->proplist here, because we
+     * don't export that field */
+
+    return o;
+}
+
+pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+    const char * const *k;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, keys && keys[0], PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_CLIENT_PROPLIST, &tag);
+
+    for (k = keys; *k; k++)
+        pa_tagstruct_puts(t, *k);
+
+    pa_tagstruct_puts(t, NULL);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    /* Please note that we don't update c->proplist here, because we
+     * don't export that field */
+
+    return o;
+}
diff --git a/src/pulse/context.h b/src/pulse/context.h
new file mode 100644 (file)
index 0000000..8dff764
--- /dev/null
@@ -0,0 +1,253 @@
+#ifndef foocontexthfoo
+#define foocontexthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/def.h>
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+#include <pulse/operation.h>
+#include <pulse/proplist.h>
+
+/** \page async Asynchronous API
+ *
+ * \section overv_sec Overview
+ *
+ * The asynchronous API is the native interface to the PulseAudio library.
+ * It allows full access to all available functions. This also means that
+ * it is rather complex and can take some time to fully master.
+ *
+ * \section mainloop_sec Main Loop Abstraction
+ *
+ * The API is based around an asynchronous event loop, or main loop,
+ * abstraction. This abstraction contains three basic elements:
+ *
+ * \li Deferred events - Events that will trigger as soon as possible. Note
+ *                       that some implementations may block all other events
+ *                       when a deferred event is active.
+ * \li I/O events - Events that trigger on file descriptor activities.
+ * \li Times events - Events that trigger after a fixed ammount of time.
+ *
+ * The abstraction is represented as a number of function pointers in the
+ * pa_mainloop_api structure.
+ *
+ * To actually be able to use these functions, an implementation needs to
+ * be coupled to the abstraction. There are three of these shipped with
+ * PulseAudio, but any other can be used with a minimal ammount of work,
+ * provided it supports the three basic events listed above.
+ *
+ * The implementations shipped with PulseAudio are:
+ *
+ * \li \subpage mainloop - A minimal but fast implementation based on poll().
+ * \li \subpage threaded_mainloop - A special version of the previous
+ *                                  implementation where all of PulseAudio's
+ *                                  internal handling runs in a separate
+ *                                  thread.
+ * \li \subpage glib-mainloop - A wrapper around GLIB's main loop. Available
+ *                              for both GLIB 1.2 and GLIB 2.x.
+ *
+ * UNIX signals may be hooked to a main loop using the functions from
+ * \ref mainloop-signal.h. These rely only on the main loop abstraction
+ * and can therefore be used with any of the implementations.
+ *
+ * \section refcnt_sec Reference Counting
+ *
+ * Almost all objects in PulseAudio are reference counted. What that means
+ * is that you rarely malloc() or free() any objects. Instead you increase
+ * and decrease their reference counts. Whenever an object's reference
+ * count reaches zero, that object gets destroy and any resources it uses
+ * get freed.
+ *
+ * The benefit of this design is that an application need not worry about
+ * whether or not it needs to keep an object around in case the library is
+ * using it internally. If it is, then it has made sure it has its own
+ * reference to it.
+ *
+ * Whenever the library creates an object, it will have an initial
+ * reference count of one. Most of the time, this single reference will be
+ * sufficient for the application, so all required reference count
+ * interaction will be a single call to the objects unref function.
+ *
+ * \section context_sec Context
+ *
+ * A context is the basic object for a connection to a PulseAudio server.
+ * It multiplexes commands, data streams and events through a single
+ * channel.
+ *
+ * There is no need for more than one context per application, unless
+ * connections to multiple servers are needed.
+ *
+ * \subsection ops_subsec Operations
+ *
+ * All operations on the context are performed asynchronously. I.e. the
+ * client will not wait for the server to complete the request. To keep
+ * track of all these in-flight operations, the application is given a
+ * pa_operation object for each asynchronous operation.
+ *
+ * There are only two actions (besides reference counting) that can be
+ * performed on a pa_operation: querying its state with
+ * pa_operation_get_state() and aborting it with pa_operation_cancel().
+ *
+ * A pa_operation object is reference counted, so an application must
+ * make sure to unreference it, even if it has no intention of using it.
+ *
+ * \subsection conn_subsec Connecting
+ *
+ * A context must be connected to a server before any operation can be
+ * issued. Calling pa_context_connect() will initiate the connection
+ * procedure. Unlike most asynchronous operations, connecting does not
+ * result in a pa_operation object. Instead, the application should
+ * register a callback using pa_context_set_state_callback().
+ *
+ * \subsection disc_subsec Disconnecting
+ *
+ * When the sound support is no longer needed, the connection needs to be
+ * closed using pa_context_disconnect(). This is an immediate function that
+ * works synchronously.
+ *
+ * Since the context object has references to other objects it must be
+ * disconnected after use or there is a high risk of memory leaks. If the
+ * connection has terminated by itself, then there is no need to explicitly
+ * disconnect the context using pa_context_disconnect().
+ *
+ * \section Functions
+ *
+ * The sound server's functionality can be divided into a number of
+ * subsections:
+ *
+ * \li \subpage streams
+ * \li \subpage scache
+ * \li \subpage introspect
+ * \li \subpage subscribe
+ */
+
+/** \file
+ * Connection contexts for asynchrononous communication with a
+ * server. A pa_context object wraps a connection to a PulseAudio
+ * server using its native protocol. */
+
+/** \example pacat.c
+ * A playback and recording tool using the asynchronous API */
+
+/** \example paplay.c
+ * A sound file playback tool using the asynchronous API, based on libsndfile */
+
+PA_C_DECL_BEGIN
+
+/** An opaque connection context to a daemon */
+typedef struct pa_context pa_context;
+
+/** Generic notification callback prototype */
+typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
+
+/** A generic callback for operation completion */
+typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
+
+/** Instantiate a new connection context with an abstract mainloop API
+ * and an application name. It is recommended to use pa_context_new_with_proplist()
+ * instead and specify some initial properties.*/
+pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
+
+/** Instantiate a new connection context with an abstract mainloop API
+ * and an application name, and specify the the initial client property
+ * list. \since 0.9.11 */
+pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist);
+
+/** Decrease the reference counter of the context by one */
+void pa_context_unref(pa_context *c);
+
+/** Increase the reference counter of the context by one */
+pa_context* pa_context_ref(pa_context *c);
+
+/** Set a callback function that is called whenever the context status changes */
+void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
+
+/** Return the error number of the last failed operation */
+int pa_context_errno(pa_context *c);
+
+/** Return non-zero if some data is pending to be written to the connection */
+int pa_context_is_pending(pa_context *c);
+
+/** Return the current context status */
+pa_context_state_t pa_context_get_state(pa_context *c);
+
+/** Connect the context to the specified server. If server is NULL,
+connect to the default server. This routine may but will not always
+return synchronously on error. Use pa_context_set_state_callback() to
+be notified when the connection is established. If flags doesn't have
+PA_NOAUTOSPAWN set and no specific server is specified or accessible a
+new daemon is spawned. If api is non-NULL, the functions specified in
+the structure are used when forking a new child process. */
+int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);
+
+/** Terminate the context connection immediately */
+void pa_context_disconnect(pa_context *c);
+
+/** Drain the context. If there is nothing to drain, the function returns NULL */
+pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
+
+/** Tell the daemon to exit. The returned operation is unlikely to
+ * complete succesfully, since the daemon probably died before
+ * returning a success notification */
+pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the name of the default sink. */
+pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the name of the default source. */
+pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. */
+int pa_context_is_local(pa_context *c);
+
+/** Set a different application name for context on the server. */
+pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Return the server name this context is connected to. */
+const char* pa_context_get_server(pa_context *c);
+
+/** Return the protocol version of the library. */
+uint32_t pa_context_get_protocol_version(pa_context *c);
+
+/** Return the protocol version of the connected server. */
+uint32_t pa_context_get_server_protocol_version(pa_context *c);
+
+/* Update the property list of the client, adding new entries. Please
+ * note that it is highly recommended to set as much properties
+ * initially via pa_context_new_with_proplist() as possible instead a
+ * posteriori with this function, since that information may then be
+ * used to route streams of the client to the right device. \since 0.9.11 */
+pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata);
+
+/* Update the property list of the client, remove entries. \since 0.9.11 */
+pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata);
+
+/** Return the client index this context is
+ * identified in the server with. This is useful for usage with the
+ * introspection functions, such as pa_context_get_client_info(). \since 0.9.11 */
+uint32_t pa_context_get_index(pa_context *s);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/def.h b/src/pulse/def.h
new file mode 100644 (file)
index 0000000..a91c103
--- /dev/null
@@ -0,0 +1,495 @@
+#ifndef foodefhfoo
+#define foodefhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/sample.h>
+
+/** \file
+ * Global definitions */
+
+PA_C_DECL_BEGIN
+
+/** The state of a connection context */
+typedef enum pa_context_state {
+    PA_CONTEXT_UNCONNECTED,    /**< The context hasn't been connected yet */
+    PA_CONTEXT_CONNECTING,     /**< A connection is being established */
+    PA_CONTEXT_AUTHORIZING,    /**< The client is authorizing itself to the daemon */
+    PA_CONTEXT_SETTING_NAME,   /**< The client is passing its application name to the daemon */
+    PA_CONTEXT_READY,          /**< The connection is established, the context is ready to execute operations */
+    PA_CONTEXT_FAILED,         /**< The connection failed or was disconnected */
+    PA_CONTEXT_TERMINATED      /**< The connection was terminated cleanly */
+} pa_context_state_t;
+
+/** Return non-zero if the passed state is one of the connected states */
+static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
+    return
+        x == PA_CONTEXT_CONNECTING ||
+        x == PA_CONTEXT_AUTHORIZING ||
+        x == PA_CONTEXT_SETTING_NAME ||
+        x == PA_CONTEXT_READY;
+}
+
+/** The state of a stream */
+typedef enum pa_stream_state {
+    PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */
+    PA_STREAM_CREATING,     /**< The stream is being created */
+    PA_STREAM_READY,        /**< The stream is established, you may pass audio data to it now */
+    PA_STREAM_FAILED,       /**< An error occured that made the stream invalid */
+    PA_STREAM_TERMINATED    /**< The stream has been terminated cleanly */
+} pa_stream_state_t;
+
+/** Return non-zero if the passed state is one of the connected states */
+static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
+    return
+        x == PA_STREAM_CREATING ||
+        x == PA_STREAM_READY;
+}
+
+/** The state of an operation */
+typedef enum pa_operation_state {
+    PA_OPERATION_RUNNING,      /**< The operation is still running */
+    PA_OPERATION_DONE,         /**< The operation has been completed */
+    PA_OPERATION_CANCELED      /**< The operation has been canceled */
+} pa_operation_state_t;
+
+/** An invalid index */
+#define PA_INVALID_INDEX ((uint32_t) -1)
+
+/** Some special flags for contexts. */
+typedef enum pa_context_flags {
+    PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the PulseAudio daemon if required */
+} pa_context_flags_t;
+
+/** The direction of a pa_stream object */
+typedef enum pa_stream_direction {
+    PA_STREAM_NODIRECTION,   /**< Invalid direction */
+    PA_STREAM_PLAYBACK,      /**< Playback stream */
+    PA_STREAM_RECORD,        /**< Record stream */
+    PA_STREAM_UPLOAD         /**< Sample upload stream */
+} pa_stream_direction_t;
+
+/** Some special flags for stream connections. */
+typedef enum pa_stream_flags {
+    PA_STREAM_START_CORKED = 1,       /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */
+    PA_STREAM_INTERPOLATE_TIMING = 2, /**< Interpolate the latency for
+                                       * this stream. When enabled,
+                                       * pa_stream_get_latency() and
+                                       * pa_stream_get_time() will try
+                                       * to estimate the current
+                                       * record/playback time based on
+                                       * the local time that passed
+                                       * since the last timing info
+                                       * update.  Using this option
+                                       * has the advantage of not
+                                       * requiring a whole roundtrip
+                                       * when the current
+                                       * playback/recording time is
+                                       * needed. Consider using this
+                                       * option when requesting
+                                       * latency information
+                                       * frequently. This is
+                                       * especially useful on long
+                                       * latency network
+                                       * connections. It makes a lot
+                                       * of sense to combine this
+                                       * option with
+                                       * PA_STREAM_AUTO_TIMING_UPDATE. */
+    PA_STREAM_NOT_MONOTONOUS = 4,    /**< Don't force the time to
+                                      * increase monotonically. If
+                                      * this option is enabled,
+                                      * pa_stream_get_time() will not
+                                      * necessarily return always
+                                      * monotonically increasing time
+                                      * values on each call. This may
+                                      * confuse applications which
+                                      * cannot deal with time going
+                                      * 'backwards', but has the
+                                      * advantage that bad transport
+                                      * latency estimations that
+                                      * caused the time to to jump
+                                      * ahead can be corrected
+                                      * quickly, without the need to
+                                      * wait. */
+    PA_STREAM_AUTO_TIMING_UPDATE = 8, /**< If set timing update requests
+                                       * are issued periodically
+                                       * automatically. Combined with
+                                       * PA_STREAM_INTERPOLATE_TIMING
+                                       * you will be able to query the
+                                       * current time and latency with
+                                       * pa_stream_get_time() and
+                                       * pa_stream_get_latency() at
+                                       * all times without a packet
+                                       * round trip.*/
+    PA_STREAM_NO_REMAP_CHANNELS = 16, /**< Don't remap channels by
+                                       * their name, instead map them
+                                       * simply by their
+                                       * index. Implies
+                                       * PA_STREAM_NO_REMIX_CHANNELS. Only
+                                       * supported when the server is
+                                       * at least PA 0.9.8. It is
+                                       * ignored on older
+                                       * servers.\since 0.9.8 */
+    PA_STREAM_NO_REMIX_CHANNELS = 32, /**< When remapping channels by
+                                       * name, don't upmix or downmix
+                                       * them to related
+                                       * channels. Copy them into
+                                       * matching channels of the
+                                       * device 1:1. Only supported
+                                       * when the server is at least
+                                       * PA 0.9.8. It is ignored on
+                                       * older servers. \since
+                                       * 0.9.8 */
+    PA_STREAM_FIX_FORMAT = 64, /**< Use the sample format of the
+                                * sink/device this stream is being
+                                * connected to, and possibly ignore
+                                * the format the sample spec contains
+                                * -- but you still have to pass a
+                                * valid value in it as a hint to
+                                * PulseAudio what would suit your
+                                * stream best. If this is used you
+                                * should query the used sample format
+                                * after creating the stream by using
+                                * pa_stream_get_sample_spec(). Also,
+                                * if you specified manual buffer
+                                * metrics it is recommended to update
+                                * them with
+                                * pa_stream_set_buffer_attr() to
+                                * compensate for the changed frame
+                                * sizes. Only supported when the
+                                * server is at least PA 0.9.8. It is
+                                * ignored on older servers. \since
+                                * 0.9.8 */
+
+    PA_STREAM_FIX_RATE = 128, /**< Use the sample rate of the sink,
+                               * and possibly ignore the rate the
+                               * sample spec contains. Usage similar
+                               * to PA_STREAM_FIX_FORMAT.Only
+                               * supported when the server is at least
+                               * PA 0.9.8. It is ignored on older
+                               * servers. \since 0.9.8 */
+
+    PA_STREAM_FIX_CHANNELS = 256, /**< Use the number of channels and
+                               * the channel map of the sink, and
+                               * possibly ignore the number of
+                               * channels and the map the sample spec
+                               * and the passed channel map
+                               * contains. Usage similar to
+                               * PA_STREAM_FIX_FORMAT. Only supported
+                               * when the server is at least PA
+                               * 0.9.8. It is ignored on older
+                               * servers. \since 0.9.8 */
+    PA_STREAM_DONT_MOVE = 512, /**< Don't allow moving of this stream to
+                              * another sink/device. Useful if you use
+                              * any of the PA_STREAM_FIX_ flags and
+                              * want to make sure that resampling
+                              * never takes place -- which might
+                              * happen if the stream is moved to
+                              * another sink/source whith a different
+                              * sample spec/channel map. Only
+                              * supported when the server is at least
+                              * PA 0.9.8. It is ignored on older
+                              * servers. \since 0.9.8 */
+    PA_STREAM_VARIABLE_RATE = 1024, /**< Allow dynamic changing of the
+                                     * sampling rate during playback
+                                     * with
+                                     * pa_stream_update_sample_rate(). Only
+                                     * supported when the server is at
+                                     * least PA 0.9.8. It is ignored
+                                     * on older servers. \since
+                                     * 0.9.8 */
+    PA_STREAM_PEAK_DETECT = 2048, /**< Find peaks instead of
+                                   * resampling. \since 0.9.11 */
+
+    PA_STREAM_START_MUTED = 4096,  /**< Create in muted state. \since 0.9.11 */
+
+
+    PA_STREAM_ADJUST_LATENCY = 8192, /**< Try to adjust the latency of
+                                      * the sink/source based on the
+                                      * requested buffer metrics and
+                                      * adjust buffer metrics
+                                      * accordingly. \since 0.9.11 */
+} pa_stream_flags_t;
+
+
+/** English is an evil language \since 0.9.11 */
+#define PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONOUS
+
+/** Playback and record buffer metrics */
+typedef struct pa_buffer_attr {
+    uint32_t maxlength;      /**< Maximum length of the
+                              * buffer. Setting this to 0 will
+                              * initialize this to the maximum value
+                              * supported by server, which is
+                              * recommended. */
+    uint32_t tlength;        /**< Playback only: target length of the
+                              * buffer. The server tries to assure
+                              * that at least tlength bytes are always
+                              * available in the buffer. It is
+                              * recommended to set this to 0, which
+                              * will initialize this to a value that
+                              * is deemed sensible by the
+                              * server. However, this value will
+                              * default to something like 2s, i.e. for
+                              * applications that have specific
+                              * latency requirements this value should
+                              * be set to the maximum latency that the
+                              * application can deal with.  */
+    uint32_t prebuf;         /**< Playback only: pre-buffering. The
+                              * server does not start with playback
+                              * before at least prebug bytes are
+                              * available in the buffer. It is
+                              * recommended to set this to 0, which
+                              * will initialize this to the same value
+                              * as tlength, whatever that may be. */
+    uint32_t minreq;         /**< Playback only: minimum request. The
+                              * server does not request less than
+                              * minreq bytes from the client, instead
+                              * waits until the buffer is free enough
+                              * to request more bytes at once. It is
+                              * recommended to set this to 0, which
+                              * will initialize this to a value that
+                              * is deemed sensible by the server. */
+    uint32_t fragsize;       /**< Recording only: fragment size. The
+                              * server sends data in blocks of
+                              * fragsize bytes size. Large values
+                              * deminish interactivity with other
+                              * operations on the connection context
+                              * but decrease control overhead. It is
+                              * recommended to set this to 0, which
+                              * will initialize this to a value that
+                              * is deemed sensible by the
+                              * server. However, this value will
+                              * default to something like 2s, i.e. for
+                              * applications that have specific
+                              * latency requirements this value should
+                              * be set to the maximum latency that the
+                              * application can deal with. */
+} pa_buffer_attr;
+
+/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */
+enum {
+    PA_OK = 0,                     /**< No error */
+    PA_ERR_ACCESS,                 /**< Access failure */
+    PA_ERR_COMMAND,                /**< Unknown command */
+    PA_ERR_INVALID,                /**< Invalid argument */
+    PA_ERR_EXIST,                  /**< Entity exists */
+    PA_ERR_NOENTITY,               /**< No such entity */
+    PA_ERR_CONNECTIONREFUSED,      /**< Connection refused */
+    PA_ERR_PROTOCOL,               /**< Protocol error */
+    PA_ERR_TIMEOUT,                /**< Timeout */
+    PA_ERR_AUTHKEY,                /**< No authorization key */
+    PA_ERR_INTERNAL,               /**< Internal error */
+    PA_ERR_CONNECTIONTERMINATED,   /**< Connection terminated */
+    PA_ERR_KILLED,                 /**< Entity killed */
+    PA_ERR_INVALIDSERVER,          /**< Invalid server */
+    PA_ERR_MODINITFAILED,          /**< Module initialization failed */
+    PA_ERR_BADSTATE,               /**< Bad state */
+    PA_ERR_NODATA,                 /**< No data */
+    PA_ERR_VERSION,                /**< Incompatible protocol version */
+    PA_ERR_TOOLARGE,               /**< Data too large */
+    PA_ERR_NOTSUPPORTED,           /**< Operation not supported \since 0.9.5 */
+    PA_ERR_UNKNOWN,                /**< The error code was unknown to the client */
+    PA_ERR_MAX                     /**< Not really an error but the first invalid error code */
+};
+
+/** Subscription event mask, as used by pa_context_subscribe() */
+typedef enum pa_subscription_mask {
+    PA_SUBSCRIPTION_MASK_NULL = 0,               /**< No events */
+    PA_SUBSCRIPTION_MASK_SINK = 1,               /**< Sink events */
+    PA_SUBSCRIPTION_MASK_SOURCE = 2,             /**< Source events */
+    PA_SUBSCRIPTION_MASK_SINK_INPUT = 4,         /**< Sink input events */
+    PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 8,      /**< Source output events */
+    PA_SUBSCRIPTION_MASK_MODULE = 16,            /**< Module events */
+    PA_SUBSCRIPTION_MASK_CLIENT = 32,            /**< Client events */
+    PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64,      /**< Sample cache events */
+    PA_SUBSCRIPTION_MASK_SERVER = 128,           /**< Other global server changes. */
+    PA_SUBSCRIPTION_MASK_AUTOLOAD = 256,         /**< Autoload table events. */
+    PA_SUBSCRIPTION_MASK_ALL = 511               /**< Catch all events */
+} pa_subscription_mask_t;
+
+/** Subscription event types, as used by pa_context_subscribe() */
+typedef enum pa_subscription_event_type {
+    PA_SUBSCRIPTION_EVENT_SINK = 0,           /**< Event type: Sink */
+    PA_SUBSCRIPTION_EVENT_SOURCE = 1,         /**< Event type: Source */
+    PA_SUBSCRIPTION_EVENT_SINK_INPUT = 2,     /**< Event type: Sink input */
+    PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 3,  /**< Event type: Source output */
+    PA_SUBSCRIPTION_EVENT_MODULE = 4,         /**< Event type: Module */
+    PA_SUBSCRIPTION_EVENT_CLIENT = 5,         /**< Event type: Client */
+    PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6,   /**< Event type: Sample cache item */
+    PA_SUBSCRIPTION_EVENT_SERVER = 7,         /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. */
+    PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8,       /**< Event type: Autoload table changes. */
+    PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */
+
+    PA_SUBSCRIPTION_EVENT_NEW = 0,            /**< A new object was created */
+    PA_SUBSCRIPTION_EVENT_CHANGE = 16,        /**< A property of the object was modified */
+    PA_SUBSCRIPTION_EVENT_REMOVE = 32,        /**< An object was removed */
+    PA_SUBSCRIPTION_EVENT_TYPE_MASK = 16+32   /**< A mask to extract the event operation from an event value */
+} pa_subscription_event_type_t;
+
+/** Return one if an event type t matches an event mask bitfield */
+#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))))
+
+/** A structure for all kinds of timing information of a stream. See
+ * pa_stream_update_timing_info() and pa_stream_get_timing_info(). The
+ * total output latency a sample that is written with
+ * pa_stream_write() takes to be played may be estimated by
+ * sink_usec+buffer_usec+transport_usec. (where buffer_usec is defined
+ * as pa_bytes_to_usec(write_index-read_index)) The output buffer
+ * which buffer_usec relates to may be manipulated freely (with
+ * pa_stream_write()'s seek argument, pa_stream_flush() and friends),
+ * the buffers sink_usec and source_usec relate to are first-in
+ * first-out (FIFO) buffers which cannot be flushed or manipulated in
+ * any way. The total input latency a sample that is recorded takes to
+ * be delivered to the application is:
+ * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of
+ * sign issues!) When connected to a monitor source sink_usec contains
+ * the latency of the owning sink. The two latency estimations
+ * described here are implemented in pa_stream_get_latency(). Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release.*/
+typedef struct pa_timing_info {
+    struct timeval timestamp; /**< The time when this timing info structure was current */
+    int synchronized_clocks;  /**< Non-zero if the local and the
+                               * remote machine have synchronized
+                               * clocks. If synchronized clocks are
+                               * detected transport_usec becomes much
+                               * more reliable. However, the code that
+                               * detects synchronized clocks is very
+                               * limited und unreliable itself. */
+
+    pa_usec_t sink_usec;      /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */
+    pa_usec_t source_usec;    /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. */
+    pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. */
+
+    int playing;              /**< Non-zero when the stream is
+                               * currently not underrun and data is
+                               * being passed on to the device. Only
+                               * for playback streams. This field does
+                               * not say whether the data is actually
+                               * already being played. To determine
+                               * this check whether since_underrun
+                               * (converted to usec) is larger than
+                               * sink_usec.*/
+
+    int write_index_corrupt;  /**< Non-zero if write_index is not
+                               * up-to-date because a local write
+                               * command that corrupted it has been
+                               * issued in the time since this latency
+                               * info was current . Only write
+                               * commands with SEEK_RELATIVE_ON_READ
+                               * and SEEK_RELATIVE_END can corrupt
+                               * write_index. */
+    int64_t write_index;      /**< Current write index into the
+                               * playback buffer in bytes. Think twice before
+                               * using this for seeking purposes: it
+                               * might be out of date a the time you
+                               * want to use it. Consider using
+                               * PA_SEEK_RELATIVE instead.  */
+
+    int read_index_corrupt;   /**< Non-zero if read_index is not
+                               * up-to-date because a local pause or
+                               * flush request that corrupted it has
+                               * been issued in the time since this
+                               * latency info was current. */
+
+    int64_t read_index;       /**< Current read index into the
+                               * playback buffer in bytes. Think twice before
+                               * using this for seeking purposes: it
+                               * might be out of date a the time you
+                               * want to use it. Consider using
+                               * PA_SEEK_RELATIVE_ON_READ
+                               * instead. */
+
+    pa_usec_t configured_sink_usec;   /**< The static configured latency for
+                                * the sink. \since 0.9.11 */
+    pa_usec_t configured_source_usec; /**< The static configured latency for
+                                * the source. \since 0.9.11 */
+
+    int64_t since_underrun;    /**< Bytes that were handed to the sink
+                                  since the last underrun happened, or
+                                  since playback started again after
+                                  the last underrun. playing will tell
+                                  you which case it is. \since
+                                  0.9.11 */
+
+} pa_timing_info;
+
+/** A structure for the spawn api. This may be used to integrate auto
+ * spawned daemons into your application. For more information see
+ * pa_context_connect(). When spawning a new child process the
+ * waitpid() is used on the child's PID. The spawn routine will not
+ * block or ignore SIGCHLD signals, since this cannot be done in a
+ * thread compatible way. You might have to do this in
+ * prefork/postfork. */
+typedef struct pa_spawn_api {
+    void (*prefork)(void);     /**< Is called just before the fork in the parent process. May be NULL. */
+    void (*postfork)(void);    /**< Is called immediately after the fork in the parent process. May be NULL.*/
+    void (*atfork)(void);      /**< Is called immediately after the
+                                * fork in the child process. May be
+                                * NULL. It is not safe to close all
+                                * file descriptors in this function
+                                * unconditionally, since a UNIX socket
+                                * (created using socketpair()) is
+                                * passed to the new process. */
+} pa_spawn_api;
+
+/** Seek type for pa_stream_write(). */
+typedef enum pa_seek_mode {
+    PA_SEEK_RELATIVE = 0,           /**< Seek relatively to the write index */
+    PA_SEEK_ABSOLUTE = 1,           /**< Seek relatively to the start of the buffer queue */
+    PA_SEEK_RELATIVE_ON_READ = 2,   /**< Seek relatively to the read index.  */
+    PA_SEEK_RELATIVE_END = 3        /**< Seek relatively to the current end of the buffer queue. */
+} pa_seek_mode_t;
+
+/** Special sink flags. */
+typedef enum pa_sink_flags {
+    PA_SINK_HW_VOLUME_CTRL = 1,   /**< Supports hardware volume control */
+    PA_SINK_LATENCY = 2,          /**< Supports latency querying */
+    PA_SINK_HARDWARE = 4,         /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */
+    PA_SINK_NETWORK = 8,          /**< Is a networked sink of some kind. \since 0.9.7 */
+    PA_SINK_HW_MUTE_CTRL = 16,    /**< Supports hardware mute control \since 0.9.11 */
+    PA_SINK_DECIBEL_VOLUME = 32   /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */
+} pa_sink_flags_t;
+
+/** Special source flags.  */
+typedef enum pa_source_flags {
+    PA_SOURCE_HW_VOLUME_CTRL = 1,  /**< Supports hardware volume control */
+    PA_SOURCE_LATENCY = 2,         /**< Supports latency querying */
+    PA_SOURCE_HARDWARE = 4,        /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */
+    PA_SOURCE_NETWORK = 8,         /**< Is a networked sink of some kind. \since 0.9.7 */
+    PA_SOURCE_HW_MUTE_CTRL = 16,   /**< Supports hardware mute control \since 0.9.11 */
+    PA_SOURCE_DECIBEL_VOLUME = 32  /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */
+} pa_source_flags_t;
+
+/** A generic free() like callback prototype */
+typedef void (*pa_free_cb_t)(void *p);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/error.c b/src/pulse/error.c
new file mode 100644 (file)
index 0000000..29690c8
--- /dev/null
@@ -0,0 +1,67 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/native-common.h>
+
+#include "error.h"
+
+const char*pa_strerror(int error) {
+
+    static const char* const errortab[PA_ERR_MAX] = {
+        [PA_OK] = "OK",
+        [PA_ERR_ACCESS] = "Access denied",
+        [PA_ERR_COMMAND] = "Unknown command",
+        [PA_ERR_INVALID] = "Invalid argument",
+        [PA_ERR_EXIST] = "Entity exists",
+        [PA_ERR_NOENTITY] = "No such entity",
+        [PA_ERR_CONNECTIONREFUSED] = "Connection refused",
+        [PA_ERR_PROTOCOL] = "Protocol error",
+        [PA_ERR_TIMEOUT] = "Timeout",
+        [PA_ERR_AUTHKEY] = "No authorization key",
+        [PA_ERR_INTERNAL] = "Internal error",
+        [PA_ERR_CONNECTIONTERMINATED] = "Connection terminated",
+        [PA_ERR_KILLED] = "Entity killed",
+        [PA_ERR_INVALIDSERVER] = "Invalid server",
+        [PA_ERR_MODINITFAILED] = "Module initalization failed",
+        [PA_ERR_BADSTATE] = "Bad state",
+        [PA_ERR_NODATA] = "No data",
+        [PA_ERR_VERSION] = "Incompatible protocol version",
+        [PA_ERR_TOOLARGE] = "Too large"
+    };
+
+    if (error < 0 || error >= PA_ERR_MAX)
+        return NULL;
+
+    return errortab[error];
+}
similarity index 61%
rename from polyp/polyplib-error.h
rename to src/pulse/error.h
index dbbbf00678e3c36d232149a5a2a2b51781523409..9f9e3d336b8796df1512c8d554a80543b4d1b9e1 100644 (file)
@@ -1,29 +1,30 @@
-#ifndef foopolypliberrorhfoo
-#define foopolypliberrorhfoo
-
-/* $Id$ */
+#ifndef fooerrorhfoo
+#define fooerrorhfoo
 
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include <inttypes.h>
-#include "cdecl.h"
+#include <pulse/cdecl.h>
 
 /** \file
  * Error management */
@@ -31,7 +32,7 @@
 PA_C_DECL_BEGIN
 
 /** Return a human readable error message for the specified numeric error code */
-const char* pa_strerror(uint32_t error);
+const char* pa_strerror(int error);
 
 PA_C_DECL_END
 
diff --git a/src/pulse/gccmacro.h b/src/pulse/gccmacro.h
new file mode 100644 (file)
index 0000000..e406203
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef foopulsegccmacrohfoo
+#define foopulsegccmacrohfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef __GNUC__
+#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
+#else
+/** If we're in GNU C, use some magic for detecting invalid format strings */
+#define PA_GCC_PRINTF_ATTR(a,b)
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ >= 4)
+#define PA_GCC_SENTINEL __attribute__ ((sentinel))
+#else
+/** Macro for usage of GCC's sentinel compilation warnings */
+#define PA_GCC_SENTINEL
+#endif
+
+#ifdef __GNUC__
+#define PA_GCC_NORETURN __attribute__((noreturn))
+#else
+/** Macro for no-return functions */
+#define PA_GCC_NORETURN
+#endif
+
+#ifdef __GNUC__
+#define PA_GCC_UNUSED __attribute__ ((unused))
+#else
+/** Macro for not used parameter */
+#define PA_GCC_UNUSED
+#endif
+
+#ifdef __GNUC__
+#define PA_GCC_DESTRUCTOR __attribute__ ((destructor))
+#else
+/** Call this function when process terminates */
+#define PA_GCC_DESTRUCTOR
+#endif
+
+#ifndef PA_GCC_PURE
+#ifdef __GNUC__
+#define PA_GCC_PURE __attribute__ ((pure))
+#else
+/** This function's return value depends only the arguments list and global state **/
+#define PA_GCC_PURE
+#endif
+#endif
+
+#ifndef PA_GCC_CONST
+#ifdef __GNUC__
+#define PA_GCC_CONST __attribute__ ((const))
+#else
+/** This function's return value depends only the arguments list (stricter version of PA_GCC_PURE) **/
+#define PA_GCC_CONST
+#endif
+#endif
+
+#ifndef PA_GCC_DEPRECATED
+#ifdef __GNUC__
+#define PA_GCC_DEPRECATED __attribute__ ((deprecated))
+#else
+/** This function is deprecated **/
+#define PA_GCC_DEPRECATED
+#endif
+#endif
+
+#ifndef PA_GCC_PACKED
+#ifdef __GNUCC__
+#define PA_GCC_PACKED __attribute__ ((packed))
+#else
+/** Structure shall be packed in memory **/
+#define PA_GCC_PACKED
+#endif
+#endif
+
+#endif
diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c
new file mode 100644 (file)
index 0000000..6ddb0fa
--- /dev/null
@@ -0,0 +1,663 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/idxset.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/llist.h>
+
+#include <glib.h>
+#include "glib-mainloop.h"
+
+struct pa_io_event  {
+    pa_glib_mainloop *mainloop;
+    int dead;
+
+    GPollFD poll_fd;
+    int poll_fd_added;
+
+    pa_io_event_cb_t callback;
+    void *userdata;
+    pa_io_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_io_event);
+};
+
+struct pa_time_event {
+    pa_glib_mainloop *mainloop;
+    int dead;
+
+    int enabled;
+    struct timeval timeval;
+
+    pa_time_event_cb_t callback;
+    void *userdata;
+    pa_time_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_time_event);
+};
+
+struct pa_defer_event {
+    pa_glib_mainloop *mainloop;
+    int dead;
+
+    int enabled;
+
+    pa_defer_event_cb_t callback;
+    void *userdata;
+    pa_defer_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_defer_event);
+};
+
+struct pa_glib_mainloop {
+    GSource source;
+
+    pa_mainloop_api api;
+    GMainContext *context;
+
+    PA_LLIST_HEAD(pa_io_event, io_events);
+    PA_LLIST_HEAD(pa_time_event, time_events);
+    PA_LLIST_HEAD(pa_defer_event, defer_events);
+
+    int n_enabled_defer_events, n_enabled_time_events;
+    int io_events_please_scan, time_events_please_scan, defer_events_please_scan;
+
+    pa_time_event *cached_next_time_event;
+};
+
+static void cleanup_io_events(pa_glib_mainloop *g, int force) {
+    pa_io_event *e;
+
+    e = g->io_events;
+    while (e) {
+        pa_io_event *n = e->next;
+
+        if (!force && g->io_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_io_event, g->io_events, e);
+
+            if (e->dead) {
+                g_assert(g->io_events_please_scan > 0);
+                g->io_events_please_scan--;
+            }
+
+            if (e->poll_fd_added)
+                g_source_remove_poll(&g->source, &e->poll_fd);
+
+            if (e->destroy_callback)
+                e->destroy_callback(&g->api, e, e->userdata);
+
+            pa_xfree(e);
+        }
+
+        e = n;
+    }
+
+    g_assert(g->io_events_please_scan == 0);
+}
+
+static void cleanup_time_events(pa_glib_mainloop *g, int force) {
+    pa_time_event *e;
+
+    e = g->time_events;
+    while (e) {
+        pa_time_event *n = e->next;
+
+        if (!force && g->time_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_time_event, g->time_events, e);
+
+            if (e->dead) {
+                g_assert(g->time_events_please_scan > 0);
+                g->time_events_please_scan--;
+            }
+
+            if (!e->dead && e->enabled) {
+                g_assert(g->n_enabled_time_events > 0);
+                g->n_enabled_time_events--;
+            }
+
+            if (e->destroy_callback)
+                e->destroy_callback(&g->api, e, e->userdata);
+
+            pa_xfree(e);
+        }
+
+        e = n;
+    }
+
+    g_assert(g->time_events_please_scan == 0);
+}
+
+static void cleanup_defer_events(pa_glib_mainloop *g, int force) {
+    pa_defer_event *e;
+
+    e = g->defer_events;
+    while (e) {
+        pa_defer_event *n = e->next;
+
+        if (!force && g->defer_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_defer_event, g->defer_events, e);
+
+            if (e->dead) {
+                g_assert(g->defer_events_please_scan > 0);
+                g->defer_events_please_scan--;
+            }
+
+            if (!e->dead && e->enabled) {
+                g_assert(g->n_enabled_defer_events > 0);
+                g->n_enabled_defer_events--;
+            }
+
+            if (e->destroy_callback)
+                e->destroy_callback(&g->api, e, e->userdata);
+
+            pa_xfree(e);
+        }
+
+        e = n;
+    }
+
+    g_assert(g->defer_events_please_scan == 0);
+}
+
+static gushort map_flags_to_glib(pa_io_event_flags_t flags) {
+    return
+        (flags & PA_IO_EVENT_INPUT ? G_IO_IN : 0) |
+        (flags & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0) |
+        (flags & PA_IO_EVENT_ERROR ? G_IO_ERR : 0) |
+        (flags & PA_IO_EVENT_HANGUP ? G_IO_HUP : 0);
+}
+
+static pa_io_event_flags_t map_flags_from_glib(gushort flags) {
+    return
+        (flags & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
+        (flags & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
+        (flags & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
+        (flags & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
+}
+
+static pa_io_event* glib_io_new(
+        pa_mainloop_api*m,
+        int fd,
+        pa_io_event_flags_t f,
+        pa_io_event_cb_t cb,
+        void *userdata) {
+
+    pa_io_event *e;
+    pa_glib_mainloop *g;
+
+    g_assert(m);
+    g_assert(m->userdata);
+    g_assert(fd >= 0);
+    g_assert(cb);
+
+    g = m->userdata;
+
+    e = pa_xnew(pa_io_event, 1);
+    e->mainloop = g;
+    e->dead = 0;
+
+    e->poll_fd.fd = fd;
+    e->poll_fd.events = map_flags_to_glib(f);
+    e->poll_fd.revents = 0;
+
+    e->callback = cb;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+    PA_LLIST_PREPEND(pa_io_event, g->io_events, e);
+
+    g_source_add_poll(&g->source, &e->poll_fd);
+    e->poll_fd_added = 1;
+
+    return e;
+}
+
+static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->poll_fd.events = map_flags_to_glib(f);
+}
+
+static void glib_io_free(pa_io_event*e) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->dead = 1;
+    e->mainloop->io_events_please_scan++;
+
+    if (e->poll_fd_added) {
+        g_source_remove_poll(&e->mainloop->source, &e->poll_fd);
+        e->poll_fd_added = 0;
+    }
+}
+
+static void glib_io_set_destroy(pa_io_event*e, pa_io_event_destroy_cb_t cb) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->destroy_callback = cb;
+}
+
+/* Time sources */
+
+static pa_time_event* glib_time_new(
+        pa_mainloop_api*m,
+        const struct timeval *tv,
+        pa_time_event_cb_t cb,
+        void *userdata) {
+
+    pa_glib_mainloop *g;
+    pa_time_event *e;
+
+    g_assert(m);
+    g_assert(m->userdata);
+    g_assert(cb);
+
+    g = m->userdata;
+
+    e = pa_xnew(pa_time_event, 1);
+    e->mainloop = g;
+    e->dead = 0;
+
+    if ((e->enabled = !!tv)) {
+        e->timeval = *tv;
+        g->n_enabled_time_events++;
+
+        if (g->cached_next_time_event) {
+            g_assert(g->cached_next_time_event->enabled);
+
+            if (pa_timeval_cmp(tv, &g->cached_next_time_event->timeval) < 0)
+                g->cached_next_time_event = e;
+        }
+    }
+
+    e->callback = cb;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+    PA_LLIST_PREPEND(pa_time_event, g->time_events, e);
+
+    return e;
+}
+
+static void glib_time_restart(pa_time_event*e, const struct timeval *tv) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    if (e->enabled && !tv) {
+        g_assert(e->mainloop->n_enabled_time_events > 0);
+        e->mainloop->n_enabled_time_events--;
+    } else if (!e->enabled && tv)
+        e->mainloop->n_enabled_time_events++;
+
+    if ((e->enabled = !!tv))
+        e->timeval = *tv;
+
+    if (e->mainloop->cached_next_time_event && e->enabled) {
+        g_assert(e->mainloop->cached_next_time_event->enabled);
+
+        if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0)
+            e->mainloop->cached_next_time_event = e;
+    } else if (e->mainloop->cached_next_time_event == e)
+        e->mainloop->cached_next_time_event = NULL;
+ }
+
+static void glib_time_free(pa_time_event *e) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->dead = 1;
+    e->mainloop->time_events_please_scan++;
+
+    if (e->enabled)
+        e->mainloop->n_enabled_time_events--;
+
+    if (e->mainloop->cached_next_time_event == e)
+        e->mainloop->cached_next_time_event = NULL;
+}
+
+static void glib_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->destroy_callback = cb;
+}
+
+/* Deferred sources */
+
+static pa_defer_event* glib_defer_new(
+        pa_mainloop_api*m,
+        pa_defer_event_cb_t cb,
+        void *userdata) {
+
+    pa_defer_event *e;
+    pa_glib_mainloop *g;
+
+    g_assert(m);
+    g_assert(m->userdata);
+    g_assert(cb);
+
+    g = m->userdata;
+
+    e = pa_xnew(pa_defer_event, 1);
+    e->mainloop = g;
+    e->dead = 0;
+
+    e->enabled = 1;
+    g->n_enabled_defer_events++;
+
+    e->callback = cb;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+    PA_LLIST_PREPEND(pa_defer_event, g->defer_events, e);
+    return e;
+}
+
+static void glib_defer_enable(pa_defer_event *e, int b) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    if (e->enabled && !b) {
+        g_assert(e->mainloop->n_enabled_defer_events > 0);
+        e->mainloop->n_enabled_defer_events--;
+    } else if (!e->enabled && b)
+        e->mainloop->n_enabled_defer_events++;
+
+    e->enabled = b;
+}
+
+static void glib_defer_free(pa_defer_event *e) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->dead = 1;
+    e->mainloop->defer_events_please_scan++;
+
+    if (e->enabled) {
+        g_assert(e->mainloop->n_enabled_defer_events > 0);
+        e->mainloop->n_enabled_defer_events--;
+    }
+}
+
+static void glib_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) {
+    g_assert(e);
+    g_assert(!e->dead);
+
+    e->destroy_callback = cb;
+}
+
+/* quit() */
+
+static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) {
+
+    g_warning("quit() ignored");
+
+    /* NOOP */
+}
+
+static pa_time_event* find_next_time_event(pa_glib_mainloop *g) {
+    pa_time_event *t, *n = NULL;
+    g_assert(g);
+
+    if (g->cached_next_time_event)
+        return g->cached_next_time_event;
+
+    for (t = g->time_events; t; t = t->next) {
+
+        if (t->dead || !t->enabled)
+            continue;
+
+        if (!n || pa_timeval_cmp(&t->timeval, &n->timeval) < 0) {
+            n = t;
+
+            /* Shortcut for tv = { 0, 0 } */
+            if (n->timeval.tv_sec <= 0)
+                break;
+        }
+    }
+
+    g->cached_next_time_event = n;
+    return n;
+}
+
+static void scan_dead(pa_glib_mainloop *g) {
+    g_assert(g);
+
+    if (g->io_events_please_scan)
+        cleanup_io_events(g, 0);
+
+    if (g->time_events_please_scan)
+        cleanup_time_events(g, 0);
+
+    if (g->defer_events_please_scan)
+        cleanup_defer_events(g, 0);
+}
+
+static gboolean prepare_func(GSource *source, gint *timeout) {
+    pa_glib_mainloop *g = (pa_glib_mainloop*) source;
+
+    g_assert(g);
+    g_assert(timeout);
+
+    scan_dead(g);
+
+    if (g->n_enabled_defer_events) {
+        *timeout = 0;
+        return TRUE;
+    } else if (g->n_enabled_time_events) {
+        pa_time_event *t;
+        GTimeVal now;
+        struct timeval tvnow;
+        pa_usec_t usec;
+
+        t = find_next_time_event(g);
+        g_assert(t);
+
+        g_source_get_current_time(source, &now);
+        tvnow.tv_sec = now.tv_sec;
+        tvnow.tv_usec = now.tv_usec;
+
+        if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) {
+            *timeout = 0;
+            return TRUE;
+        }
+        usec = pa_timeval_diff(&t->timeval, &tvnow);
+        *timeout = (gint) (usec / 1000);
+    } else
+        *timeout = -1;
+
+    return FALSE;
+}
+static gboolean check_func(GSource *source) {
+    pa_glib_mainloop *g = (pa_glib_mainloop*) source;
+    pa_io_event *e;
+
+    g_assert(g);
+
+    if (g->n_enabled_defer_events)
+        return TRUE;
+    else if (g->n_enabled_time_events) {
+        pa_time_event *t;
+        GTimeVal now;
+        struct timeval tvnow;
+
+        t = find_next_time_event(g);
+        g_assert(t);
+
+        g_source_get_current_time(source, &now);
+        tvnow.tv_sec = now.tv_sec;
+        tvnow.tv_usec = now.tv_usec;
+
+        if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0)
+            return TRUE;
+    }
+
+    for (e = g->io_events; e; e = e->next)
+        if (!e->dead && e->poll_fd.revents != 0)
+            return TRUE;
+
+    return FALSE;
+}
+
+static gboolean dispatch_func(GSource *source, PA_GCC_UNUSED GSourceFunc callback, PA_GCC_UNUSED gpointer userdata) {
+    pa_glib_mainloop *g = (pa_glib_mainloop*) source;
+    pa_io_event *e;
+
+    g_assert(g);
+
+    if (g->n_enabled_defer_events) {
+        pa_defer_event *d;
+
+        for (d = g->defer_events; d; d = d->next) {
+            if (d->dead || !d->enabled)
+                continue;
+
+            break;
+        }
+
+        g_assert(d);
+
+        d->callback(&g->api, d, d->userdata);
+        return TRUE;
+    }
+
+    if (g->n_enabled_time_events) {
+        GTimeVal now;
+        struct timeval tvnow;
+        pa_time_event *t;
+
+        t = find_next_time_event(g);
+        g_assert(t);
+
+        g_source_get_current_time(source, &now);
+        tvnow.tv_sec = now.tv_sec;
+        tvnow.tv_usec = now.tv_usec;
+
+        if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) {
+
+            /* Disable time event */
+            glib_time_restart(t, NULL);
+
+            t->callback(&g->api, t, &t->timeval, t->userdata);
+            return TRUE;
+        }
+    }
+
+    for (e = g->io_events; e; e = e->next)
+        if (!e->dead && e->poll_fd.revents != 0) {
+            e->callback(&g->api, e, e->poll_fd.fd, map_flags_from_glib(e->poll_fd.revents), e->userdata);
+            e->poll_fd.revents = 0;
+            return TRUE;
+        }
+
+    return FALSE;
+}
+
+static const pa_mainloop_api vtable = {
+    .userdata = NULL,
+
+    .io_new = glib_io_new,
+    .io_enable = glib_io_enable,
+    .io_free = glib_io_free,
+    .io_set_destroy= glib_io_set_destroy,
+
+    .time_new = glib_time_new,
+    .time_restart = glib_time_restart,
+    .time_free = glib_time_free,
+    .time_set_destroy = glib_time_set_destroy,
+
+    .defer_new = glib_defer_new,
+    .defer_enable = glib_defer_enable,
+    .defer_free = glib_defer_free,
+    .defer_set_destroy = glib_defer_set_destroy,
+
+    .quit = glib_quit,
+};
+
+pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
+    pa_glib_mainloop *g;
+
+    static GSourceFuncs source_funcs = {
+        prepare_func,
+        check_func,
+        dispatch_func,
+        NULL,
+        NULL,
+        NULL
+    };
+
+    g = (pa_glib_mainloop*) g_source_new(&source_funcs, sizeof(pa_glib_mainloop));
+    g_main_context_ref(g->context = c ? c : g_main_context_default());
+
+    g->api = vtable;
+    g->api.userdata = g;
+
+    PA_LLIST_HEAD_INIT(pa_io_event, g->io_events);
+    PA_LLIST_HEAD_INIT(pa_time_event, g->time_events);
+    PA_LLIST_HEAD_INIT(pa_defer_event, g->defer_events);
+
+    g->n_enabled_defer_events = g->n_enabled_time_events = 0;
+    g->io_events_please_scan = g->time_events_please_scan = g->defer_events_please_scan = 0;
+
+    g->cached_next_time_event = NULL;
+
+    g_source_attach(&g->source, g->context);
+    g_source_set_can_recurse(&g->source, FALSE);
+
+    return g;
+}
+
+void pa_glib_mainloop_free(pa_glib_mainloop* g) {
+    g_assert(g);
+
+    cleanup_io_events(g, 1);
+    cleanup_defer_events(g, 1);
+    cleanup_time_events(g, 1);
+
+    g_main_context_unref(g->context);
+    g_source_destroy(&g->source);
+    g_source_unref(&g->source);
+}
+
+pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) {
+    g_assert(g);
+
+    return &g->api;
+}
diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
new file mode 100644 (file)
index 0000000..60fd61a
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef fooglibmainloophfoo
+#define fooglibmainloophfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <glib.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+
+/** \page glib-mainloop GLIB Main Loop Bindings
+ *
+ * \section overv_sec Overview
+ *
+ * The GLIB main loop bindings are extremely easy to use. All that is
+ * required is to create a pa_glib_mainloop object using
+ * pa_glib_mainloop_new(). When the main loop abstraction is needed, it is
+ * provided by pa_glib_mainloop_get_api().
+ *
+ */
+
+/** \file
+ * GLIB main loop support */
+
+PA_C_DECL_BEGIN
+
+/** An opaque GLIB main loop object */
+typedef struct pa_glib_mainloop pa_glib_mainloop;
+
+/** Create a new GLIB main loop object for the specified GLIB main
+ * loop context. Takes an argument c for the
+ * GMainContext to use. If c is NULL the default context is used. */
+pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c);
+
+/** Free the GLIB main loop object */
+void pa_glib_mainloop_free(pa_glib_mainloop* g);
+
+/** Return the abstract main loop API vtable for the GLIB main loop object */
+pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
new file mode 100644 (file)
index 0000000..9ed541d
--- /dev/null
@@ -0,0 +1,236 @@
+#ifndef foointernalhfoo
+#define foointernalhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/context.h>
+#include <pulse/stream.h>
+#include <pulse/operation.h>
+#include <pulse/subscribe.h>
+
+#include <pulsecore/socket-client.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/dynarray.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/mcalign.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/time-smoother.h>
+
+#include "client-conf.h"
+
+#define DEFAULT_TIMEOUT (30)
+
+struct pa_context {
+    PA_REFCNT_DECLARE;
+
+    pa_proplist *proplist;
+    pa_mainloop_api* mainloop;
+
+    pa_socket_client *client;
+    pa_pstream *pstream;
+    pa_pdispatch *pdispatch;
+
+    pa_dynarray *record_streams, *playback_streams;
+    PA_LLIST_HEAD(pa_stream, streams);
+    PA_LLIST_HEAD(pa_operation, operations);
+
+    uint32_t version;
+    uint32_t ctag;
+    uint32_t csyncid;
+    uint32_t error;
+    pa_context_state_t state;
+
+    pa_context_notify_cb_t state_callback;
+    void *state_userdata;
+    pa_context_subscribe_cb_t subscribe_callback;
+    void *subscribe_userdata;
+
+    pa_mempool *mempool;
+
+    pa_bool_t is_local:1;
+    pa_bool_t do_autospawn:1;
+    pa_bool_t do_shm:1;
+    int autospawn_lock_fd;
+    pa_spawn_api spawn_api;
+
+    pa_strlist *server_list;
+
+    char *server;
+
+    pa_client_conf *conf;
+
+    uint32_t client_index;
+};
+
+#define PA_MAX_WRITE_INDEX_CORRECTIONS 32
+
+typedef struct pa_index_correction {
+    uint32_t tag;
+    int64_t value;
+    pa_bool_t valid:1;
+    pa_bool_t absolute:1;
+    pa_bool_t corrupt:1;
+} pa_index_correction;
+
+struct pa_stream {
+    PA_REFCNT_DECLARE;
+    PA_LLIST_FIELDS(pa_stream);
+
+    pa_context *context;
+    pa_mainloop_api *mainloop;
+
+    uint32_t direct_on_input;
+
+    pa_stream_direction_t direction;
+    pa_stream_state_t state;
+    pa_stream_flags_t flags;
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+
+    pa_proplist *proplist;
+
+    pa_bool_t channel_valid:1;
+    pa_bool_t suspended:1;
+    pa_bool_t corked:1;
+    pa_bool_t timing_info_valid:1;
+    pa_bool_t auto_timing_update_requested:1;
+
+    uint32_t channel;
+    uint32_t syncid;
+    uint32_t stream_index;
+
+    uint32_t requested_bytes;
+    pa_buffer_attr buffer_attr;
+
+    uint32_t device_index;
+    char *device_name;
+
+    pa_memchunk peek_memchunk;
+    void *peek_data;
+    pa_memblockq *record_memblockq;
+
+    /* Store latest latency info */
+    pa_timing_info timing_info;
+
+    /* Use to make sure that time advances monotonically */
+    pa_usec_t previous_time;
+
+    /* time updates with tags older than these are invalid */
+    uint32_t write_index_not_before;
+    uint32_t read_index_not_before;
+
+    /* Data about individual timing update correctoins */
+    pa_index_correction write_index_corrections[PA_MAX_WRITE_INDEX_CORRECTIONS];
+    int current_write_index_correction;
+
+    /* Latency interpolation stuff */
+    pa_time_event *auto_timing_update_event;
+
+    pa_smoother *smoother;
+
+    /* Callbacks */
+    pa_stream_notify_cb_t state_callback;
+    void *state_userdata;
+    pa_stream_request_cb_t read_callback;
+    void *read_userdata;
+    pa_stream_request_cb_t write_callback;
+    void *write_userdata;
+    pa_stream_notify_cb_t overflow_callback;
+    void *overflow_userdata;
+    pa_stream_notify_cb_t underflow_callback;
+    void *underflow_userdata;
+    pa_stream_notify_cb_t latency_update_callback;
+    void *latency_update_userdata;
+    pa_stream_notify_cb_t moved_callback;
+    void *moved_userdata;
+    pa_stream_notify_cb_t suspended_callback;
+    void *suspended_userdata;
+    pa_stream_notify_cb_t started_callback;
+    void *started_userdata;
+};
+
+typedef void (*pa_operation_cb_t)(void);
+
+struct pa_operation {
+    PA_REFCNT_DECLARE;
+
+    pa_context *context;
+    pa_stream *stream;
+
+    PA_LLIST_FIELDS(pa_operation);
+
+    pa_operation_state_t state;
+    void *userdata;
+    pa_operation_cb_t callback;
+
+    void *private; /* some operations might need this */
+};
+
+void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
+void pa_operation_done(pa_operation *o);
+
+void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+void pa_context_fail(pa_context *c, int error);
+int pa_context_set_error(pa_context *c, int error);
+void pa_context_set_state(pa_context *c, pa_context_state_t st);
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail);
+pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
+
+void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
+
+pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag);
+
+#define PA_CHECK_VALIDITY(context, expression, error) do { \
+        if (!(expression)) \
+            return -pa_context_set_error((context), (error)); \
+} while(0)
+
+
+#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) do { \
+        if (!(expression)) { \
+            pa_context_set_error((context), (error)); \
+            return value; \
+        } \
+} while(0)
+
+#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
+
+#endif
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
new file mode 100644 (file)
index 0000000..4be2c62
--- /dev/null
@@ -0,0 +1,1536 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/context.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+
+#include "introspect.h"
+
+/*** Statistics ***/
+
+static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    pa_stat_info i, *p = &i;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    memset(&i, 0, sizeof(i));
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        p = NULL;
+    } else if (pa_tagstruct_getu32(t, &i.memblock_total) < 0 ||
+               pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 ||
+               pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 ||
+               pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 ||
+               pa_tagstruct_getu32(t, &i.scache_size) < 0) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_stat_info_cb_t cb = (pa_stat_info_cb_t) o->callback;
+        cb(o->context, p, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Server Info ***/
+
+static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    pa_server_info i, *p = &i;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    memset(&i, 0, sizeof(i));
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        p = NULL;
+    } else if (pa_tagstruct_gets(t, &i.server_name) < 0 ||
+               pa_tagstruct_gets(t, &i.server_version) < 0 ||
+               pa_tagstruct_gets(t, &i.user_name) < 0 ||
+               pa_tagstruct_gets(t, &i.host_name) < 0 ||
+               pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+               pa_tagstruct_gets(t, &i.default_sink_name) < 0 ||
+               pa_tagstruct_gets(t, &i.default_source_name) < 0 ||
+               pa_tagstruct_getu32(t, &i.cookie) < 0 ||
+               !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_server_info_cb_t cb = (pa_server_info_cb_t) o->callback;
+        cb(o->context, p, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SERVER_INFO, context_get_server_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Sink Info ***/
+
+static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+        uint32_t flags;
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_sink_info i;
+            pa_bool_t mute = FALSE;
+
+            memset(&i, 0, sizeof(i));
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_gets(t, &i.description) < 0 ||
+                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                pa_tagstruct_get_boolean(t, &mute) < 0 ||
+                pa_tagstruct_getu32(t, &i.monitor_source) < 0 ||
+                pa_tagstruct_gets(t, &i.monitor_source_name) < 0 ||
+                pa_tagstruct_get_usec(t, &i.latency) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                pa_tagstruct_getu32(t, &flags) < 0 ||
+                (o->context->version >= 13 &&
+                 (pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
+                  pa_tagstruct_get_usec(t, &i.configured_latency) < 0))) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                pa_proplist_free(i.proplist);
+                goto finish;
+            }
+
+            i.mute = (int) mute;
+            i.flags = (pa_sink_flags_t) flags;
+
+            if (o->callback) {
+                pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INFO_LIST, context_get_sink_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INFO, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+/*** Source info ***/
+
+static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_source_info i;
+            uint32_t flags;
+            pa_bool_t mute = FALSE;
+
+            memset(&i, 0, sizeof(i));
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_gets(t, &i.description) < 0 ||
+                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                pa_tagstruct_get_boolean(t, &mute) < 0 ||
+                pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 ||
+                pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 ||
+                pa_tagstruct_get_usec(t, &i.latency) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                pa_tagstruct_getu32(t, &flags) < 0 ||
+                (o->context->version >= 13 &&
+                 (pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
+                  pa_tagstruct_get_usec(t, &i.configured_latency) < 0))) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                pa_proplist_free(i.proplist);
+                goto finish;
+            }
+
+            i.mute = (int) mute;
+            i.flags = (pa_source_flags_t) flags;
+
+            if (o->callback) {
+                pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_INFO_LIST, context_get_source_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_INFO, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+/*** Client info ***/
+
+static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_client_info i;
+
+            memset(&i, 0, sizeof(i));
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                pa_proplist_free(i.proplist);
+                goto finish;
+            }
+
+            if (o->callback) {
+                pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_CLIENT_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_client_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Module info ***/
+
+static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_module_info i;
+            pa_bool_t auto_unload = FALSE;
+            memset(&i, 0, sizeof(i));
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_gets(t, &i.argument) < 0 ||
+                pa_tagstruct_getu32(t, &i.n_used) < 0 ||
+                pa_tagstruct_get_boolean(t, &auto_unload) < 0) {
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            i.auto_unload = (int) auto_unload;
+
+            if (o->callback) {
+                pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+        }
+    }
+
+    if (o->callback) {
+        pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_MODULE_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_module_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_MODULE_INFO_LIST, context_get_module_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Sink input info ***/
+
+static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_sink_input_info i;
+            pa_bool_t mute = FALSE;
+
+            memset(&i, 0, sizeof(i));
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_getu32(t, &i.client) < 0 ||
+                pa_tagstruct_getu32(t, &i.sink) < 0 ||
+                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
+                pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
+                pa_tagstruct_gets(t, &i.resample_method) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                (o->context->version >= 11 && pa_tagstruct_get_boolean(t, &mute) < 0) ||
+                (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                pa_proplist_free(i.proplist);
+                goto finish;
+            }
+
+            i.mute = (int) mute;
+
+            if (o->callback) {
+                pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SINK_INPUT_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_input_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INPUT_INFO_LIST, context_get_sink_input_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Source output info ***/
+
+static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_source_output_info i;
+
+            memset(&i, 0, sizeof(i));
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_getu32(t, &i.client) < 0 ||
+                pa_tagstruct_getu32(t, &i.source) < 0 ||
+                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
+                pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
+                pa_tagstruct_gets(t, &i.resample_method) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                pa_proplist_free(i.proplist);
+                goto finish;
+            }
+
+            if (o->callback) {
+                pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_output_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_source_output_info_list(pa_context *c,  pa_source_output_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, context_get_source_output_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+/*** Volume manipulation ***/
+
+pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(name);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_MUTE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(name);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_MUTE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_VOLUME, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_MUTE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_VOLUME, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(name);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_VOLUME, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_MUTE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(name);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_MUTE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+/** Sample Cache **/
+
+static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_sample_info i;
+            pa_bool_t lazy = FALSE;
+
+            memset(&i, 0, sizeof(i));
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                pa_tagstruct_get_usec(t, &i.duration) < 0 ||
+                pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_getu32(t, &i.bytes) < 0 ||
+                pa_tagstruct_get_boolean(t, &lazy) < 0 ||
+                pa_tagstruct_gets(t, &i.filename) < 0 ||
+                (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            i.lazy = (int) lazy;
+
+            if (o->callback) {
+                pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+        }
+    }
+
+    if (o->callback) {
+        pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SAMPLE_INFO, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_SAMPLE_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, command, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+    return command_kill(c, PA_COMMAND_KILL_CLIENT, idx, cb, userdata);
+}
+
+pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+    return command_kill(c, PA_COMMAND_KILL_SINK_INPUT, idx, cb, userdata);
+}
+
+pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+    return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, idx, cb, userdata);
+}
+
+static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t idx;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        idx = PA_INVALID_INDEX;
+    } else if (pa_tagstruct_getu32(t, &idx) ||
+               !pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_context_index_cb_t cb = (pa_context_index_cb_t) o->callback;
+        cb(o->context, idx, o->userdata);
+    }
+
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_LOAD_MODULE, &tag);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_puts(t, argument);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_index_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
+    return command_kill(c, PA_COMMAND_UNLOAD_MODULE, idx, cb, userdata);
+}
+
+/*** Autoload stuff ***/
+
+static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_autoload_info i;
+
+            memset(&i, 0, sizeof(i));
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_getu32(t, &i.type) < 0 ||
+                pa_tagstruct_gets(t, &i.module) < 0 ||
+                pa_tagstruct_gets(t, &i.argument) < 0) {
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (o->callback) {
+                pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+        }
+    }
+
+    if (o->callback) {
+        pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
+pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_putu32(t, type);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
+pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+
+PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
+pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+PA_WARN_REFERENCE(pa_context_add_autoload, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
+pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, module && *module, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_ADD_AUTOLOAD, &tag);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_putu32(t, type);
+    pa_tagstruct_puts(t, module);
+    pa_tagstruct_puts(t, argument);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_index_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
+pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_putu32(t, type);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
+pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, sink_name && *sink_name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SINK_INPUT, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, sink_name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, sink_idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SINK_INPUT, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu32(t, sink_idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, source_name && *source_name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SOURCE_OUTPUT, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, source_name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 10, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, source_idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_MOVE_SOURCE_OUTPUT, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu32(t, source_idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !sink_name || *sink_name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SINK, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, sink_name);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SINK, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, idx == PA_INVALID_INDEX ? "" : NULL);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !source_name || *source_name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SOURCE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, source_name);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 11, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SUSPEND_SOURCE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, idx == PA_INVALID_INDEX ? "" : NULL);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
new file mode 100644 (file)
index 0000000..c8c13a7
--- /dev/null
@@ -0,0 +1,597 @@
+#ifndef foointrospecthfoo
+#define foointrospecthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/operation.h>
+#include <pulse/context.h>
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+#include <pulse/proplist.h>
+
+/** \page introspect Server Query and Control
+ *
+ * \section overv_sec Overview
+ *
+ * Sometimes it is necessary to query and modify global settings in the
+ * server. For this, PulseAudio has the introspection API. It can list sinks,
+ * sources, samples and other aspects of the server. It can also modify the
+ * attributes of the server that will affect operations on a global level,
+ * and not just the application's context.
+ *
+ * \section query_sec Querying
+ *
+ * All querying is done through callbacks. This design is necessary to
+ * maintain an asynchronous design. The client will request the information
+ * and some time later, the server will respond with the desired data.
+ *
+ * Some objects can have multiple entries at the server. When requesting all
+ * of these at once, the callback will be called multiple times, once for
+ * each object. When the list has been exhausted, the callback will be called
+ * without an information structure and the eol parameter set to a non-zero
+ * value.
+ *
+ * Note that even if a single object is requested, and not the entire list,
+ * the terminating call will still be made.
+ *
+ * If an error occurs, the callback will be called without and information
+ * structure and eol set to zero.
+ *
+ * Data members in the information structures are only valid during the
+ * duration of the callback. If they are required after the callback is
+ * finished, a deep copy must be performed.
+ *
+ * \subsection server_subsec Server Information
+ *
+ * The server can be queried about its name, the environment it's running on
+ * and the currently active global defaults. Calling
+ * pa_context_get_server_info() will get access to a pa_server_info structure
+ * containing all of these.
+ *
+ * \subsection memstat_subsec Memory Usage
+ *
+ * Statistics about memory usage can be fetched using pa_context_stat(),
+ * giving a pa_stat_info structure.
+ *
+ * \subsection sinksrc_subsec Sinks and Sources
+ *
+ * The server can have an arbitrary number of sinks and sources. Each sink
+ * and source have both an index and a name associated with it. As such
+ * there are three ways to get access to them:
+ *
+ * \li By index - pa_context_get_sink_info_by_index() /
+ *                pa_context_get_source_info_by_index()
+ * \li By name - pa_context_get_sink_info_by_name() /
+ *               pa_context_get_source_info_by_name()
+ * \li All - pa_context_get_sink_info_list() /
+ *           pa_context_get_source_info_list()
+ *
+ * All three method use the same callback and will provide a pa_sink_info or
+ * pa_source_info structure.
+ *
+ * \subsection siso_subsec Sink Inputs and Source Outputs
+ *
+ * Sink inputs and source outputs are the representations of the client ends
+ * of streams inside the server. I.e. they connect a client stream to one of
+ * the global sinks or sources.
+ *
+ * Sink inputs and source outputs only have an index to identify them. As
+ * such, there are only two ways to get information about them:
+ *
+ * \li By index - pa_context_get_sink_input_info() /
+ *                pa_context_get_source_output_info()
+ * \li All - pa_context_get_sink_input_info_list() /
+ *           pa_context_get_source_output_info_list()
+ *
+ * The structure returned is the pa_sink_input_info or pa_source_output_info
+ * structure.
+ *
+ * \subsection samples_subsec Samples
+ *
+ * The list of cached samples can be retrieved from the server. Three methods
+ * exist for querying the sample cache list:
+ *
+ * \li By index - pa_context_get_sample_info_by_index()
+ * \li By name - pa_context_get_sample_info_by_name()
+ * \li All - pa_context_get_sample_info_list()
+ *
+ * Note that this only retrieves information about the sample, not the sample
+ * data itself.
+ *
+ * \subsection module_subsec Driver Modules
+ *
+ * PulseAudio driver modules are identified by index and are retrieved using either
+ * pa_context_get_module_info() or pa_context_get_module_info_list(). The
+ * information structure is called pa_module_info.
+ *
+ * \subsection autoload_subsec Autoload Entries
+ *
+ * Modules can be autoloaded as a result of a client requesting a certain
+ * sink or source. This mapping between sink/source names and modules can be
+ * queried from the server:
+ *
+ * \li By index - pa_context_get_autoload_info_by_index()
+ * \li By sink/source name - pa_context_get_autoload_info_by_name()
+ * \li All - pa_context_get_autoload_info_list()
+ *
+ * \subsection client_subsec Clients
+ *
+ * PulseAudio clients are also identified by index and are retrieved using
+ * either pa_context_get_client_info() or pa_context_get_client_info_list().
+ * The information structure is called pa_client_info.
+ *
+ * \section ctrl_sec Control
+ *
+ * Some parts of the server are only possible to read, but most can also be
+ * modified in different ways. Note that these changes will affect all
+ * connected clients and not just the one issuing the request.
+ *
+ * \subsection sinksrc_subsec Sinks and Sources
+ *
+ * The most common change one would want to do to sinks and sources is to
+ * modify the volume of the audio. Identical to how sinks and sources can
+ * be queried, there are two ways of identifying them:
+ *
+ * \li By index - pa_context_set_sink_volume_by_index() /
+ *                pa_context_set_source_volume_by_index()
+ * \li By name - pa_context_set_sink_volume_by_name() /
+ *               pa_context_set_source_volume_by_name()
+ *
+ * It is also possible to mute a sink or source:
+ *
+ * \li By index - pa_context_set_sink_mute_by_index() /
+ *                pa_context_set_source_mute_by_index()
+ * \li By name - pa_context_set_sink_mute_by_name() /
+ *               pa_context_set_source_mute_by_name()
+ *
+ * \subsection siso_subsec Sink Inputs and Source Outputs
+ *
+ * If an application desires to modify the volume of just a single stream
+ * (commonly one of its own streams), this can be done by setting the volume
+ * of its associated sink input, using pa_context_set_sink_input_volume().
+ *
+ * There is no support for modifying the volume of source outputs.
+ *
+ * It is also possible to remove sink inputs and source outputs, terminating
+ * the streams associated with them:
+ *
+ * \li Sink input - pa_context_kill_sink_input()
+ * \li Source output - pa_context_kill_source_output()
+ *
+ * \subsection module_subsec Modules
+ *
+ * Server modules can be remotely loaded and unloaded using
+ * pa_context_load_module() and pa_context_unload_module().
+ *
+ * \subsection autoload_subsec Autoload Entries
+ *
+ * New module autoloading rules can be added, and existing can be removed
+ * using pa_context_add_autoload() and pa_context_remove_autoload_by_index()
+ * / pa_context_remove_autoload_by_name().
+ *
+ * \subsection client_subsec Clients
+ *
+ * The only operation supported on clients, is the possibility of kicking
+ * them off the server using pa_context_kill_client().
+ */
+
+/** \file
+ *
+ * Routines for daemon introspection.
+ */
+
+PA_C_DECL_BEGIN
+
+#define PA_PORT_DIGITAL "spdif"
+#define PA_PORT_ANALOG_STEREO "analog-stereo"
+#define PA_PORT_ANALOG_5_1 "analog-5-1"
+#define PA_PORT_ANALOG_4_0 "analog-4-0"
+
+/** @{ \name Sinks */
+
+/** Stores information about sinks. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_sink_info {
+    const char *name;                  /**< Name of the sink */
+    uint32_t index;                    /**< Index of the sink */
+    const char *description;           /**< Description of this sink */
+    pa_sample_spec sample_spec;        /**< Sample spec of this sink */
+    pa_channel_map channel_map;        /**< Channel map */
+    uint32_t owner_module;             /**< Index of the owning module of this sink, or PA_INVALID_INDEX */
+    pa_cvolume volume;                 /**< Volume of the sink */
+    int mute;                          /**< Mute switch of the sink */
+    uint32_t monitor_source;           /**< Index of the monitor source connected to this sink */
+    const char *monitor_source_name;   /**< The name of the monitor source */
+    pa_usec_t latency;                 /**< Length of queued audio in the output buffer. */
+    const char *driver;                /**< Driver name. */
+    pa_sink_flags_t flags;             /**< Flags */
+    pa_proplist *proplist;             /**< Property list \since 0.9.11 */
+    pa_usec_t configured_latency;      /**< The latency this device has been configured to. \since 0.9.11 */
+} pa_sink_info;
+
+/** Callback prototype for pa_context_get_sink_info_by_name() and friends */
+typedef void (*pa_sink_info_cb_t)(pa_context *c, const pa_sink_info *i, int eol, void *userdata);
+
+/** Get information about a sink by its name */
+pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata);
+
+/** Get information about a sink by its index */
+pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_sink_info_cb_t cb, void *userdata);
+
+/** Get the complete sink list */
+pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata);
+
+/** Set the volume of a sink device specified by its index */
+pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a sink device specified by its name */
+pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink device specified by its index */
+pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink device specified by its name */
+pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Suspend/Resume a sink. \since 0.9.7 */
+pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata);
+
+/** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */
+pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend,  pa_context_success_cb_t cb, void* userdata);
+
+/** @} */
+
+/** @{ \name Sources */
+
+/** Stores information about sources. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_source_info {
+    const char *name;                   /**< Name of the source */
+    uint32_t index;                     /**< Index of the source */
+    const char *description;            /**< Description of this source */
+    pa_sample_spec sample_spec;         /**< Sample spec of this source */
+    pa_channel_map channel_map;         /**< Channel map */
+    uint32_t owner_module;              /**< Owning module index, or PA_INVALID_INDEX */
+    pa_cvolume volume;                  /**< Volume of the source */
+    int mute;                           /**< Mute switch of the sink */
+    uint32_t monitor_of_sink;           /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */
+    const char *monitor_of_sink_name;   /**< Name of the owning sink, or PA_INVALID_INDEX */
+    pa_usec_t latency;                  /**< Length of filled record buffer of this source. */
+    const char *driver;                 /**< Driver name */
+    pa_source_flags_t flags;            /**< Flags */
+    pa_proplist *proplist;              /**< Property list \since 0.9.11 */
+    pa_usec_t configured_latency;       /**< The latency this device has been configured to. \since 0.9.11 */
+} pa_source_info;
+
+/** Callback prototype for pa_context_get_source_info_by_name() and friends */
+typedef void (*pa_source_info_cb_t)(pa_context *c, const pa_source_info *i, int eol, void *userdata);
+
+/** Get information about a source by its name */
+pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata);
+
+/** Get information about a source by its index */
+pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa_source_info_cb_t cb, void *userdata);
+
+/** Get the complete source list */
+pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata);
+
+/** Set the volume of a source device specified by its index */
+pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the volume of a source device specified by its name */
+pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source device specified by its index */
+pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source device specified by its name */
+pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Server */
+
+/** Server information. Please note that this structure can be
+ * extended as part of evolutionary API updates at any time in any new
+ * release. */
+typedef struct pa_server_info {
+    const char *user_name;              /**< User name of the daemon process */
+    const char *host_name;              /**< Host name the daemon is running on */
+    const char *server_version;         /**< Version string of the daemon */
+    const char *server_name;            /**< Server package name (usually "pulseaudio") */
+    pa_sample_spec sample_spec;         /**< Default sample specification */
+    const char *default_sink_name;      /**< Name of default sink. */
+    const char *default_source_name;    /**< Name of default sink. */
+    uint32_t cookie;                    /**< A random cookie for identifying this instance of PulseAudio. */
+} pa_server_info;
+
+/** Callback prototype for pa_context_get_server_info() */
+typedef void (*pa_server_info_cb_t) (pa_context *c, const pa_server_info*i, void *userdata);
+
+/** Get some information about the server */
+pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Modules */
+
+/** Stores information about modules. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_module_info {
+    uint32_t index;                     /**< Index of the module */
+    const char*name,                    /**< Name of the module */
+        *argument;                      /**< Argument string of the module */
+    uint32_t n_used;                    /**< Usage counter or PA_INVALID_INDEX */
+    int auto_unload;                    /**< Non-zero if this is an autoloaded module */
+} pa_module_info;
+
+/** Callback prototype for pa_context_get_module_info() and firends*/
+typedef void (*pa_module_info_cb_t) (pa_context *c, const pa_module_info*i, int eol, void *userdata);
+
+/** Get some information about a module by its index */
+pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata);
+
+/** Get the complete list of currently loaded modules */
+pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata);
+
+/** Callback prototype for pa_context_load_module() */
+typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+
+/** Load a module. */
+pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata);
+
+/** Unload a module. */
+pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Clients */
+
+/** Stores information about clients. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_client_info {
+    uint32_t index;                      /**< Index of this client */
+    const char *name;                    /**< Name of this client */
+    uint32_t owner_module;               /**< Index of the owning module, or PA_INVALID_INDEX */
+    const char *driver;                  /**< Driver name */
+    pa_proplist *proplist;               /**< Property list \since 0.9.11 */
+} pa_client_info;
+
+/** Callback prototype for pa_context_get_client_info() and firends*/
+typedef void (*pa_client_info_cb_t) (pa_context *c, const pa_client_info*i, int eol, void *userdata);
+
+/** Get information about a client by its index */
+pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata);
+
+/** Get the complete client list */
+pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata);
+
+/** Kill a client. */
+pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Sink Inputs */
+
+/** Stores information about sink inputs. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_sink_input_info {
+    uint32_t index;                      /**< Index of the sink input */
+    const char *name;                    /**< Name of the sink input */
+    uint32_t owner_module;               /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
+    uint32_t client;                     /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
+    uint32_t sink;                       /**< Index of the connected sink */
+    pa_sample_spec sample_spec;          /**< The sample specification of the sink input */
+    pa_channel_map channel_map;          /**< Channel map */
+    pa_cvolume volume;                   /**< The volume of this sink input */
+    pa_usec_t buffer_usec;               /**< Latency due to buffering in sink input, see pa_latency_info for details */
+    pa_usec_t sink_usec;                 /**< Latency of the sink device, see pa_latency_info for details */
+    const char *resample_method;         /**< Thre resampling method used by this sink input. */
+    const char *driver;                  /**< Driver name */
+    int mute;                            /**< Stream muted \since 0.9.7 */
+    pa_proplist *proplist;               /**< Property list \since 0.9.11 */
+} pa_sink_input_info;
+
+/** Callback prototype for pa_context_get_sink_input_info() and firends*/
+typedef void (*pa_sink_input_info_cb_t) (pa_context *c, const pa_sink_input_info *i, int eol, void *userdata);
+
+/** Get some information about a sink input by its index */
+pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata);
+
+/** Get the complete sink input list */
+pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata);
+
+/** Move the specified sink input to a different sink. \since 0.9.5 */
+pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata);
+
+/** Move the specified sink input to a different sink. \since 0.9.5 */
+pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata);
+
+/** Set the volume of a sink input stream */
+pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a sink input stream \since 0.9.7 */
+pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
+/** Kill a sink input. */
+pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Source Outputs */
+
+/** Stores information about source outputs. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_source_output_info {
+    uint32_t index;                      /**< Index of the sink input */
+    const char *name;                    /**< Name of the sink input */
+    uint32_t owner_module;               /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */
+    uint32_t client;                     /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */
+    uint32_t source;                     /**< Index of the connected source */
+    pa_sample_spec sample_spec;          /**< The sample specification of the source output */
+    pa_channel_map channel_map;          /**< Channel map */
+    pa_usec_t buffer_usec;               /**< Latency due to buffering in the source output, see pa_latency_info for details. */
+    pa_usec_t source_usec;               /**< Latency of the source device, see pa_latency_info for details. */
+    const char *resample_method;         /**< Thre resampling method used by this source output. */
+    const char *driver;                  /**< Driver name */
+    pa_proplist *proplist;               /**< Property list \since 0.9.11 */
+} pa_source_output_info;
+
+/** Callback prototype for pa_context_get_source_output_info() and firends*/
+typedef void (*pa_source_output_info_cb_t) (pa_context *c, const pa_source_output_info *i, int eol, void *userdata);
+
+/** Get information about a source output by its index */
+pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata);
+
+/** Get the complete list of source outputs */
+pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata);
+
+/** Move the specified source output to a different source. \since 0.9.5 */
+pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata);
+
+/** Move the specified source output to a different source. \since 0.9.5 */
+pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata);
+
+/** Suspend/Resume a source. \since 0.9.7 */
+pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata);
+
+/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */
+pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
+
+/** Kill a source output. */
+pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Statistics */
+
+/** Memory block statistics. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_stat_info {
+    uint32_t memblock_total;           /**< Currently allocated memory blocks */
+    uint32_t memblock_total_size;      /**< Currentl total size of allocated memory blocks */
+    uint32_t memblock_allocated;       /**< Allocated memory blocks during the whole lifetime of the daemon */
+    uint32_t memblock_allocated_size;  /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */
+    uint32_t scache_size;              /**< Total size of all sample cache entries. */
+} pa_stat_info;
+
+/** Callback prototype for pa_context_stat() */
+typedef void (*pa_stat_info_cb_t) (pa_context *c, const pa_stat_info *i, void *userdata);
+
+/** Get daemon memory block statistics */
+pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata);
+
+/** @} */
+
+/** @{ \name Cached Samples */
+
+/** Stores information about sample cache entries. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_sample_info {
+    uint32_t index;                       /**< Index of this entry */
+    const char *name;                     /**< Name of this entry */
+    pa_cvolume volume;                    /**< Default volume of this entry */
+    pa_sample_spec sample_spec;           /**< Sample specification of the sample */
+    pa_channel_map channel_map;           /**< The channel map */
+    pa_usec_t duration;                   /**< Duration of this entry */
+    uint32_t bytes;                       /**< Length of this sample in bytes. */
+    int lazy;                             /**< Non-zero when this is a lazy cache entry. */
+    const char *filename;                 /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. */
+    pa_proplist *proplist;                /**< Property list for this sample. \since 0.9.11 */
+} pa_sample_info;
+
+/** Callback prototype for pa_context_get_sample_info_by_name() and firends */
+typedef void (*pa_sample_info_cb_t)(pa_context *c, const pa_sample_info *i, int eol, void *userdata);
+
+/** Get information about a sample by its name */
+pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata);
+
+/** Get information about a sample by its index */
+pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata);
+
+/** Get the complete list of samples stored in the daemon. */
+pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata);
+
+/** @} */
+
+/** \cond fulldocs */
+
+/** @{ \name Autoload Entries */
+
+/** Type of an autoload entry. */
+typedef enum pa_autoload_type {
+    PA_AUTOLOAD_SINK = 0,
+    PA_AUTOLOAD_SOURCE = 1
+} pa_autoload_type_t;
+
+/** Stores information about autoload entries. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release. */
+typedef struct pa_autoload_info {
+    uint32_t index;               /**< Index of this autoload entry */
+    const char *name;             /**< Name of the sink or source */
+    pa_autoload_type_t type;   /**< Type of the autoload entry */
+    const char *module;           /**< Module name to load */
+    const char *argument;         /**< Argument string for module */
+} pa_autoload_info;
+
+/** Callback prototype for pa_context_get_autoload_info_by_name() and firends */
+typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata);
+
+/** Get info about a specific autoload entry. */
+pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
+
+/** Get info about a specific autoload entry. */
+pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
+
+/** Get the complete list of autoload entries. */
+pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
+
+/** Add a new autoload entry. */
+pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata) PA_GCC_DEPRECATED;
+
+/** Remove an autoload entry. */
+pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
+
+/** Remove an autoload entry. */
+pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
+
+/** @} */
+
+/** \endcond */
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c
new file mode 100644 (file)
index 0000000..90aff16
--- /dev/null
@@ -0,0 +1,75 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
+
+#include "mainloop-api.h"
+
+struct once_info {
+    void (*callback)(pa_mainloop_api*m, void *userdata);
+    void *userdata;
+};
+
+static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+    struct once_info *i = userdata;
+
+    pa_assert(m);
+    pa_assert(i);
+
+    pa_assert(i->callback);
+    i->callback(m, i->userdata);
+
+    pa_assert(m->defer_free);
+    m->defer_free(e);
+}
+
+static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) {
+    struct once_info *i = userdata;
+
+    pa_assert(m);
+    pa_assert(i);
+    pa_xfree(i);
+}
+
+void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) {
+    struct once_info *i;
+    pa_defer_event *e;
+
+    pa_assert(m);
+    pa_assert(callback);
+
+    i = pa_xnew(struct once_info, 1);
+    i->callback = callback;
+    i->userdata = userdata;
+
+    pa_assert(m->defer_new);
+    pa_assert_se(e = m->defer_new(m, once_callback, i));
+    m->defer_set_destroy(e, free_callback);
+}
diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h
new file mode 100644 (file)
index 0000000..53c7411
--- /dev/null
@@ -0,0 +1,122 @@
+#ifndef foomainloopapihfoo
+#define foomainloopapihfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/time.h>
+#include <time.h>
+
+#include <pulse/cdecl.h>
+
+/** \file
+ *
+ * Main loop abstraction layer. Both the PulseAudio core and the
+ * PulseAudio client library use a main loop abstraction layer. Due to
+ * this it is possible to embed PulseAudio into other
+ * applications easily. Two main loop implemenations are
+ * currently available:
+ * \li A minimal implementation based on the C library's poll() function (See \ref mainloop.h)
+ * \li A wrapper around the GLIB main loop. Use this to embed PulseAudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h)
+ *
+ * The structure pa_mainloop_api is used as vtable for the main loop abstraction.
+ *
+ * This mainloop abstraction layer has no direct support for UNIX signals. Generic, mainloop implementation agnostic support is available throught \ref mainloop-signal.h.
+ * */
+
+PA_C_DECL_BEGIN
+
+/** An abstract mainloop API vtable */
+typedef struct pa_mainloop_api pa_mainloop_api;
+
+/** A bitmask for IO events */
+typedef enum pa_io_event_flags {
+    PA_IO_EVENT_NULL = 0,     /**< No event */
+    PA_IO_EVENT_INPUT = 1,    /**< Input event */
+    PA_IO_EVENT_OUTPUT = 2,   /**< Output event */
+    PA_IO_EVENT_HANGUP = 4,   /**< Hangup event */
+    PA_IO_EVENT_ERROR = 8     /**< Error event */
+} pa_io_event_flags_t;
+
+/** An opaque IO event source object */
+typedef struct pa_io_event pa_io_event;
+/** An IO event callback protoype \since 0.9.3 */
+typedef void (*pa_io_event_cb_t)(pa_mainloop_api*ea, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata);
+/** A IO event destroy callback prototype \ since 0.9.3 */
+typedef void (*pa_io_event_destroy_cb_t)(pa_mainloop_api*a, pa_io_event *e, void *userdata);
+
+/** An opaque timer event source object */
+typedef struct pa_time_event pa_time_event;
+/** A time event callback prototype \since 0.9.3 */
+typedef void (*pa_time_event_cb_t)(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata);
+/** A time event destroy callback prototype \ since 0.9.3 */
+typedef void (*pa_time_event_destroy_cb_t)(pa_mainloop_api*a, pa_time_event *e, void *userdata);
+
+/** An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */
+typedef struct pa_defer_event pa_defer_event;
+/** A defer event callback protoype \since 0.9.3 */
+typedef void (*pa_defer_event_cb_t)(pa_mainloop_api*a, pa_defer_event* e, void *userdata);
+/** A defer event destroy callback prototype \ since 0.9.3 */
+typedef void (*pa_defer_event_destroy_cb_t)(pa_mainloop_api*a, pa_defer_event *e, void *userdata);
+
+/** An abstract mainloop API vtable */
+struct pa_mainloop_api  {
+    /** A pointer to some private, arbitrary data of the main loop implementation */
+    void *userdata;
+
+    /** Create a new IO event source object */
+    pa_io_event* (*io_new)(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata);
+    /** Enable or disable IO events on this object */
+    void (*io_enable)(pa_io_event* e, pa_io_event_flags_t events);
+    /** Free a IO event source object */
+    void (*io_free)(pa_io_event* e);
+    /** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */
+    void (*io_set_destroy)(pa_io_event *e, pa_io_event_destroy_cb_t cb);
+
+    /** Create a new timer event source object for the specified Unix time */
+    pa_time_event* (*time_new)(pa_mainloop_api*a, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata);
+    /** Restart a running or expired timer event source with a new Unix time */
+    void (*time_restart)(pa_time_event* e, const struct timeval *tv);
+    /** Free a deferred timer event source object */
+    void (*time_free)(pa_time_event* e);
+    /** Set a function that is called when the timer event source is destroyed. Use this to free the userdata argument if required */
+    void (*time_set_destroy)(pa_time_event *e, pa_time_event_destroy_cb_t cb);
+
+    /** Create a new deferred event source object */
+    pa_defer_event* (*defer_new)(pa_mainloop_api*a, pa_defer_event_cb_t cb, void *userdata);
+    /** Enable or disable a deferred event source temporarily */
+    void (*defer_enable)(pa_defer_event* e, int b);
+    /** Free a deferred event source object */
+    void (*defer_free)(pa_defer_event* e);
+    /** Set a function that is called when the deferred event source is destroyed. Use this to free the userdata argument if required */
+    void (*defer_set_destroy)(pa_defer_event *e, pa_defer_event_destroy_cb_t cb);
+
+    /** Exit the main loop and return the specfied retval*/
+    void (*quit)(pa_mainloop_api*a, int retval);
+};
+
+/** Run the specified callback function once from the main loop using an anonymous defer event. */
+void pa_mainloop_api_once(pa_mainloop_api*m, void (*callback)(pa_mainloop_api*m, void *userdata), void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c
new file mode 100644 (file)
index 0000000..9161dec
--- /dev/null
@@ -0,0 +1,227 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "mainloop-signal.h"
+
+struct pa_signal_event {
+    int sig;
+#ifdef HAVE_SIGACTION
+    struct sigaction saved_sigaction;
+#else
+    void (*saved_handler)(int sig);
+#endif
+    void *userdata;
+    pa_signal_cb_t callback;
+    pa_signal_destroy_cb_t destroy_callback;
+    pa_signal_event *previous, *next;
+};
+
+static pa_mainloop_api *api = NULL;
+static int signal_pipe[2] = { -1, -1 };
+static pa_io_event* io_event = NULL;
+static pa_signal_event *signals = NULL;
+
+static void signal_handler(int sig) {
+    int saved_errno;
+
+    saved_errno = errno;
+
+#ifndef HAVE_SIGACTION
+    signal(sig, signal_handler);
+#endif
+
+    pa_write(signal_pipe[1], &sig, sizeof(sig), NULL);
+
+    errno = saved_errno;
+}
+
+static void dispatch(pa_mainloop_api*a, int sig) {
+    pa_signal_event *s;
+
+    for (s = signals; s; s = s->next)
+        if (s->sig == sig) {
+            pa_assert(s->callback);
+            s->callback(a, s, sig, s->userdata);
+            break;
+        }
+}
+
+static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) {
+    ssize_t r;
+    int sig;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(f == PA_IO_EVENT_INPUT);
+    pa_assert(e == io_event);
+    pa_assert(fd == signal_pipe[0]);
+
+    if ((r = pa_read(signal_pipe[0], &sig, sizeof(sig), NULL)) < 0) {
+        if (errno == EAGAIN)
+            return;
+
+        pa_log("read(): %s", pa_cstrerror(errno));
+        return;
+    }
+
+    if (r != sizeof(sig)) {
+        pa_log("short read()");
+        return;
+    }
+
+    dispatch(a, sig);
+}
+
+int pa_signal_init(pa_mainloop_api *a) {
+
+    pa_assert(a);
+    pa_assert(!api);
+    pa_assert(signal_pipe[0] == -1);
+    pa_assert(signal_pipe[1] == -1);
+    pa_assert(!io_event);
+
+    if (pipe(signal_pipe) < 0) {
+        pa_log("pipe(): %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_make_fd_nonblock(signal_pipe[0]);
+    pa_make_fd_nonblock(signal_pipe[1]);
+    pa_make_fd_cloexec(signal_pipe[0]);
+    pa_make_fd_cloexec(signal_pipe[1]);
+
+    api = a;
+
+    pa_assert_se(io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL));
+
+    return 0;
+}
+
+void pa_signal_done(void) {
+    while (signals)
+        pa_signal_free(signals);
+
+    if (io_event) {
+        pa_assert(api);
+        api->io_free(io_event);
+        io_event = NULL;
+    }
+
+    pa_close_pipe(signal_pipe);
+
+    api = NULL;
+}
+
+pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t _callback, void *userdata) {
+    pa_signal_event *e = NULL;
+
+#ifdef HAVE_SIGACTION
+    struct sigaction sa;
+#endif
+
+    pa_assert(sig > 0);
+    pa_assert(_callback);
+
+    for (e = signals; e; e = e->next)
+        if (e->sig == sig)
+            goto fail;
+
+    e = pa_xnew(pa_signal_event, 1);
+    e->sig = sig;
+    e->callback = _callback;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+#ifdef HAVE_SIGACTION
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = signal_handler;
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = SA_RESTART;
+
+    if (sigaction(sig, &sa, &e->saved_sigaction) < 0)
+#else
+    if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR)
+#endif
+        goto fail;
+
+    e->previous = NULL;
+    e->next = signals;
+    signals = e;
+
+    return e;
+fail:
+    if (e)
+        pa_xfree(e);
+    return NULL;
+}
+
+void pa_signal_free(pa_signal_event *e) {
+    pa_assert(e);
+
+    if (e->next)
+        e->next->previous = e->previous;
+    if (e->previous)
+        e->previous->next = e->next;
+    else
+        signals = e->next;
+
+#ifdef HAVE_SIGACTION
+    pa_assert_se(sigaction(e->sig, &e->saved_sigaction, NULL) == 0);
+#else
+    pa_assert_se(signal(e->sig, e->saved_handler) == signal_handler);
+#endif
+
+    if (e->destroy_callback)
+        e->destroy_callback(api, e, e->userdata);
+
+    pa_xfree(e);
+}
+
+void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t _callback) {
+    pa_assert(e);
+
+    e->destroy_callback = _callback;
+}
similarity index 61%
rename from polyp/mainloop-signal.h
rename to src/pulse/mainloop-signal.h
index 9ba9214129d41b285e993e2c3e63261af806437b..a6c16f2f63df395bd8816c43acb3531e52bd8fd8 100644 (file)
@@ -1,29 +1,30 @@
 #ifndef foomainloopsignalhfoo
 #define foomainloopsignalhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "mainloop-api.h"
-#include "cdecl.h"
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
 
 PA_C_DECL_BEGIN
 
@@ -36,24 +37,27 @@ PA_C_DECL_BEGIN
  * signals. However, you may hook signal support into an abstract main loop via the routines defined herein.
  */
 
+/** An opaque UNIX signal event source object */
+typedef struct pa_signal_event pa_signal_event;
+
+typedef void (*pa_signal_cb_t) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata);
+
+typedef void (*pa_signal_destroy_cb_t) (pa_mainloop_api *api, pa_signal_event*e, void *userdata);
+
 /** Initialize the UNIX signal subsystem and bind it to the specified main loop */
-int pa_signal_init(struct pa_mainloop_api *api);
+int pa_signal_init(pa_mainloop_api *api);
 
 /** Cleanup the signal subsystem */
 void pa_signal_done(void);
 
-/** \struct pa_signal_event
- * An opaque UNIX signal event source object */
-struct pa_signal_event;
-
 /** Create a new UNIX signal event source object */
-struct pa_signal_event* pa_signal_new(int signal, void (*callback) (struct pa_mainloop_api *api, struct pa_signal_event*e, int signal, void *userdata), void *userdata);
+pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata);
 
 /** Free a UNIX signal event source object */
-void pa_signal_free(struct pa_signal_event *e);
+void pa_signal_free(pa_signal_event *e);
 
 /** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */
-void pa_signal_set_destroy(struct pa_signal_event *e, void (*callback) (struct pa_mainloop_api *api, struct pa_signal_event*e, void *userdata));
+void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t callback);
 
 PA_C_DECL_END
 
diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
new file mode 100644 (file)
index 0000000..aaed3ca
--- /dev/null
@@ -0,0 +1,963 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else
+#include <pulsecore/poll.h>
+#endif
+
+#ifndef HAVE_PIPE
+#include <pulsecore/pipe.h>
+#endif
+
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/winsock.h>
+#include <pulsecore/macro.h>
+
+#include "mainloop.h"
+
+struct pa_io_event {
+    pa_mainloop *mainloop;
+    int dead;
+
+    int fd;
+    pa_io_event_flags_t events;
+    struct pollfd *pollfd;
+
+    pa_io_event_cb_t callback;
+    void *userdata;
+    pa_io_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_io_event);
+};
+
+struct pa_time_event {
+    pa_mainloop *mainloop;
+    int dead;
+
+    int enabled;
+    struct timeval timeval;
+
+    pa_time_event_cb_t callback;
+    void *userdata;
+    pa_time_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_time_event);
+};
+
+struct pa_defer_event {
+    pa_mainloop *mainloop;
+    int dead;
+
+    int enabled;
+
+    pa_defer_event_cb_t callback;
+    void *userdata;
+    pa_defer_event_destroy_cb_t destroy_callback;
+
+    PA_LLIST_FIELDS(pa_defer_event);
+};
+
+struct pa_mainloop {
+    PA_LLIST_HEAD(pa_io_event, io_events);
+    PA_LLIST_HEAD(pa_time_event, time_events);
+    PA_LLIST_HEAD(pa_defer_event, defer_events);
+
+    int n_enabled_defer_events, n_enabled_time_events, n_io_events;
+    int io_events_please_scan, time_events_please_scan, defer_events_please_scan;
+
+    struct pollfd *pollfds;
+    unsigned max_pollfds, n_pollfds;
+    int rebuild_pollfds;
+
+    int prepared_timeout;
+    pa_time_event *cached_next_time_event;
+
+    int quit, retval;
+    pa_mainloop_api api;
+
+    int wakeup_pipe[2];
+    int wakeup_pipe_type;
+    int wakeup_requested;
+
+    enum {
+        STATE_PASSIVE,
+        STATE_PREPARED,
+        STATE_POLLING,
+        STATE_POLLED,
+        STATE_QUIT
+    } state;
+
+    pa_poll_func poll_func;
+    void *poll_func_userdata;
+    int poll_func_ret;
+};
+
+static short map_flags_to_libc(pa_io_event_flags_t flags) {
+    return
+        (flags & PA_IO_EVENT_INPUT ? POLLIN : 0) |
+        (flags & PA_IO_EVENT_OUTPUT ? POLLOUT : 0) |
+        (flags & PA_IO_EVENT_ERROR ? POLLERR : 0) |
+        (flags & PA_IO_EVENT_HANGUP ? POLLHUP : 0);
+}
+
+static pa_io_event_flags_t map_flags_from_libc(short flags) {
+    return
+        (flags & POLLIN ? PA_IO_EVENT_INPUT : 0) |
+        (flags & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) |
+        (flags & POLLERR ? PA_IO_EVENT_ERROR : 0) |
+        (flags & POLLHUP ? PA_IO_EVENT_HANGUP : 0);
+}
+
+/* IO events */
+static pa_io_event* mainloop_io_new(
+        pa_mainloop_api*a,
+        int fd,
+        pa_io_event_flags_t events,
+        pa_io_event_cb_t callback,
+        void *userdata) {
+
+    pa_mainloop *m;
+    pa_io_event *e;
+
+    pa_assert(a);
+    pa_assert(a->userdata);
+    pa_assert(fd >= 0);
+    pa_assert(callback);
+
+    m = a->userdata;
+    pa_assert(a == &m->api);
+
+    e = pa_xnew(pa_io_event, 1);
+    e->mainloop = m;
+    e->dead = 0;
+
+    e->fd = fd;
+    e->events = events;
+    e->pollfd = NULL;
+
+    e->callback = callback;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+#ifdef OS_IS_WIN32
+    {
+        fd_set xset;
+        struct timeval tv;
+
+        tv.tv_sec = 0;
+        tv.tv_usec = 0;
+
+        FD_ZERO (&xset);
+        FD_SET (fd, &xset);
+
+        if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset,
+                    SELECT_TYPE_ARG5 &tv) == -1) &&
+             (WSAGetLastError() == WSAENOTSOCK)) {
+            pa_log_warn("Cannot monitor non-socket file descriptors.");
+            e->dead = 1;
+        }
+    }
+#endif
+
+    PA_LLIST_PREPEND(pa_io_event, m->io_events, e);
+    m->rebuild_pollfds = 1;
+    m->n_io_events ++;
+
+    pa_mainloop_wakeup(m);
+
+    return e;
+}
+
+static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    if (e->events == events)
+        return;
+
+    e->events = events;
+
+    if (e->pollfd)
+        e->pollfd->events = map_flags_to_libc(events);
+    else
+        e->mainloop->rebuild_pollfds = 1;
+
+    pa_mainloop_wakeup(e->mainloop);
+}
+
+static void mainloop_io_free(pa_io_event *e) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    e->dead = 1;
+    e->mainloop->io_events_please_scan ++;
+
+    e->mainloop->n_io_events --;
+    e->mainloop->rebuild_pollfds = 1;
+
+    pa_mainloop_wakeup(e->mainloop);
+}
+
+static void mainloop_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t callback) {
+    pa_assert(e);
+
+    e->destroy_callback = callback;
+}
+
+/* Defer events */
+static pa_defer_event* mainloop_defer_new(
+        pa_mainloop_api*a,
+        pa_defer_event_cb_t callback,
+        void *userdata) {
+
+    pa_mainloop *m;
+    pa_defer_event *e;
+
+    pa_assert(a);
+    pa_assert(a->userdata);
+    pa_assert(callback);
+
+    m = a->userdata;
+    pa_assert(a == &m->api);
+
+    e = pa_xnew(pa_defer_event, 1);
+    e->mainloop = m;
+    e->dead = 0;
+
+    e->enabled = 1;
+    m->n_enabled_defer_events++;
+
+    e->callback = callback;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+    PA_LLIST_PREPEND(pa_defer_event, m->defer_events, e);
+
+    pa_mainloop_wakeup(e->mainloop);
+
+    return e;
+}
+
+static void mainloop_defer_enable(pa_defer_event *e, int b) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    if (e->enabled && !b) {
+        pa_assert(e->mainloop->n_enabled_defer_events > 0);
+        e->mainloop->n_enabled_defer_events--;
+    } else if (!e->enabled && b) {
+        e->mainloop->n_enabled_defer_events++;
+        pa_mainloop_wakeup(e->mainloop);
+    }
+
+    e->enabled = b;
+}
+
+static void mainloop_defer_free(pa_defer_event *e) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    e->dead = 1;
+    e->mainloop->defer_events_please_scan ++;
+
+    if (e->enabled) {
+        pa_assert(e->mainloop->n_enabled_defer_events > 0);
+        e->mainloop->n_enabled_defer_events--;
+        e->enabled = 0;
+    }
+}
+
+static void mainloop_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t callback) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    e->destroy_callback = callback;
+}
+
+/* Time events */
+static pa_time_event* mainloop_time_new(
+        pa_mainloop_api*a,
+        const struct timeval *tv,
+        pa_time_event_cb_t callback,
+        void *userdata) {
+
+    pa_mainloop *m;
+    pa_time_event *e;
+
+    pa_assert(a);
+    pa_assert(a->userdata);
+    pa_assert(callback);
+
+    m = a->userdata;
+    pa_assert(a == &m->api);
+
+    e = pa_xnew(pa_time_event, 1);
+    e->mainloop = m;
+    e->dead = 0;
+
+    if ((e->enabled = !!tv)) {
+        e->timeval = *tv;
+
+        m->n_enabled_time_events++;
+
+        if (m->cached_next_time_event) {
+            pa_assert(m->cached_next_time_event->enabled);
+
+            if (pa_timeval_cmp(tv, &m->cached_next_time_event->timeval) < 0)
+                m->cached_next_time_event = e;
+        }
+    }
+
+    e->callback = callback;
+    e->userdata = userdata;
+    e->destroy_callback = NULL;
+
+    PA_LLIST_PREPEND(pa_time_event, m->time_events, e);
+
+    if (e->enabled)
+        pa_mainloop_wakeup(m);
+
+    return e;
+}
+
+static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    if (e->enabled && !tv) {
+        pa_assert(e->mainloop->n_enabled_time_events > 0);
+        e->mainloop->n_enabled_time_events--;
+    } else if (!e->enabled && tv)
+        e->mainloop->n_enabled_time_events++;
+
+    if ((e->enabled = !!tv)) {
+        e->timeval = *tv;
+        pa_mainloop_wakeup(e->mainloop);
+    }
+
+    if (e->mainloop->cached_next_time_event && e->enabled) {
+        pa_assert(e->mainloop->cached_next_time_event->enabled);
+
+        if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0)
+            e->mainloop->cached_next_time_event = e;
+    } else if (e->mainloop->cached_next_time_event == e)
+        e->mainloop->cached_next_time_event = NULL;
+}
+
+static void mainloop_time_free(pa_time_event *e) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    e->dead = 1;
+    e->mainloop->time_events_please_scan ++;
+
+    if (e->enabled) {
+        pa_assert(e->mainloop->n_enabled_time_events > 0);
+        e->mainloop->n_enabled_time_events--;
+        e->enabled = 0;
+    }
+
+    if (e->mainloop->cached_next_time_event == e)
+        e->mainloop->cached_next_time_event = NULL;
+
+    /* no wakeup needed here. Think about it! */
+}
+
+static void mainloop_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t callback) {
+    pa_assert(e);
+    pa_assert(!e->dead);
+
+    e->destroy_callback = callback;
+}
+
+/* quit() */
+
+static void mainloop_quit(pa_mainloop_api*a, int retval) {
+    pa_mainloop *m;
+
+    pa_assert(a);
+    pa_assert(a->userdata);
+    m = a->userdata;
+    pa_assert(a == &m->api);
+
+    pa_mainloop_quit(m, retval);
+}
+
+static const pa_mainloop_api vtable = {
+    .userdata = NULL,
+
+    .io_new= mainloop_io_new,
+    .io_enable= mainloop_io_enable,
+    .io_free= mainloop_io_free,
+    .io_set_destroy= mainloop_io_set_destroy,
+
+    .time_new = mainloop_time_new,
+    .time_restart = mainloop_time_restart,
+    .time_free = mainloop_time_free,
+    .time_set_destroy = mainloop_time_set_destroy,
+
+    .defer_new = mainloop_defer_new,
+    .defer_enable = mainloop_defer_enable,
+    .defer_free = mainloop_defer_free,
+    .defer_set_destroy = mainloop_defer_set_destroy,
+
+    .quit = mainloop_quit,
+};
+
+pa_mainloop *pa_mainloop_new(void) {
+    pa_mainloop *m;
+
+    m = pa_xnew(pa_mainloop, 1);
+
+    m->wakeup_pipe_type = 0;
+    if (pipe(m->wakeup_pipe) < 0) {
+        pa_log_error("ERROR: cannot create wakeup pipe");
+        pa_xfree(m);
+        return NULL;
+    }
+
+    pa_make_fd_nonblock(m->wakeup_pipe[0]);
+    pa_make_fd_nonblock(m->wakeup_pipe[1]);
+    pa_make_fd_cloexec(m->wakeup_pipe[0]);
+    pa_make_fd_cloexec(m->wakeup_pipe[1]);
+    m->wakeup_requested = 0;
+
+    PA_LLIST_HEAD_INIT(pa_io_event, m->io_events);
+    PA_LLIST_HEAD_INIT(pa_time_event, m->time_events);
+    PA_LLIST_HEAD_INIT(pa_defer_event, m->defer_events);
+
+    m->n_enabled_defer_events = m->n_enabled_time_events = m->n_io_events = 0;
+    m->io_events_please_scan = m->time_events_please_scan = m->defer_events_please_scan = 0;
+
+    m->cached_next_time_event = NULL;
+    m->prepared_timeout = 0;
+
+    m->pollfds = NULL;
+    m->max_pollfds = m->n_pollfds = 0;
+    m->rebuild_pollfds = 1;
+
+    m->quit = m->retval = 0;
+
+    m->api = vtable;
+    m->api.userdata = m;
+
+    m->state = STATE_PASSIVE;
+
+    m->poll_func = NULL;
+    m->poll_func_userdata = NULL;
+    m->poll_func_ret = -1;
+
+    return m;
+}
+
+static void cleanup_io_events(pa_mainloop *m, int force) {
+    pa_io_event *e;
+
+    e = m->io_events;
+    while (e) {
+        pa_io_event *n = e->next;
+
+        if (!force && m->io_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_io_event, m->io_events, e);
+
+            if (e->dead) {
+                pa_assert(m->io_events_please_scan > 0);
+                m->io_events_please_scan--;
+            }
+
+            if (e->destroy_callback)
+                e->destroy_callback(&m->api, e, e->userdata);
+
+            pa_xfree(e);
+
+            m->rebuild_pollfds = 1;
+        }
+
+        e = n;
+    }
+
+    pa_assert(m->io_events_please_scan == 0);
+}
+
+static void cleanup_time_events(pa_mainloop *m, int force) {
+    pa_time_event *e;
+
+    e = m->time_events;
+    while (e) {
+        pa_time_event *n = e->next;
+
+        if (!force && m->time_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_time_event, m->time_events, e);
+
+            if (e->dead) {
+                pa_assert(m->time_events_please_scan > 0);
+                m->time_events_please_scan--;
+            }
+
+            if (!e->dead && e->enabled) {
+                pa_assert(m->n_enabled_time_events > 0);
+                m->n_enabled_time_events--;
+                e->enabled = 0;
+            }
+
+            if (e->destroy_callback)
+                e->destroy_callback(&m->api, e, e->userdata);
+
+            pa_xfree(e);
+        }
+
+        e = n;
+    }
+
+    pa_assert(m->time_events_please_scan == 0);
+}
+
+static void cleanup_defer_events(pa_mainloop *m, int force) {
+    pa_defer_event *e;
+
+    e = m->defer_events;
+    while (e) {
+        pa_defer_event *n = e->next;
+
+        if (!force && m->defer_events_please_scan <= 0)
+            break;
+
+        if (force || e->dead) {
+            PA_LLIST_REMOVE(pa_defer_event, m->defer_events, e);
+
+            if (e->dead) {
+                pa_assert(m->defer_events_please_scan > 0);
+                m->defer_events_please_scan--;
+            }
+
+            if (!e->dead && e->enabled) {
+                pa_assert(m->n_enabled_defer_events > 0);
+                m->n_enabled_defer_events--;
+                e->enabled = 0;
+            }
+
+            if (e->destroy_callback)
+                e->destroy_callback(&m->api, e, e->userdata);
+
+            pa_xfree(e);
+        }
+
+        e = n;
+    }
+
+    pa_assert(m->defer_events_please_scan == 0);
+}
+
+
+void pa_mainloop_free(pa_mainloop* m) {
+    pa_assert(m);
+
+    cleanup_io_events(m, 1);
+    cleanup_defer_events(m, 1);
+    cleanup_time_events(m, 1);
+
+    pa_xfree(m->pollfds);
+
+    pa_close_pipe(m->wakeup_pipe);
+
+    pa_xfree(m);
+}
+
+static void scan_dead(pa_mainloop *m) {
+    pa_assert(m);
+
+    if (m->io_events_please_scan)
+        cleanup_io_events(m, 0);
+
+    if (m->time_events_please_scan)
+        cleanup_time_events(m, 0);
+
+    if (m->defer_events_please_scan)
+        cleanup_defer_events(m, 0);
+}
+
+static void rebuild_pollfds(pa_mainloop *m) {
+    pa_io_event*e;
+    struct pollfd *p;
+    unsigned l;
+
+    l = m->n_io_events + 1;
+    if (m->max_pollfds < l) {
+        l *= 2;
+        m->pollfds = pa_xrealloc(m->pollfds, sizeof(struct pollfd)*l);
+        m->max_pollfds = l;
+    }
+
+    m->n_pollfds = 0;
+    p = m->pollfds;
+
+    if (m->wakeup_pipe[0] >= 0) {
+        m->pollfds[0].fd = m->wakeup_pipe[0];
+        m->pollfds[0].events = POLLIN;
+        m->pollfds[0].revents = 0;
+        p++;
+        m->n_pollfds++;
+    }
+
+    for (e = m->io_events; e; e = e->next) {
+        if (e->dead) {
+            e->pollfd = NULL;
+            continue;
+        }
+
+        e->pollfd = p;
+        p->fd = e->fd;
+        p->events = map_flags_to_libc(e->events);
+        p->revents = 0;
+
+        p++;
+        m->n_pollfds++;
+    }
+
+    m->rebuild_pollfds = 0;
+}
+
+static int dispatch_pollfds(pa_mainloop *m) {
+    pa_io_event *e;
+    int r = 0, k;
+
+    pa_assert(m->poll_func_ret > 0);
+
+    for (e = m->io_events, k = m->poll_func_ret; e && !m->quit && k > 0; e = e->next) {
+        if (e->dead || !e->pollfd || !e->pollfd->revents)
+            continue;
+
+        pa_assert(e->pollfd->fd == e->fd);
+        pa_assert(e->callback);
+        e->callback(&m->api, e, e->fd, map_flags_from_libc(e->pollfd->revents), e->userdata);
+        e->pollfd->revents = 0;
+        r++;
+
+        k--;
+    }
+
+    return r;
+}
+
+static int dispatch_defer(pa_mainloop *m) {
+    pa_defer_event *e;
+    int r = 0;
+
+    if (m->n_enabled_defer_events <= 0)
+        return 0;
+
+    for (e = m->defer_events; e && !m->quit; e = e->next) {
+        if (e->dead || !e->enabled)
+            continue;
+
+        pa_assert(e->callback);
+        e->callback(&m->api, e, e->userdata);
+        r++;
+    }
+
+    return r;
+}
+
+static pa_time_event* find_next_time_event(pa_mainloop *m) {
+    pa_time_event *t, *n = NULL;
+    pa_assert(m);
+
+    if (m->cached_next_time_event)
+        return m->cached_next_time_event;
+
+    for (t = m->time_events; t; t = t->next) {
+
+        if (t->dead || !t->enabled)
+            continue;
+
+        if (!n || pa_timeval_cmp(&t->timeval, &n->timeval) < 0) {
+            n = t;
+
+            /* Shortcut for tv = { 0, 0 } */
+            if (n->timeval.tv_sec <= 0)
+                break;
+        }
+    }
+
+    m->cached_next_time_event = n;
+    return n;
+}
+
+static int calc_next_timeout(pa_mainloop *m) {
+    pa_time_event *t;
+    struct timeval now;
+    pa_usec_t usec;
+
+    if (!m->n_enabled_time_events)
+        return -1;
+
+    t = find_next_time_event(m);
+    pa_assert(t);
+
+    if (t->timeval.tv_sec <= 0)
+        return 0;
+
+    pa_gettimeofday(&now);
+
+    if (pa_timeval_cmp(&t->timeval, &now) <= 0)
+        return 0;
+
+    usec = pa_timeval_diff(&t->timeval, &now);
+    return (int) (usec / 1000);
+}
+
+static int dispatch_timeout(pa_mainloop *m) {
+    pa_time_event *e;
+    struct timeval now;
+    int r = 0;
+    pa_assert(m);
+
+    if (m->n_enabled_time_events <= 0)
+        return 0;
+
+    pa_gettimeofday(&now);
+
+    for (e = m->time_events; e && !m->quit; e = e->next) {
+
+        if (e->dead || !e->enabled)
+            continue;
+
+        if (pa_timeval_cmp(&e->timeval, &now) <= 0) {
+            pa_assert(e->callback);
+
+            /* Disable time event */
+            mainloop_time_restart(e, NULL);
+
+            e->callback(&m->api, e, &e->timeval, e->userdata);
+
+            r++;
+        }
+    }
+
+    return r;
+}
+
+void pa_mainloop_wakeup(pa_mainloop *m) {
+    char c = 'W';
+    pa_assert(m);
+
+    if (m->wakeup_pipe[1] >= 0 && m->state == STATE_POLLING) {
+        pa_write(m->wakeup_pipe[1], &c, sizeof(c), &m->wakeup_pipe_type);
+        m->wakeup_requested++;
+    }
+}
+
+static void clear_wakeup(pa_mainloop *m) {
+    char c[10];
+
+    pa_assert(m);
+
+    if (m->wakeup_pipe[0] < 0)
+        return;
+
+    if (m->wakeup_requested) {
+        while (pa_read(m->wakeup_pipe[0], &c, sizeof(c), &m->wakeup_pipe_type) == sizeof(c));
+        m->wakeup_requested = 0;
+    }
+}
+
+int pa_mainloop_prepare(pa_mainloop *m, int timeout) {
+    pa_assert(m);
+    pa_assert(m->state == STATE_PASSIVE);
+
+    clear_wakeup(m);
+    scan_dead(m);
+
+    if (m->quit)
+        goto quit;
+
+    if (m->n_enabled_defer_events <= 0) {
+        if (m->rebuild_pollfds)
+            rebuild_pollfds(m);
+
+        m->prepared_timeout = calc_next_timeout(m);
+        if (timeout >= 0 && (timeout < m->prepared_timeout || m->prepared_timeout < 0))
+            m->prepared_timeout = timeout;
+    }
+
+    m->state = STATE_PREPARED;
+    return 0;
+
+quit:
+    m->state = STATE_QUIT;
+    return -2;
+}
+
+int pa_mainloop_poll(pa_mainloop *m) {
+    pa_assert(m);
+    pa_assert(m->state == STATE_PREPARED);
+
+    if (m->quit)
+        goto quit;
+
+    m->state = STATE_POLLING;
+
+    if (m->n_enabled_defer_events )
+        m->poll_func_ret = 0;
+    else {
+        pa_assert(!m->rebuild_pollfds);
+
+        if (m->poll_func)
+            m->poll_func_ret = m->poll_func(m->pollfds, m->n_pollfds, m->prepared_timeout, m->poll_func_userdata);
+        else
+            m->poll_func_ret = poll(m->pollfds, m->n_pollfds, m->prepared_timeout);
+
+        if (m->poll_func_ret < 0) {
+            if (errno == EINTR)
+                m->poll_func_ret = 0;
+            else
+                pa_log("poll(): %s", pa_cstrerror(errno));
+        }
+    }
+
+    m->state = m->poll_func_ret < 0 ? STATE_PASSIVE : STATE_POLLED;
+    return m->poll_func_ret;
+
+quit:
+    m->state = STATE_QUIT;
+    return -2;
+}
+
+int pa_mainloop_dispatch(pa_mainloop *m) {
+    int dispatched = 0;
+
+    pa_assert(m);
+    pa_assert(m->state == STATE_POLLED);
+
+    if (m->quit)
+        goto quit;
+
+    if (m->n_enabled_defer_events)
+        dispatched += dispatch_defer(m);
+    else {
+        if (m->n_enabled_time_events)
+            dispatched += dispatch_timeout(m);
+
+        if (m->quit)
+            goto quit;
+
+        if (m->poll_func_ret > 0)
+            dispatched += dispatch_pollfds(m);
+    }
+
+    if (m->quit)
+        goto quit;
+
+    m->state = STATE_PASSIVE;
+
+    return dispatched;
+
+quit:
+    m->state = STATE_QUIT;
+    return -2;
+}
+
+int pa_mainloop_get_retval(pa_mainloop *m) {
+    pa_assert(m);
+    return m->retval;
+}
+
+int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
+    int r;
+    pa_assert(m);
+
+    if ((r = pa_mainloop_prepare(m, block ? -1 : 0)) < 0)
+        goto quit;
+
+    if ((r = pa_mainloop_poll(m)) < 0)
+        goto quit;
+
+    if ((r = pa_mainloop_dispatch(m)) < 0)
+        goto quit;
+
+    return r;
+
+quit:
+
+    if ((r == -2) && retval)
+        *retval = pa_mainloop_get_retval(m);
+    return r;
+}
+
+int pa_mainloop_run(pa_mainloop *m, int *retval) {
+    int r;
+
+    while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0);
+
+    if (r == -2)
+        return 1;
+    else if (r < 0)
+        return -1;
+    else
+        return 0;
+}
+
+void pa_mainloop_quit(pa_mainloop *m, int retval) {
+    pa_assert(m);
+
+    m->quit = 1;
+    m->retval = retval;
+    pa_mainloop_wakeup(m);
+}
+
+pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) {
+    pa_assert(m);
+    return &m->api;
+}
+
+void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) {
+    pa_assert(m);
+
+    m->poll_func = poll_func;
+    m->poll_func_userdata = userdata;
+}
diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h
new file mode 100644 (file)
index 0000000..907e94a
--- /dev/null
@@ -0,0 +1,128 @@
+#ifndef foomainloophfoo
+#define foomainloophfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+struct pollfd;
+
+/** \page mainloop Main Loop
+ *
+ * \section overv_sec Overview
+ *
+ * The built-in main loop implementation is based on the poll() system call.
+ * It supports the functions defined in the main loop abstraction and very
+ * little else.
+ *
+ * The main loop is created using pa_mainloop_new() and destroyed using
+ * pa_mainloop_free(). To get access to the main loop abstraction,
+ * pa_mainloop_get_api() is used.
+ *
+ * \section iter_sec Iteration
+ *
+ * The main loop is designed around the concept of iterations. Each iteration
+ * consists of three steps that repeat during the application's entire
+ * lifetime:
+ *
+ * -# Prepare - Build a list of file descriptors
+ *               that need to be monitored and calculate the next timeout.
+ * -# Poll - Execute the actuall poll() system call.
+ * -# Dispatch - Dispatch any events that have fired.
+ *
+ * When using the main loop, the application can either execute each
+ * iteration, one at a time, using pa_mainloop_iterate(), or let the library
+ * iterate automatically using pa_mainloop_run().
+ *
+ * \section thread_sec Threads
+ *
+ * The main loop functions are designed to be thread safe, but the objects
+ * are not. What this means is that multiple main loops can be used, but only
+ * one object per thread.
+ *
+ */
+
+/** \file
+ *
+ * A minimal main loop implementation based on the C library's poll()
+ * function. Using the routines defined herein you may create a simple
+ * main loop supporting the generic main loop abstraction layer as
+ * defined in \ref mainloop-api.h. This implementation is thread safe
+ * as long as you access the main loop object from a single thread only.*/
+
+/** An opaque main loop object */
+typedef struct pa_mainloop pa_mainloop;
+
+/** Allocate a new main loop object */
+pa_mainloop *pa_mainloop_new(void);
+
+/** Free a main loop object */
+void pa_mainloop_free(pa_mainloop* m);
+
+/** Prepare for a single iteration of the main loop. Returns a negative value
+on error or exit request. timeout specifies a maximum timeout for the subsequent
+poll, or -1 for blocking behaviour. .*/
+int pa_mainloop_prepare(pa_mainloop *m, int timeout);
+
+/** Execute the previously prepared poll. Returns a negative value on error.*/
+int pa_mainloop_poll(pa_mainloop *m);
+
+/** Dispatch timeout, io and deferred events from the previously executed poll. Returns
+a negative value on error. On success returns the number of source dispatched. */
+int pa_mainloop_dispatch(pa_mainloop *m);
+
+/** Return the return value as specified with the main loop's quit() routine. */
+int pa_mainloop_get_retval(pa_mainloop *m);
+
+/** Run a single iteration of the main loop. This is a convenience function
+for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
+Returns a negative value on error or exit request. If block is nonzero,
+block for events if none are queued. Optionally return the return value as
+specified with the main loop's quit() routine in the integer variable retval points
+to. On success returns the number of sources dispatched in this iteration. */
+int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval);
+
+/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
+int pa_mainloop_run(pa_mainloop *m, int *retval);
+
+/** Return the abstract main loop abstraction layer vtable for this main loop. */
+pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m);
+
+/** Shutdown the main loop */
+void pa_mainloop_quit(pa_mainloop *m, int r);
+
+/** Interrupt a running poll (for threaded systems) */
+void pa_mainloop_wakeup(pa_mainloop *m);
+
+/** Generic prototype of a poll() like function */
+typedef int (*pa_poll_func)(struct pollfd *ufds, unsigned long nfds, int timeout, void*userdata);
+
+/** Change the poll() implementation */
+void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/operation.c b/src/pulse/operation.c
new file mode 100644 (file)
index 0000000..13b470a
--- /dev/null
@@ -0,0 +1,131 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+
+#include "internal.h"
+#include "operation.h"
+
+PA_STATIC_FLIST_DECLARE(operations, 0, pa_xfree);
+
+pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_assert(c);
+
+    if (!(o = pa_flist_pop(PA_STATIC_FLIST_GET(operations))))
+        o = pa_xnew(pa_operation, 1);
+
+    PA_REFCNT_INIT(o);
+    o->context = c;
+    o->stream = s;
+    o->private = NULL;
+
+    o->state = PA_OPERATION_RUNNING;
+    o->callback = cb;
+    o->userdata = userdata;
+
+    /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
+    PA_LLIST_PREPEND(pa_operation, c->operations, o);
+    pa_operation_ref(o);
+
+    return o;
+}
+
+pa_operation *pa_operation_ref(pa_operation *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    PA_REFCNT_INC(o);
+    return o;
+}
+void pa_operation_unref(pa_operation *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (PA_REFCNT_DEC(o) <= 0) {
+        pa_assert(!o->context);
+        pa_assert(!o->stream);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(operations), o) < 0)
+            pa_xfree(o);
+    }
+}
+
+static void operation_unlink(pa_operation *o) {
+    pa_assert(o);
+
+    if (o->context) {
+        pa_assert(PA_REFCNT_VALUE(o) >= 2);
+
+        PA_LLIST_REMOVE(pa_operation, o->context->operations, o);
+        pa_operation_unref(o);
+
+        o->context = NULL;
+    }
+
+    o->stream = NULL;
+    o->callback = NULL;
+    o->userdata = NULL;
+}
+
+static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (st == o->state)
+        return;
+
+    pa_operation_ref(o);
+
+    o->state = st;
+
+    if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED))
+        operation_unlink(o);
+
+    pa_operation_unref(o);
+}
+
+void pa_operation_cancel(pa_operation *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    operation_set_state(o, PA_OPERATION_CANCELED);
+}
+
+void pa_operation_done(pa_operation *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    operation_set_state(o, PA_OPERATION_DONE);
+}
+
+pa_operation_state_t pa_operation_get_state(pa_operation *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    return o->state;
+}
similarity index 56%
rename from polyp/polyplib-operation.h
rename to src/pulse/operation.h
index 5c3af49ca6fc548d27d87aa58b31e6d25231bb83..188e2cb96aa5e4aec1d949ba12d253843cc34230 100644 (file)
@@ -1,50 +1,49 @@
-#ifndef foopolypliboperationhfoo
-#define foopolypliboperationhfoo
-
-/* $Id$ */
+#ifndef foooperationhfoo
+#define foooperationhfoo
 
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include <polyp/cdecl.h>
-#include <polyp/polyplib-def.h>
+#include <pulse/cdecl.h>
+#include <pulse/def.h>
 
 /** \file
  * Asynchronous operations */
 
 PA_C_DECL_BEGIN
 
-/** \struct pa_operation
- * An asynchronous operation object */
-struct pa_operation;
+/** An asynchronous operation object */
+typedef struct pa_operation pa_operation;
 
 /** Increase the reference count by one */
-struct pa_operation *pa_operation_ref(struct pa_operation *o);
+pa_operation *pa_operation_ref(pa_operation *o);
 
 /** Decrease the reference count by one */
-void pa_operation_unref(struct pa_operation *o);
+void pa_operation_unref(pa_operation *o);
 
 /** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */
-void pa_operation_cancel(struct pa_operation *o);
+void pa_operation_cancel(pa_operation *o);
 
 /** Return the current status of the operation */
-enum pa_operation_state pa_operation_get_state(struct pa_operation *o);
+pa_operation_state_t pa_operation_get_state(pa_operation *o);
 
 PA_C_DECL_END
 
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
new file mode 100644 (file)
index 0000000..74aea20
--- /dev/null
@@ -0,0 +1,321 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/hashmap.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core-util.h>
+
+#include "proplist.h"
+
+struct property {
+    char *key;
+    void *value;
+    size_t nbytes;
+};
+
+#define MAKE_HASHMAP(p) ((pa_hashmap*) (p))
+#define MAKE_PROPLIST(p) ((pa_proplist*) (p))
+
+static pa_bool_t property_name_valid(const char *key) {
+
+    if (!pa_utf8_valid(key))
+        return FALSE;
+
+    if (strlen(key) <= 0)
+        return FALSE;
+
+    return TRUE;
+}
+
+static void property_free(struct property *prop) {
+    pa_assert(prop);
+
+    pa_xfree(prop->key);
+    pa_xfree(prop->value);
+    pa_xfree(prop);
+}
+
+pa_proplist* pa_proplist_new(void) {
+    return MAKE_PROPLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func));
+}
+
+void pa_proplist_free(pa_proplist* p) {
+    pa_assert(p);
+
+    pa_proplist_clear(p);
+    pa_hashmap_free(MAKE_HASHMAP(p), NULL, NULL);
+}
+
+/** Will accept only valid UTF-8 */
+int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
+    struct property *prop;
+    pa_bool_t add = FALSE;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!property_name_valid(key) || !pa_utf8_valid(value))
+        return -1;
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+        prop = pa_xnew(struct property, 1);
+        prop->key = pa_xstrdup(key);
+        add = TRUE;
+    } else
+        pa_xfree(prop->value);
+
+    prop->value = pa_xstrdup(value);
+    prop->nbytes = strlen(value)+1;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+    return 0;
+}
+
+/** Will accept only valid UTF-8 */
+int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) {
+    va_list ap;
+    int r;
+    char *t;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!property_name_valid(key) || !pa_utf8_valid(format))
+        return -1;
+
+    va_start(ap, format);
+    t = pa_vsprintf_malloc(format, ap);
+    va_end(ap);
+
+    r = pa_proplist_sets(p, key, t);
+
+    pa_xfree(t);
+    return r;
+}
+
+int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
+    struct property *prop;
+    pa_bool_t add = FALSE;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!property_name_valid(key))
+        return -1;
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+        prop = pa_xnew(struct property, 1);
+        prop->key = pa_xstrdup(key);
+        add = TRUE;
+    } else
+        pa_xfree(prop->value);
+
+    prop->value = pa_xmemdup(data, nbytes);
+    prop->nbytes = nbytes;
+
+    if (add)
+        pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+    return 0;
+}
+
+const char *pa_proplist_gets(pa_proplist *p, const char *key) {
+    struct property *prop;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!property_name_valid(key))
+        return NULL;
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+        return NULL;
+
+    if (prop->nbytes <= 0)
+        return NULL;
+
+    if (((char*) prop->value)[prop->nbytes-1] != 0)
+        return NULL;
+
+    if (strlen((char*) prop->value) != prop->nbytes-1)
+        return NULL;
+
+    if (!pa_utf8_valid((char*) prop->value))
+        return NULL;
+
+    return (char*) prop->value;
+}
+
+int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes) {
+    struct property *prop;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!property_name_valid(key))
+        return -1;
+
+    if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+        return -1;
+
+    *data = prop->value;
+    *nbytes = prop->nbytes;
+
+    return 0;
+}
+
+void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other) {
+    struct property *prop;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE);
+    pa_assert(other);
+
+    if (mode == PA_UPDATE_SET)
+        pa_proplist_clear(p);
+
+    while ((prop = pa_hashmap_iterate(MAKE_HASHMAP(other), &state, NULL))) {
+
+        if (mode == PA_UPDATE_MERGE && pa_proplist_contains(p, prop->key))
+            continue;
+
+        pa_assert_se(pa_proplist_set(p, prop->key, prop->value, prop->nbytes) == 0);
+    }
+}
+
+int pa_proplist_unset(pa_proplist *p, const char *key) {
+    struct property *prop;
+
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!property_name_valid(key))
+        return -1;
+
+    if (!(prop = pa_hashmap_remove(MAKE_HASHMAP(p), key)))
+        return -2;
+
+    property_free(prop);
+    return 0;
+}
+
+int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) {
+    const char * const * k;
+    int n = 0;
+
+    pa_assert(p);
+    pa_assert(keys);
+
+    for (k = keys; *k; k++)
+        if (!property_name_valid(*k))
+            return -1;
+
+    for (k = keys; *k; k++)
+        if (pa_proplist_unset(p, *k) >= 0)
+            n++;
+
+    return n;
+}
+
+const char *pa_proplist_iterate(pa_proplist *p, void **state) {
+    struct property *prop;
+
+    if (!(prop = pa_hashmap_iterate(MAKE_HASHMAP(p), state, NULL)))
+        return NULL;
+
+    return prop->key;
+}
+
+char *pa_proplist_to_string(pa_proplist *p) {
+    const char *key;
+    void *state = NULL;
+    pa_strbuf *buf;
+
+    pa_assert(p);
+
+    buf = pa_strbuf_new();
+
+    while ((key = pa_proplist_iterate(p, &state))) {
+
+        const char *v;
+
+        if ((v = pa_proplist_gets(p, key)))
+            pa_strbuf_printf(buf, "%s = \"%s\"\n", key, v);
+        else {
+            const void *value;
+            size_t nbytes;
+            char *c;
+
+            pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0);
+            c = pa_xnew(char, nbytes*2+1);
+            pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
+
+            pa_strbuf_printf(buf, "%s = hex:%s\n", key, c);
+            pa_xfree(c);
+        }
+    }
+
+    return pa_strbuf_tostring_free(buf);
+}
+
+int pa_proplist_contains(pa_proplist *p, const char *key) {
+    pa_assert(p);
+    pa_assert(key);
+
+    if (!property_name_valid(key))
+        return -1;
+
+    if (!(pa_hashmap_get(MAKE_HASHMAP(p), key)))
+        return 0;
+
+    return 1;
+}
+
+void pa_proplist_clear(pa_proplist *p) {
+    struct property *prop;
+    pa_assert(p);
+
+    while ((prop = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
+        property_free(prop);
+}
+
+pa_proplist* pa_proplist_copy(pa_proplist *template) {
+    pa_proplist *p;
+
+    pa_assert_se(p = pa_proplist_new());
+
+    if (template)
+        pa_proplist_update(p, PA_UPDATE_REPLACE, template);
+
+    return p;
+}
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
new file mode 100644 (file)
index 0000000..f75cca5
--- /dev/null
@@ -0,0 +1,217 @@
+#ifndef foopulseproplisthfoo
+#define foopulseproplisthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+
+PA_C_DECL_BEGIN
+
+/* Defined properties:
+ *
+ *    media.name                    "Guns'N'Roses: Civil War"
+ *    media.title                   "Civil War"
+ *    media.artist                  "Guns'N'Roses"
+ *    media.language                "de_DE"
+ *    media.filename
+ *    media.icon
+ *    media.icon_name
+ *    media.role                    video, music, game, event, phone, production, filter, abstract, stream
+ *    event.id                      button-click, session-login
+ *    event.mouse.x
+ *    event.mouse.y
+ *    event.mouse.hpos
+ *    event.mouse.vpos
+ *    event.mouse.button
+ *    window.name
+ *    window.id
+ *    window.icon
+ *    window.icon_name
+ *    window.x11.display
+ *    window.x11.screen
+ *    window.x11.monitor
+ *    window.x11.xid
+ *    application.name              "Rhythmbox Media Player"
+ *    application.id                "org.gnome.rhythmbox"
+ *    application.version
+ *    application.icon
+ *    application.icon_name
+ *    application.language
+ *    application.process.id
+ *    application.process.binary
+ *    application.process.user
+ *    application.process.host
+ *    device.string
+ *    device.api                     oss, alsa, sunaudio
+ *    device.description
+ *    device.bus_path
+ *    device.serial
+ *    device.vendor_product_id
+ *    device.class                   sound, modem, monitor, filter, abstract
+ *    device.form_factor             laptop-speakers, external-speakers, telephone, tv-capture, webcam-capture, microphone-capture, headset
+ *    device.connector               isa, pci, usb, firewire, bluetooth
+ *    device.access_mode             mmap, mmap_rewrite, serial
+ *    device.master_device
+ *    device.bufferin.buffer_size
+ *    device.bufferin.fragment_size
+ */
+#define PA_PROP_MEDIA_NAME                     "media.name"
+#define PA_PROP_MEDIA_TITLE                    "media.title"
+#define PA_PROP_MEDIA_ARTIST                   "media.artist"
+#define PA_PROP_MEDIA_LANGUAGE                 "media.language"
+#define PA_PROP_MEDIA_FILENAME                 "media.filename"
+#define PA_PROP_MEDIA_ICON                     "media.icon"
+#define PA_PROP_MEDIA_ICON_NAME                "media.icon_name"
+#define PA_PROP_MEDIA_ROLE                     "media.role"
+#define PA_PROP_EVENT_ID                       "event.id"
+#define PA_PROP_EVENT_MOUSE_X                  "event.mouse.x"
+#define PA_PROP_EVENT_MOUSE_Y                  "event.mouse.y"
+#define PA_PROP_EVENT_MOUSE_HPOS               "event.mouse.hpos"
+#define PA_PROP_EVENT_MOUSE_VPOS               "event.mouse.vpos"
+#define PA_PROP_EVENT_MOUSE_BUTTON             "event.mouse.button"
+#define PA_PROP_WINDOW_NAME                    "window.name"
+#define PA_PROP_WINDOW_ID                      "window.id"
+#define PA_PROP_WINDOW_ICON                    "window.icon"
+#define PA_PROP_WINDOW_ICON_NAME               "window.icon_name"
+#define PA_PROP_WINDOW_X11_DISPLAY             "window.x11.display"
+#define PA_PROP_WINDOW_X11_SCREEN              "window.x11.screen"
+#define PA_PROP_WINDOW_X11_MONITOR             "window.x11.monitor"
+#define PA_PROP_WINDOW_X11_XID                 "window.x11.xid"
+#define PA_PROP_APPLICATION_NAME               "application.name"
+#define PA_PROP_APPLICATION_ID                 "application.id"
+#define PA_PROP_APPLICATION_VERSION            "application.version"
+#define PA_PROP_APPLICATION_ICON               "application.icon"
+#define PA_PROP_APPLICATION_ICON_NAME          "application.icon_name"
+#define PA_PROP_APPLICATION_LANGUAGE           "application.language"
+#define PA_PROP_APPLICATION_PROCESS_ID         "application.process.id"
+#define PA_PROP_APPLICATION_PROCESS_BINARY     "application.process.binary"
+#define PA_PROP_APPLICATION_PROCESS_USER       "application.process.user"
+#define PA_PROP_APPLICATION_PROCESS_HOST       "application.process.host"
+#define PA_PROP_DEVICE_STRING                  "device.string"
+#define PA_PROP_DEVICE_API                     "device.api"
+#define PA_PROP_DEVICE_DESCRIPTION             "device.description"
+#define PA_PROP_DEVICE_BUS_PATH                "device.bus_path"
+#define PA_PROP_DEVICE_SERIAL                  "device.serial"
+#define PA_PROP_DEVICE_VENDOR_PRODUCT_ID       "device.vendor_product_id"
+#define PA_PROP_DEVICE_CLASS                   "device.class"
+#define PA_PROP_DEVICE_FORM_FACTOR             "device.form_factor"
+#define PA_PROP_DEVICE_CONNECTOR               "device.connector"
+#define PA_PROP_DEVICE_ACCESS_MODE             "device.access_mode"
+#define PA_PROP_DEVICE_MASTER_DEVICE           "device.master_device"
+#define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE   "device.buffering.buffer_size"
+#define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size"
+
+/** A property list object. Basically a dictionary with UTF-8 strings
+ * as keys and arbitrary data as values. \since 0.9.11 */
+typedef struct pa_proplist pa_proplist;
+
+/** Allocate a property list. \since 0.9.11 */
+pa_proplist* pa_proplist_new(void);
+
+/** Free the property list. \since 0.9.11 */
+void pa_proplist_free(pa_proplist* p);
+
+/** Append a new string entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. Will accept only valid
+ * UTF-8. \since 0.9.11 */
+int pa_proplist_sets(pa_proplist *p, const char *key, const char *value);
+
+/** Append a new string entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. Will accept only valid
+ * UTF-8. The data can be passed as printf()-style format string with
+ * arguments. \since 0.9.11 */
+int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) PA_GCC_PRINTF_ATTR(3,4);
+
+/** Append a new arbitrary data entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. \since 0.9.11 */
+int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes);
+
+/* Return a string entry for the specified key. Will return NULL if
+ * the data is not valid UTF-8. Will return a NUL-terminated string in
+ * an internally allocated buffer. The caller should make a copy of
+ * the data before accessing the property list again. \since 0.9.11 */
+const char *pa_proplist_gets(pa_proplist *p, const char *key);
+
+/** Return the the value for the specified key. Will return a
+ * NUL-terminated string for string entries. The pointer returned will
+ * point to an internally allocated buffer. The caller should make a
+ * copy of the data before the property list is accessed again. \since
+ * 0.9.11 */
+int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes);
+
+/** Update mode enum for pa_proplist_update(). \since 0.9.11 */
+typedef enum pa_update_mode {
+    PA_UPDATE_SET,  /*< Replace the entirey property list with the new one. Don't keep any of the old data around */
+    PA_UPDATE_MERGE, /*< Merge new property list into the existing one, not replacing any old entries if they share a common key with the new property list. */
+    PA_UPDATE_REPLACE /*< Merge new property list into the existing one, replacing all old entries that share a common key with  the new property list. */
+} pa_update_mode_t;
+
+/** Merge property list "other" into "p", adhering the merge mode as
+ * specified in "mode". \since 0.9.11 */
+void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other);
+
+/** Removes a single entry from the property list, identified be the
+ * specified key name. \since 0.9.11 */
+int pa_proplist_unset(pa_proplist *p, const char *key);
+
+/** Similar to pa_proplist_remove() but takes an array of keys to
+ * remove. The array should be terminated by a NULL pointer. Return -1
+ * on failure, otherwise the number of entries actually removed (which
+ * might even be 0, if there where no matching entries to
+ * remove). \since 0.9.11 */
+int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]);
+
+/** Iterate through the property list. The user should allocate a
+ * state variable of type void* and initialize it with NULL. A pointer
+ * to this variable should then be passed to pa_proplist_iterate()
+ * which should be called in a loop until it returns NULL which
+ * signifies EOL. The property list should not be modified during
+ * iteration through the list -- except for deleting the current
+ * looked at entry. On each invication this function will return the
+ * key string for the next entry. The keys in the property list do not
+ * have any particular order. \since 0.9.11 */
+const char *pa_proplist_iterate(pa_proplist *p, void **state);
+
+/** Format the property list nicely as a human readable string. Call pa_xfree() on the result. \since
+ * 0.9.11 */
+char *pa_proplist_to_string(pa_proplist *p);
+
+/** Returns 1 if an entry for the specified key is existant in the
+ * property list. \since 0.9.11 */
+int pa_proplist_contains(pa_proplist *p, const char *key);
+
+/** Remove all entries from the property list object. \since 0.9.11 */
+void pa_proplist_clear(pa_proplist *p);
+
+/** Allocate a new property list and copy over every single entry from
+ * the specific list. \since 0.9.11 */
+pa_proplist* pa_proplist_copy(pa_proplist *t);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h
new file mode 100644 (file)
index 0000000..ee8a4bb
--- /dev/null
@@ -0,0 +1,115 @@
+#ifndef foopulseaudiohfoo
+#define foopulseaudiohfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/sample.h>
+#include <pulse/def.h>
+#include <pulse/context.h>
+#include <pulse/stream.h>
+#include <pulse/introspect.h>
+#include <pulse/subscribe.h>
+#include <pulse/scache.h>
+#include <pulse/version.h>
+#include <pulse/error.h>
+#include <pulse/operation.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+#include <pulse/thread-mainloop.h>
+#include <pulse/mainloop.h>
+#include <pulse/mainloop-signal.h>
+#include <pulse/util.h>
+#include <pulse/timeval.h>
+
+/** \file
+ * Include all libpulse header files at once. The following
+ * files are included: \ref mainloop-api.h, \ref sample.h, \ref def.h,
+ * \ref context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h,
+ * \ref scache.h, \ref version.h, \ref error.h, \ref channelmap.h,
+ * \ref operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref
+ * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref timeval.h and
+ * \ref mainloop-signal.h at once */
+
+/** \mainpage
+ *
+ * \section intro_sec Introduction
+ *
+ * This document describes the client API for the PulseAudio sound
+ * server. The API comes in two flavours to accomodate different styles
+ * of applications and different needs in complexity:
+ *
+ * \li The complete but somewhat complicated to use asynchronous API
+ * \li The simplified, easy to use, but limited synchronous API
+ *
+ * All strings in PulseAudio are in the UTF-8 encoding, regardless of current
+ * locale. Some functions will filter invalid sequences from the string, some
+ * will simply fail. To ensure reliable behaviour, make sure everything you
+ * pass to the API is already in UTF-8.
+
+ * \section simple_sec Simple API
+ *
+ * Use this if you develop your program in synchronous style and just
+ * need a way to play or record data on the sound server. See
+ * \subpage simple for more details.
+ *
+ * \section async_sec Asynchronous API
+ *
+ * Use this if you develop your programs in asynchronous, event loop
+ * based style or if you want to use the advanced features of the
+ * PulseAudio API. A guide can be found in \subpage async.
+ *
+ * By using the built-in threaded main loop, it is possible to acheive a
+ * pseudo-synchronous API, which can be useful in synchronous applications
+ * where the simple API is insufficient. See the \ref async page for
+ * details.
+ *
+ * \section thread_sec Threads
+ *
+ * The PulseAudio client libraries are not designed to be used in a
+ * heavily threaded environment. They are however designed to be reentrant
+ * safe.
+ *
+ * To use a the libraries in a threaded environment, you must assure that
+ * all objects are only used in one thread at a time. Normally, this means
+ * that all objects belonging to a single context must be accessed from the
+ * same thread.
+ *
+ * The included main loop implementation is also not thread safe. Take care
+ * to make sure event lists are not manipulated when any other code is
+ * using the main loop.
+ *
+ * \section pkgconfig pkg-config
+ *
+ * The PulseAudio libraries provide pkg-config snippets for the different
+ * modules:
+ *
+ * \li libpulse - The asynchronous API and the internal main loop implementation.
+ * \li libpulse-mainloop-glib12 - GLIB 1.2 main loop bindings.
+ * \li libpulse-mainloop-glib - GLIB 2.x main loop bindings.
+ * \li libpulse-simple - The simple PulseAudio API.
+ */
+
+#endif
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
new file mode 100644 (file)
index 0000000..4aef5bb
--- /dev/null
@@ -0,0 +1,187 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <math.h>
+#include <string.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulse/timeval.h>
+
+#include "sample.h"
+
+size_t pa_sample_size(const pa_sample_spec *spec) {
+
+    static const size_t table[] = {
+        [PA_SAMPLE_U8] = 1,
+        [PA_SAMPLE_ULAW] = 1,
+        [PA_SAMPLE_ALAW] = 1,
+        [PA_SAMPLE_S16LE] = 2,
+        [PA_SAMPLE_S16BE] = 2,
+        [PA_SAMPLE_FLOAT32LE] = 4,
+        [PA_SAMPLE_FLOAT32BE] = 4,
+        [PA_SAMPLE_S32LE] = 4,
+        [PA_SAMPLE_S32BE] = 4,
+    };
+
+    pa_assert(spec);
+    pa_assert(spec->format >= 0);
+    pa_assert(spec->format < PA_SAMPLE_MAX);
+
+    return table[spec->format];
+}
+
+size_t pa_frame_size(const pa_sample_spec *spec) {
+    pa_assert(spec);
+
+    return pa_sample_size(spec) * spec->channels;
+}
+
+size_t pa_bytes_per_second(const pa_sample_spec *spec) {
+    pa_assert(spec);
+    return spec->rate*pa_frame_size(spec);
+}
+
+pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
+    pa_assert(spec);
+
+    return (((pa_usec_t) (length / pa_frame_size(spec)) * PA_USEC_PER_SEC) / spec->rate);
+}
+
+size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {
+    pa_assert(spec);
+
+    return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec);
+}
+
+int pa_sample_spec_valid(const pa_sample_spec *spec) {
+    pa_assert(spec);
+
+    if (spec->rate <= 0 ||
+        spec->rate > PA_RATE_MAX ||
+        spec->channels <= 0 ||
+        spec->channels > PA_CHANNELS_MAX ||
+        spec->format >= PA_SAMPLE_MAX ||
+        spec->format < 0)
+        return 0;
+
+    return 1;
+}
+
+int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    return
+        (a->format == b->format) &&
+        (a->rate == b->rate) &&
+        (a->channels == b->channels);
+}
+
+const char *pa_sample_format_to_string(pa_sample_format_t f) {
+    static const char* const table[]= {
+        [PA_SAMPLE_U8] = "u8",
+        [PA_SAMPLE_ALAW] = "aLaw",
+        [PA_SAMPLE_ULAW] = "uLaw",
+        [PA_SAMPLE_S16LE] = "s16le",
+        [PA_SAMPLE_S16BE] = "s16be",
+        [PA_SAMPLE_FLOAT32LE] = "float32le",
+        [PA_SAMPLE_FLOAT32BE] = "float32be",
+        [PA_SAMPLE_S32LE] = "s32le",
+        [PA_SAMPLE_S32BE] = "s32be",
+    };
+
+    if (f < 0 || f >= PA_SAMPLE_MAX)
+        return NULL;
+
+    return table[f];
+}
+
+char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
+    pa_assert(s);
+    pa_assert(l);
+    pa_assert(spec);
+
+    if (!pa_sample_spec_valid(spec))
+        pa_snprintf(s, l, "Invalid");
+    else
+        pa_snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
+
+    return s;
+}
+
+char* pa_bytes_snprint(char *s, size_t l, unsigned v) {
+    pa_assert(s);
+
+    if (v >= ((unsigned) 1024)*1024*1024)
+        pa_snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024);
+    else if (v >= ((unsigned) 1024)*1024)
+        pa_snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024);
+    else if (v >= (unsigned) 1024)
+        pa_snprintf(s, l, "%0.1f KiB", ((double) v)/1024);
+    else
+        pa_snprintf(s, l, "%u B", (unsigned) v);
+
+    return s;
+}
+
+pa_sample_format_t pa_parse_sample_format(const char *format) {
+    pa_assert(format);
+
+    if (strcasecmp(format, "s16le") == 0)
+        return PA_SAMPLE_S16LE;
+    else if (strcasecmp(format, "s16be") == 0)
+        return PA_SAMPLE_S16BE;
+    else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0)
+        return PA_SAMPLE_S16NE;
+    else if (strcasecmp(format, "s16re") == 0)
+        return PA_SAMPLE_S16RE;
+    else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0)
+        return PA_SAMPLE_U8;
+    else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0 || strcasecmp(format, "float") == 0)
+        return PA_SAMPLE_FLOAT32NE;
+    else if (strcasecmp(format, "float32re") == 0)
+        return PA_SAMPLE_FLOAT32RE;
+    else if (strcasecmp(format, "float32le") == 0)
+        return PA_SAMPLE_FLOAT32LE;
+    else if (strcasecmp(format, "float32be") == 0)
+        return PA_SAMPLE_FLOAT32BE;
+    else if (strcasecmp(format, "ulaw") == 0 || strcasecmp(format, "mulaw") == 0)
+        return PA_SAMPLE_ULAW;
+    else if (strcasecmp(format, "alaw") == 0)
+        return PA_SAMPLE_ALAW;
+    else if (strcasecmp(format, "s32le") == 0)
+        return PA_SAMPLE_S32LE;
+    else if (strcasecmp(format, "s32be") == 0)
+        return PA_SAMPLE_S32BE;
+    else if (strcasecmp(format, "s32ne") == 0 || strcasecmp(format, "s32") == 0 || strcasecmp(format, "32") == 0)
+        return PA_SAMPLE_S32NE;
+    else if (strcasecmp(format, "s32re") == 0)
+        return PA_SAMPLE_S32RE;
+
+    return -1;
+}
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
new file mode 100644 (file)
index 0000000..1ba3f87
--- /dev/null
@@ -0,0 +1,215 @@
+#ifndef foosamplehfoo
+#define foosamplehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <math.h>
+
+#include <pulse/gccmacro.h>
+#include <pulse/cdecl.h>
+
+/** \page sample Sample Format Specifications
+ *
+ * \section overv_sec Overview
+ *
+ * PulseAudio is capable of handling a multitude of sample formats, rates
+ * and channels, transparently converting and mixing them as needed.
+ *
+ * \section format_sec Sample Format
+ *
+ * PulseAudio supports the following sample formats:
+ *
+ * \li PA_SAMPLE_U8 - Unsigned 8 bit integer PCM.
+ * \li PA_SAMPLE_S16LE - Signed 16 integer bit PCM, little endian.
+ * \li PA_SAMPLE_S16BE - Signed 16 integer bit PCM, big endian.
+ * \li PA_SAMPLE_FLOAT32LE - 32 bit IEEE floating point PCM, little endian.
+ * \li PA_SAMPLE_FLOAT32BE - 32 bit IEEE floating point PCM, big endian.
+ * \li PA_SAMPLE_ALAW - 8 bit a-Law.
+ * \li PA_SAMPLE_ULAW - 8 bit mu-Law.
+ * \li PA_SAMPLE_S32LE - Signed 32 bit integer PCM, little endian.
+ * \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian.
+ *
+ * The floating point sample formats have the range from -1 to 1.
+ *
+ * The sample formats that are sensitive to endianness have convenience
+ * macros for native endian (NE), and reverse endian (RE).
+ *
+ * \section rate_sec Sample Rates
+ *
+ * PulseAudio supports any sample rate between 1 Hz and 4 GHz. There is no
+ * point trying to exceed the sample rate of the output device though as the
+ * signal will only get downsampled, consuming CPU on the machine running the
+ * server.
+ *
+ * \section chan_sec Channels
+ *
+ * PulseAudio supports up to 16 individiual channels. The order of the
+ * channels is up to the application, but they must be continous. To map
+ * channels to speakers, see \ref channelmap.
+ *
+ * \section calc_sec Calculations
+ *
+ * The PulseAudio library contains a number of convenience functions to do
+ * calculations on sample formats:
+ *
+ * \li pa_bytes_per_second() - The number of bytes one second of audio will
+ *                             take given a sample format.
+ * \li pa_frame_size() - The size, in bytes, of one frame (i.e. one set of
+ *                       samples, one for each channel).
+ * \li pa_sample_size() - The size, in bytes, of one sample.
+ * \li pa_bytes_to_usec() - Calculate the time it would take to play a buffer
+ *                          of a certain size.
+ *
+ * \section util_sec Convenience Functions
+ *
+ * The library also contains a couple of other convenience functions:
+ *
+ * \li pa_sample_spec_valid() - Tests if a sample format specification is
+ *                              valid.
+ * \li pa_sample_spec_equal() - Tests if the sample format specifications are
+ *                              identical.
+ * \li pa_sample_format_to_string() - Return a textual description of a
+ *                                    sample format.
+ * \li pa_parse_sample_format() - Parse a text string into a sample format.
+ * \li pa_sample_spec_snprint() - Create a textual description of a complete
+ *                                 sample format specification.
+ * \li pa_bytes_snprint() - Pretty print a byte value (e.g. 2.5 MiB).
+ */
+
+/** \file
+ * Constants and routines for sample type handling */
+
+PA_C_DECL_BEGIN
+
+#if !defined(WORDS_BIGENDIAN)
+#if defined(__BYTE_ORDER)
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#endif
+#endif
+
+/** Maximum number of allowed channels */
+#define PA_CHANNELS_MAX 32U
+
+/** Maximum allowed sample rate */
+#define PA_RATE_MAX (48000U*4U)
+
+/** Sample format */
+typedef enum pa_sample_format {
+    PA_SAMPLE_U8,              /**< Unsigned 8 Bit PCM */
+    PA_SAMPLE_ALAW,            /**< 8 Bit a-Law */
+    PA_SAMPLE_ULAW,            /**< 8 Bit mu-Law */
+    PA_SAMPLE_S16LE,           /**< Signed 16 Bit PCM, little endian (PC) */
+    PA_SAMPLE_S16BE,           /**< Signed 16 Bit PCM, big endian */
+    PA_SAMPLE_FLOAT32LE,       /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */
+    PA_SAMPLE_FLOAT32BE,       /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */
+    PA_SAMPLE_S32LE,           /**< Signed 32 Bit PCM, little endian (PC) */
+    PA_SAMPLE_S32BE,           /**< Signed 32 Bit PCM, big endian (PC) */
+    PA_SAMPLE_MAX,             /**< Upper limit of valid sample types */
+    PA_SAMPLE_INVALID = -1     /**< An invalid value */
+} pa_sample_format_t;
+
+#ifdef WORDS_BIGENDIAN
+/** Signed 16 Bit PCM, native endian */
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
+/** 32 Bit IEEE floating point, native endian */
+#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
+/** Signed 32 Bit PCM, native endian */
+#define PA_SAMPLE_S32NE PA_SAMPLE_S32BE
+/** Signed 16 Bit PCM reverse endian */
+#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
+/** 32 Bit IEEE floating point, reverse endian */
+#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
+/** Signed 32 Bit PCM reverse endian */
+#define PA_SAMPLE_S32RE PA_SAMPLE_S32LE
+#else
+/** Signed 16 Bit PCM, native endian */
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
+/** 32 Bit IEEE floating point, native endian */
+#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
+/** Signed 32 Bit PCM, native endian */
+#define PA_SAMPLE_S32NE PA_SAMPLE_S32LE
+/** Signed 16 Bit PCM reverse endian */
+#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
+/** 32 Bit IEEE floating point, reverse endian */
+#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
+/** Signed 32 Bit PCM reverse endian */
+#define PA_SAMPLE_S32RE PA_SAMPLE_S32BE
+#endif
+
+/** A Shortcut for PA_SAMPLE_FLOAT32NE */
+#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE
+
+/** A sample format and attribute specification */
+typedef struct pa_sample_spec {
+    pa_sample_format_t format;     /**< The sample format */
+    uint32_t rate;                 /**< The sample rate. (e.g. 44100) */
+    uint8_t channels;              /**< Audio channels. (1 for mono, 2 for stereo, ...) */
+} pa_sample_spec;
+
+/** Type for usec specifications (unsigned). Always 64 bit. */
+typedef uint64_t pa_usec_t;
+
+/** Return the amount of bytes playback of a second of audio with the specified sample type takes */
+size_t pa_bytes_per_second(const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Return the size of a frame with the specific sample type */
+size_t pa_frame_size(const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Return the size of a sample with the specific sample type */
+size_t pa_sample_size(const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Calculate the time the specified bytes take to play with the specified sample type */
+pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Calculates the number of bytes that are required for the specified time. \since 0.9 */
+size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Return non-zero when the sample type specification is valid */
+int pa_sample_spec_valid(const pa_sample_spec *spec) PA_GCC_PURE;
+
+/** Return non-zero when the two sample type specifications match */
+int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) PA_GCC_PURE;
+
+/** Return a descriptive string for the specified sample format. \since 0.8 */
+const char *pa_sample_format_to_string(pa_sample_format_t f) PA_GCC_PURE;
+
+/** Parse a sample format text. Inverse of pa_sample_format_to_string() */
+pa_sample_format_t pa_parse_sample_format(const char *format) PA_GCC_PURE;
+
+/** Maximum required string length for pa_sample_spec_snprint() */
+#define PA_SAMPLE_SPEC_SNPRINT_MAX 32
+
+/** Pretty print a sample type specification to a string */
+char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec);
+
+/** Pretty print a byte size value. (i.e. "2.5 MiB") */
+char* pa_bytes_snprint(char *s, size_t l, unsigned v);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
new file mode 100644 (file)
index 0000000..5e31e7a
--- /dev/null
@@ -0,0 +1,257 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pulse/utf8.h>
+
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/proplist-util.h>
+
+#include "internal.h"
+
+#include "scache.h"
+
+int pa_stream_connect_upload(pa_stream *s, size_t length) {
+    pa_tagstruct *t;
+    uint32_t tag;
+    const char *name;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, length > 0, PA_ERR_INVALID);
+
+    if (!(name = pa_proplist_gets(s->proplist, PA_PROP_EVENT_ID)))
+        name = pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME);
+
+    PA_CHECK_VALIDITY(s->context, name && *name && pa_utf8_valid(name), PA_ERR_INVALID);
+
+    pa_stream_ref(s);
+
+    s->direction = PA_STREAM_UPLOAD;
+    s->flags = 0;
+
+    t = pa_tagstruct_command(s->context, PA_COMMAND_CREATE_UPLOAD_STREAM, &tag);
+
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_sample_spec(t, &s->sample_spec);
+    pa_tagstruct_put_channel_map(t, &s->channel_map);
+    pa_tagstruct_putu32(t, length);
+
+    if (s->context->version >= 13) {
+        pa_init_proplist(s->proplist);
+        pa_tagstruct_put_proplist(t, s->proplist);
+    }
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
+
+    pa_stream_set_state(s, PA_STREAM_CREATING);
+
+    pa_stream_unref(s);
+    return 0;
+}
+
+int pa_stream_finish_upload(pa_stream *s) {
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    pa_stream_ref(s);
+
+    t = pa_tagstruct_command(s->context, PA_COMMAND_FINISH_UPLOAD_STREAM, &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s, NULL);
+
+    pa_stream_unref(s);
+    return 0;
+}
+
+static void play_sample_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int success = 1;
+    uint32_t idx = PA_INVALID_INDEX;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        success = 0;
+    } else if ((o->context->version >= 13 && pa_tagstruct_getu32(t, &idx) < 0) ||
+               !pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    } else if (o->context->version >= 13 && idx == PA_INVALID_INDEX)
+        success = 0;
+
+    if (o->callback) {
+        pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
+        cb(o->context, success, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t idx;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        idx = PA_INVALID_INDEX;
+    } else if (pa_tagstruct_getu32(t, &idx) < 0 ||
+               !pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_context_play_sample_cb_t cb = (pa_context_play_sample_cb_t) o->callback;
+        cb(o->context, idx, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+
+pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    if (!dev)
+        dev = c->conf->default_sink;
+
+    t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, dev);
+    pa_tagstruct_putu32(t, volume);
+    pa_tagstruct_puts(t, name);
+
+    if (c->version >= 13) {
+        pa_proplist *p = pa_proplist_new();
+        pa_tagstruct_put_proplist(t, p);
+        pa_proplist_free(p);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_proplist *p, pa_context_play_sample_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, p, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    if (!dev)
+        dev = c->conf->default_sink;
+
+    t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, dev);
+    pa_tagstruct_putu32(t, volume);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_put_proplist(t, p);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_with_proplist_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_SAMPLE, &tag);
+    pa_tagstruct_puts(t, name);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
new file mode 100644 (file)
index 0000000..f380b4e
--- /dev/null
@@ -0,0 +1,122 @@
+#ifndef fooscachehfoo
+#define fooscachehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/context.h>
+#include <pulse/stream.h>
+#include <pulse/cdecl.h>
+
+/** \page scache Sample Cache
+ *
+ * \section overv_sec Overview
+ *
+ * The sample cache provides a simple way of overcoming high network latencies
+ * and reducing bandwidth. Instead of streaming a sound precisely when it
+ * should be played, it is stored on the server and only the command to start
+ * playing it needs to be sent.
+ *
+ * \section create_sec Creation
+ *
+ * To create a sample, the normal stream API is used (see \ref streams). The
+ * function pa_stream_connect_upload() will make sure the stream is stored as
+ * a sample on the server.
+ *
+ * To complete the upload, pa_stream_finish_upload() is called and the sample
+ * will receive the same name as the stream. If the upload should be aborted,
+ * simply call pa_stream_disconnect().
+ *
+ * \section play_sec Playing samples
+ *
+ * To play back a sample, simply call pa_context_play_sample():
+ *
+ * \code
+ * pa_operation *o;
+ *
+ * o = pa_context_play_sample(my_context,
+ *                            "sample2",       // Name of my sample
+ *                            NULL,            // Use default sink
+ *                            PA_VOLUME_NORM,  // Full volume
+ *                            NULL,            // Don't need a callback
+ *                            NULL
+ *                            );
+ * if (o)
+ *     pa_operation_unref(o);
+ * \endcode
+ *
+ * \section rem_sec Removing samples
+ *
+ * When a sample is no longer needed, it should be removed on the server to
+ * save resources. The sample is deleted using pa_context_remove_sample().
+ */
+
+/** \file
+ * All sample cache related routines */
+
+PA_C_DECL_BEGIN
+
+/** Callback prototype for pa_context_play_sample_with_proplist(). The
+ * idx value is the index of the sink input object, or
+ * PA_INVALID_INDEX on failure. \since 0.9.11 */
+typedef void (*pa_context_play_sample_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+
+/** Make this stream a sample upload stream */
+int pa_stream_connect_upload(pa_stream *s, size_t length);
+
+/** Finish the sample upload, the stream name will become the sample
+ * name. You cancel a sample upload by issuing
+ * pa_stream_disconnect() */
+int pa_stream_finish_upload(pa_stream *s);
+
+/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Play a sample from the sample cache to the specified device. If
+ * the latter is NULL use the default sink. Returns an operation
+ * object */
+pa_operation* pa_context_play_sample(
+        pa_context *c               /**< Context */,
+        const char *name            /**< Name of the sample to play */,
+        const char *dev             /**< Sink to play this sample on */,
+        pa_volume_t volume          /**< Volume to play this sample with */ ,
+        pa_context_success_cb_t cb  /**< Call this function after successfully starting playback, or NULL */,
+        void *userdata              /**< Userdata to pass to the callback */);
+
+/** Play a sample from the sample cache to the specified device,
+ * allowing specification of a property list for the playback
+ * stream. If the latter is NULL use the default sink. Returns an
+ * operation object. \since 0.9.11 */
+pa_operation* pa_context_play_sample_with_proplist(
+        pa_context *c                   /**< Context */,
+        const char *name                /**< Name of the sample to play */,
+        const char *dev                 /**< Sink to play this sample on */,
+        pa_volume_t volume              /**< Volume to play this sample with */ ,
+        pa_proplist *proplist           /**< Property list for this sound. The property list of the cached entry will be merged into this property list */,
+        pa_context_play_sample_cb_t cb  /**< Call this function after successfully starting playback, or NULL */,
+        void *userdata                  /**< Userdata to pass to the callback */);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/simple.c b/src/pulse/simple.c
new file mode 100644 (file)
index 0000000..7039683
--- /dev/null
@@ -0,0 +1,456 @@
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/thread-mainloop.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/native-common.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "simple.h"
+
+struct pa_simple {
+    pa_threaded_mainloop *mainloop;
+    pa_context *context;
+    pa_stream *stream;
+    pa_stream_direction_t direction;
+
+    const void *read_data;
+    size_t read_index, read_length;
+
+    int operation_success;
+};
+
+#define CHECK_VALIDITY_RETURN_ANY(rerror, expression, error, ret) do { \
+if (!(expression)) { \
+    if (rerror) \
+        *(rerror) = error; \
+    return (ret); \
+    }  \
+} while(0);
+
+#define CHECK_SUCCESS_GOTO(p, rerror, expression, label) do { \
+if (!(expression)) { \
+    if (rerror) \
+        *(rerror) = pa_context_errno((p)->context); \
+    goto label; \
+    }  \
+} while(0);
+
+#define CHECK_DEAD_GOTO(p, rerror, label) do { \
+if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \
+    !(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \
+        if (((p)->context && pa_context_get_state((p)->context) == PA_CONTEXT_FAILED) || \
+            ((p)->stream && pa_stream_get_state((p)->stream) == PA_STREAM_FAILED)) { \
+            if (rerror) \
+                *(rerror) = pa_context_errno((p)->context); \
+        } else \
+            if (rerror) \
+                *(rerror) = PA_ERR_BADSTATE; \
+        goto label; \
+    } \
+} while(0);
+
+static void context_state_cb(pa_context *c, void *userdata) {
+    pa_simple *p = userdata;
+    pa_assert(c);
+    pa_assert(p);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_READY:
+        case PA_CONTEXT_TERMINATED:
+        case PA_CONTEXT_FAILED:
+            pa_threaded_mainloop_signal(p->mainloop, 0);
+            break;
+
+        case PA_CONTEXT_UNCONNECTED:
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+    }
+}
+
+static void stream_state_cb(pa_stream *s, void * userdata) {
+    pa_simple *p = userdata;
+    pa_assert(s);
+    pa_assert(p);
+
+    switch (pa_stream_get_state(s)) {
+
+        case PA_STREAM_READY:
+        case PA_STREAM_FAILED:
+        case PA_STREAM_TERMINATED:
+            pa_threaded_mainloop_signal(p->mainloop, 0);
+            break;
+
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+            break;
+    }
+}
+
+static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
+    pa_simple *p = userdata;
+    pa_assert(p);
+
+    pa_threaded_mainloop_signal(p->mainloop, 0);
+}
+
+static void stream_latency_update_cb(pa_stream *s, void *userdata) {
+    pa_simple *p = userdata;
+
+    pa_assert(p);
+
+    pa_threaded_mainloop_signal(p->mainloop, 0);
+}
+
+pa_simple* pa_simple_new(
+        const char *server,
+        const char *name,
+        pa_stream_direction_t dir,
+        const char *dev,
+        const char *stream_name,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        const pa_buffer_attr *attr,
+        int *rerror) {
+
+    pa_simple *p;
+    int error = PA_ERR_INTERNAL, r;
+
+    CHECK_VALIDITY_RETURN_ANY(rerror, !server || *server, PA_ERR_INVALID, NULL);
+    CHECK_VALIDITY_RETURN_ANY(rerror, dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD, PA_ERR_INVALID, NULL);
+    CHECK_VALIDITY_RETURN_ANY(rerror, !dev || *dev, PA_ERR_INVALID, NULL);
+    CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
+    CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
+
+    p = pa_xnew(pa_simple, 1);
+    p->context = NULL;
+    p->stream = NULL;
+    p->direction = dir;
+    p->read_data = NULL;
+    p->read_index = p->read_length = 0;
+
+    if (!(p->mainloop = pa_threaded_mainloop_new()))
+        goto fail;
+
+    if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name)))
+        goto fail;
+
+    pa_context_set_state_callback(p->context, context_state_cb, p);
+
+    if (pa_context_connect(p->context, server, 0, NULL) < 0) {
+        error = pa_context_errno(p->context);
+        goto fail;
+    }
+
+    pa_threaded_mainloop_lock(p->mainloop);
+
+    if (pa_threaded_mainloop_start(p->mainloop) < 0)
+        goto unlock_and_fail;
+
+    /* Wait until the context is ready */
+    pa_threaded_mainloop_wait(p->mainloop);
+
+    if (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
+        error = pa_context_errno(p->context);
+        goto unlock_and_fail;
+    }
+
+    if (!(p->stream = pa_stream_new(p->context, stream_name, ss, map))) {
+        error = pa_context_errno(p->context);
+        goto unlock_and_fail;
+    }
+
+    pa_stream_set_state_callback(p->stream, stream_state_cb, p);
+    pa_stream_set_read_callback(p->stream, stream_request_cb, p);
+    pa_stream_set_write_callback(p->stream, stream_request_cb, p);
+    pa_stream_set_latency_update_callback(p->stream, stream_latency_update_cb, p);
+
+    if (dir == PA_STREAM_PLAYBACK)
+        r = pa_stream_connect_playback(p->stream, dev, attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
+    else
+        r = pa_stream_connect_record(p->stream, dev, attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE);
+
+    if (r < 0) {
+        error = pa_context_errno(p->context);
+        goto unlock_and_fail;
+    }
+
+    /* Wait until the stream is ready */
+    pa_threaded_mainloop_wait(p->mainloop);
+
+    /* Wait until the stream is ready */
+    if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
+        error = pa_context_errno(p->context);
+        goto unlock_and_fail;
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+
+    return p;
+
+unlock_and_fail:
+    pa_threaded_mainloop_unlock(p->mainloop);
+
+fail:
+    if (rerror)
+        *rerror = error;
+    pa_simple_free(p);
+    return NULL;
+}
+
+void pa_simple_free(pa_simple *s) {
+    pa_assert(s);
+
+    if (s->mainloop)
+        pa_threaded_mainloop_stop(s->mainloop);
+
+    if (s->stream)
+        pa_stream_unref(s->stream);
+
+    if (s->context)
+        pa_context_unref(s->context);
+
+    if (s->mainloop)
+        pa_threaded_mainloop_free(s->mainloop);
+
+    pa_xfree(s);
+}
+
+int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
+    pa_assert(p);
+
+    CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
+    CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
+
+    pa_threaded_mainloop_lock(p->mainloop);
+
+    CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+    while (length > 0) {
+        size_t l;
+        int r;
+
+        while (!(l = pa_stream_writable_size(p->stream))) {
+            pa_threaded_mainloop_wait(p->mainloop);
+            CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+        }
+
+        CHECK_SUCCESS_GOTO(p, rerror, l != (size_t) -1, unlock_and_fail);
+
+        if (l > length)
+            l = length;
+
+        r = pa_stream_write(p->stream, data, l, NULL, 0, PA_SEEK_RELATIVE);
+        CHECK_SUCCESS_GOTO(p, rerror, r >= 0, unlock_and_fail);
+
+        data = (const uint8_t*) data + l;
+        length -= l;
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return 0;
+
+unlock_and_fail:
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return -1;
+}
+
+int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
+    pa_assert(p);
+
+    CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1);
+    CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
+
+    pa_threaded_mainloop_lock(p->mainloop);
+
+    CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+    while (length > 0) {
+        size_t l;
+
+        while (!p->read_data) {
+            int r;
+
+            r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
+            CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail);
+
+            if (!p->read_data) {
+                pa_threaded_mainloop_wait(p->mainloop);
+                CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+            } else
+                p->read_index = 0;
+        }
+
+        l = p->read_length < length ? p->read_length : length;
+        memcpy(data, (const uint8_t*) p->read_data+p->read_index, l);
+
+        data = (uint8_t*) data + l;
+        length -= l;
+
+        p->read_index += l;
+        p->read_length -= l;
+
+        if (!p->read_length) {
+            int r;
+
+            r = pa_stream_drop(p->stream);
+            p->read_data = NULL;
+            p->read_length = 0;
+            p->read_index = 0;
+
+            CHECK_SUCCESS_GOTO(p, rerror, r == 0, unlock_and_fail);
+        }
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return 0;
+
+unlock_and_fail:
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return -1;
+}
+
+static void success_cb(pa_stream *s, int success, void *userdata) {
+    pa_simple *p = userdata;
+
+    pa_assert(s);
+    pa_assert(p);
+
+    p->operation_success = success;
+    pa_threaded_mainloop_signal(p->mainloop, 0);
+}
+
+int pa_simple_drain(pa_simple *p, int *rerror) {
+    pa_operation *o = NULL;
+
+    pa_assert(p);
+
+    CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
+
+    pa_threaded_mainloop_lock(p->mainloop);
+    CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+    o = pa_stream_drain(p->stream, success_cb, p);
+    CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
+
+    p->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        pa_threaded_mainloop_wait(p->mainloop);
+        CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+    }
+    CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
+
+    pa_operation_unref(o);
+    pa_threaded_mainloop_unlock(p->mainloop);
+
+    return 0;
+
+unlock_and_fail:
+
+    if (o) {
+        pa_operation_cancel(o);
+        pa_operation_unref(o);
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return -1;
+}
+
+int pa_simple_flush(pa_simple *p, int *rerror) {
+    pa_operation *o = NULL;
+
+    pa_assert(p);
+
+    CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
+
+    pa_threaded_mainloop_lock(p->mainloop);
+    CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+    o = pa_stream_flush(p->stream, success_cb, p);
+    CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
+
+    p->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        pa_threaded_mainloop_wait(p->mainloop);
+        CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+    }
+    CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);
+
+    pa_operation_unref(o);
+    pa_threaded_mainloop_unlock(p->mainloop);
+
+    return 0;
+
+unlock_and_fail:
+
+    if (o) {
+        pa_operation_cancel(o);
+        pa_operation_unref(o);
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return -1;
+}
+
+pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) {
+    pa_usec_t t;
+    int negative;
+
+    pa_assert(p);
+
+    pa_threaded_mainloop_lock(p->mainloop);
+
+    for (;;) {
+        CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
+
+        if (pa_stream_get_latency(p->stream, &t, &negative) >= 0)
+            break;
+
+        CHECK_SUCCESS_GOTO(p, rerror, pa_context_errno(p->context) == PA_ERR_NODATA, unlock_and_fail);
+
+        /* Wait until latency data is available again */
+        pa_threaded_mainloop_wait(p->mainloop);
+    }
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+
+    return negative ? 0 : t;
+
+unlock_and_fail:
+
+    pa_threaded_mainloop_unlock(p->mainloop);
+    return (pa_usec_t) -1;
+}
+
diff --git a/src/pulse/simple.h b/src/pulse/simple.h
new file mode 100644 (file)
index 0000000..a1380a0
--- /dev/null
@@ -0,0 +1,147 @@
+#ifndef foosimplehfoo
+#define foosimplehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/def.h>
+#include <pulse/cdecl.h>
+
+/** \page simple Simple API
+ *
+ * \section overv_sec Overview
+ *
+ * The simple API is designed for applications with very basic sound
+ * playback or capture needs. It can only support a single stream per
+ * connection and has no handling of complex features like events, channel
+ * mappings and volume control. It is, however, very simple to use and
+ * quite sufficent for many programs.
+ *
+ * \section conn_sec Connecting
+ *
+ * The first step before using the sound system is to connect to the
+ * server. This is normally done this way:
+ *
+ * \code
+ * pa_simple *s;
+ * pa_sample_spec ss;
+ *
+ * ss.format = PA_SAMPLE_S16NE;
+ * ss.channels = 2;
+ * ss.rate = 44100;
+ *
+ * s = pa_simple_new(NULL,               // Use the default server.
+ *                   "Fooapp",           // Our application's name.
+ *                   PA_STREAM_PLAYBACK,
+ *                   NULL,               // Use the default device.
+ *                   "Music",            // Description of our stream.
+ *                   &ss,                // Our sample format.
+ *                   NULL,               // Use default channel map
+ *                   NULL,               // Use default buffering attributes.
+ *                   NULL,               // Ignore error code.
+ *                   );
+ * \endcode
+ *
+ * At this point a connected object is returned, or NULL if there was a
+ * problem connecting.
+ *
+ * \section transfer_sec Transferring data
+ *
+ * Once the connection is established to the server, data can start flowing.
+ * Using the connection is very similar to the normal read() and write()
+ * system calls. The main difference is that they're call pa_simple_read()
+ * and pa_simple_write(). Note that these operations always block.
+ *
+ * \section ctrl_sec Buffer control
+ *
+ * If a playback stream is used then a few other operations are available:
+ *
+ * \li pa_simple_drain() - Will wait for all sent data to finish playing.
+ * \li pa_simple_flush() - Will throw away all data currently in buffers.
+ * \li pa_simple_get_playback_latency() - Will return the total latency of
+ *                                        the playback pipeline.
+ *
+ * \section cleanup_sec Cleanup
+ *
+ * Once playback or capture is complete, the connection should be closed
+ * and resources freed. This is done through:
+ *
+ * \code
+ * pa_simple_free(s);
+ * \endcode
+ */
+
+/** \file
+ * A simple but limited synchronous playback and recording
+ * API. This is a synchronous, simplified wrapper around the standard
+ * asynchronous API. */
+
+/** \example pacat-simple.c
+ * A simple playback tool using the simple API */
+
+/** \example parec-simple.c
+ * A simple recording tool using the simple API */
+
+PA_C_DECL_BEGIN
+
+/** \struct pa_simple
+ * An opaque simple connection object */
+typedef struct pa_simple pa_simple;
+
+/** Create a new connection to the server */
+pa_simple* pa_simple_new(
+    const char *server,                 /**< Server name, or NULL for default */
+    const char *name,                   /**< A descriptive name for this client (application name, ...) */
+    pa_stream_direction_t dir,          /**< Open this stream for recording or playback? */
+    const char *dev,                    /**< Sink (resp. source) name, or NULL for default */
+    const char *stream_name,            /**< A descriptive name for this client (application name, song title, ...) */
+    const pa_sample_spec *ss,           /**< The sample type to use */
+    const pa_channel_map *map,          /**< The channel map to use, or NULL for default */
+    const pa_buffer_attr *attr,         /**< Buffering attributes, or NULL for default */
+    int *error                          /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */
+    );
+
+/** Close and free the connection to the server. The connection objects becomes invalid when this is called. */
+void pa_simple_free(pa_simple *s);
+
+/** Write some data to the server */
+int pa_simple_write(pa_simple *s, const void*data, size_t bytes, int *error);
+
+/** Wait until all data already written is played by the daemon */
+int pa_simple_drain(pa_simple *s, int *error);
+
+/** Read some data from the server */
+int pa_simple_read(pa_simple *s, void*data, size_t bytes, int *error);
+
+/** Return the playback latency. */
+pa_usec_t pa_simple_get_latency(pa_simple *s, int *error);
+
+/** Flush the playback buffer. */
+int pa_simple_flush(pa_simple *s, int *error);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
new file mode 100644 (file)
index 0000000..3bee7a0
--- /dev/null
@@ -0,0 +1,2258 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+
+#include <pulse/def.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/rtclock.h>
+
+#include "internal.h"
+
+#define LATENCY_IPOL_INTERVAL_USEC (333*PA_USEC_PER_MSEC)
+
+#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
+#define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC)
+#define SMOOTHER_MIN_HISTORY (4)
+
+pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
+    return pa_stream_new_with_proplist(c, name, ss, map, NULL);
+}
+
+static void reset_callbacks(pa_stream *s) {
+    s->read_callback = NULL;
+    s->read_userdata = NULL;
+    s->write_callback = NULL;
+    s->write_userdata = NULL;
+    s->state_callback = NULL;
+    s->state_userdata = NULL;
+    s->overflow_callback = NULL;
+    s->overflow_userdata = NULL;
+    s->underflow_callback = NULL;
+    s->underflow_userdata = NULL;
+    s->latency_update_callback = NULL;
+    s->latency_update_userdata = NULL;
+    s->moved_callback = NULL;
+    s->moved_userdata = NULL;
+    s->suspended_callback = NULL;
+    s->suspended_userdata = NULL;
+    s->started_callback = NULL;
+    s->started_userdata = NULL;
+}
+
+pa_stream *pa_stream_new_with_proplist(
+        pa_context *c,
+        const char *name,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_proplist *p) {
+
+    pa_stream *s;
+    int i;
+    pa_channel_map tmap;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
+
+    if (!map)
+        PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
+
+    s = pa_xnew(pa_stream, 1);
+    PA_REFCNT_INIT(s);
+    s->context = c;
+    s->mainloop = c->mainloop;
+
+    s->direction = PA_STREAM_NODIRECTION;
+    s->state = PA_STREAM_UNCONNECTED;
+    s->flags = 0;
+
+    s->sample_spec = *ss;
+    s->channel_map = *map;
+
+    s->direct_on_input = PA_INVALID_INDEX;
+
+    s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
+    if (name)
+        pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
+
+    s->channel = 0;
+    s->channel_valid = FALSE;
+    s->syncid = c->csyncid++;
+    s->stream_index = PA_INVALID_INDEX;
+
+    s->requested_bytes = 0;
+    memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
+
+    s->device_index = PA_INVALID_INDEX;
+    s->device_name = NULL;
+    s->suspended = FALSE;
+
+    pa_memchunk_reset(&s->peek_memchunk);
+    s->peek_data = NULL;
+
+    s->record_memblockq = NULL;
+
+    s->corked = FALSE;
+
+    memset(&s->timing_info, 0, sizeof(s->timing_info));
+    s->timing_info_valid = FALSE;
+
+    s->previous_time = 0;
+
+    s->read_index_not_before = 0;
+    s->write_index_not_before = 0;
+    for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++)
+        s->write_index_corrections[i].valid = 0;
+    s->current_write_index_correction = 0;
+
+    s->auto_timing_update_event = NULL;
+    s->auto_timing_update_requested = FALSE;
+
+    reset_callbacks(s);
+
+    s->smoother = NULL;
+
+    /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
+    PA_LLIST_PREPEND(pa_stream, c->streams, s);
+    pa_stream_ref(s);
+
+    return s;
+}
+
+static void stream_unlink(pa_stream *s) {
+    pa_operation *o, *n;
+    pa_assert(s);
+
+    if (!s->context)
+        return;
+
+    /* Detach from context */
+
+    /* Unref all operatio object that point to us */
+    for (o = s->context->operations; o; o = n) {
+        n = o->next;
+
+        if (o->stream == s)
+            pa_operation_cancel(o);
+    }
+
+    /* Drop all outstanding replies for this stream */
+    if (s->context->pdispatch)
+        pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+
+    if (s->channel_valid) {
+        pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+        s->channel = 0;
+        s->channel_valid = FALSE;
+    }
+
+    PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
+    pa_stream_unref(s);
+
+    s->context = NULL;
+
+    if (s->auto_timing_update_event) {
+        pa_assert(s->mainloop);
+        s->mainloop->time_free(s->auto_timing_update_event);
+    }
+
+    reset_callbacks(s);
+}
+
+static void stream_free(pa_stream *s) {
+    pa_assert(s);
+
+    stream_unlink(s);
+
+    if (s->peek_memchunk.memblock) {
+        if (s->peek_data)
+            pa_memblock_release(s->peek_memchunk.memblock);
+        pa_memblock_unref(s->peek_memchunk.memblock);
+    }
+
+    if (s->record_memblockq)
+        pa_memblockq_free(s->record_memblockq);
+
+    if (s->proplist)
+        pa_proplist_free(s->proplist);
+
+    if (s->smoother)
+        pa_smoother_free(s->smoother);
+
+    pa_xfree(s->device_name);
+    pa_xfree(s);
+}
+
+void pa_stream_unref(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (PA_REFCNT_DEC(s) <= 0)
+        stream_free(s);
+}
+
+pa_stream* pa_stream_ref(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_REFCNT_INC(s);
+    return s;
+}
+
+pa_stream_state_t pa_stream_get_state(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    return s->state;
+}
+
+pa_context* pa_stream_get_context(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    return s->context;
+}
+
+uint32_t pa_stream_get_index(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
+    return s->stream_index;
+}
+
+void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == st)
+        return;
+
+    pa_stream_ref(s);
+
+    s->state = st;
+
+    if (s->state_callback)
+        s->state_callback(s, s->state_userdata);
+
+    if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED))
+        stream_unlink(s);
+
+    pa_stream_unref(s);
+}
+
+static void request_auto_timing_update(pa_stream *s, pa_bool_t force) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
+        return;
+
+    if (s->state == PA_STREAM_READY &&
+        (force || !s->auto_timing_update_requested)) {
+        pa_operation *o;
+
+/*         pa_log("automatically requesting new timing data"); */
+
+        if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
+            pa_operation_unref(o);
+            s->auto_timing_update_requested = TRUE;
+        }
+    }
+
+    if (s->auto_timing_update_event) {
+        struct timeval next;
+        pa_gettimeofday(&next);
+        pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC);
+        s->mainloop->time_restart(s->auto_timing_update_event, &next);
+    }
+}
+
+void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    pa_context_set_error(c, PA_ERR_KILLED);
+    pa_stream_set_state(s, PA_STREAM_FAILED);
+
+finish:
+    pa_context_unref(c);
+}
+
+void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+    const char *dn;
+    pa_bool_t suspended;
+    uint32_t di;
+    pa_usec_t usec;
+    uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 12) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &di) < 0 ||
+        pa_tagstruct_gets(t, &dn) < 0 ||
+        pa_tagstruct_get_boolean(t, &suspended) < 0) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (c->version >= 13) {
+
+        if (command == PA_COMMAND_RECORD_STREAM_MOVED) {
+            if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &fragsize) < 0 ||
+                pa_tagstruct_get_usec(t, &usec) < 0) {
+                pa_context_fail(c, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        } else {
+            if (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_get_usec(t, &usec) < 0) {
+                pa_context_fail(c, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!dn || di == PA_INVALID_INDEX) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, channel)))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    if (c->version >= 13) {
+        if (s->direction == PA_STREAM_RECORD)
+            s->timing_info.configured_source_usec = usec;
+        else
+            s->timing_info.configured_sink_usec = usec;
+
+        s->buffer_attr.maxlength = maxlength;
+        s->buffer_attr.fragsize = fragsize;
+        s->buffer_attr.tlength = tlength;
+        s->buffer_attr.prebuf = prebuf;
+        s->buffer_attr.minreq = minreq;
+    }
+
+    pa_xfree(s->device_name);
+    s->device_name = pa_xstrdup(dn);
+    s->device_index = di;
+
+    s->suspended = suspended;
+
+    request_auto_timing_update(s, TRUE);
+
+    if (s->moved_callback)
+        s->moved_callback(s, s->moved_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+    pa_bool_t suspended;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED || command == PA_COMMAND_RECORD_STREAM_SUSPENDED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 12) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_get_boolean(t, &suspended) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, channel)))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    s->suspended = suspended;
+
+    if (s->smoother) {
+        pa_usec_t x = pa_rtclock_usec();
+
+        if (s->timing_info_valid)
+            x -= s->timing_info.transport_usec;
+
+        if (s->suspended || s->corked)
+            pa_smoother_pause(s->smoother, x);
+        else
+            pa_smoother_resume(s->smoother, x);
+    }
+
+    request_auto_timing_update(s, TRUE);
+
+    if (s->suspended_callback)
+        s->suspended_callback(s, s->suspended_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_STARTED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 13) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    request_auto_timing_update(s, TRUE);
+
+    if (s->started_callback)
+        s->started_callback(s, s->suspended_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_stream *s;
+    pa_context *c = userdata;
+    uint32_t bytes, channel;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_REQUEST);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &bytes) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    s->requested_bytes += bytes;
+
+    if (s->requested_bytes > 0 && s->write_callback)
+        s->write_callback(s, s->requested_bytes, s->write_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_stream *s;
+    pa_context *c = userdata;
+    uint32_t channel;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    if (s->smoother)
+        if (s->direction == PA_STREAM_PLAYBACK && s->buffer_attr.prebuf > 0) {
+            pa_usec_t x = pa_rtclock_usec();
+
+            if (s->timing_info_valid)
+                x -= s->timing_info.transport_usec;
+
+            pa_smoother_pause(s->smoother, x);
+        }
+
+    request_auto_timing_update(s, TRUE);
+
+    if (s->state == PA_STREAM_READY) {
+
+        if (command == PA_COMMAND_OVERFLOW) {
+            if (s->overflow_callback)
+                s->overflow_callback(s, s->overflow_userdata);
+        } else if (command == PA_COMMAND_UNDERFLOW) {
+            if (s->underflow_callback)
+                s->underflow_callback(s, s->underflow_userdata);
+        }
+    }
+
+ finish:
+    pa_context_unref(c);
+}
+
+static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+/*     pa_log("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag); */
+
+    if (s->state != PA_STREAM_READY)
+        return;
+
+    if (w) {
+        s->write_index_not_before = s->context->ctag;
+
+        if (s->timing_info_valid)
+            s->timing_info.write_index_corrupt = TRUE;
+
+/*         pa_log("write_index invalidated"); */
+    }
+
+    if (r) {
+        s->read_index_not_before = s->context->ctag;
+
+        if (s->timing_info_valid)
+            s->timing_info.read_index_corrupt = TRUE;
+
+/*         pa_log("read_index invalidated"); */
+    }
+
+    request_auto_timing_update(s, TRUE);
+}
+
+static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+    pa_stream *s = userdata;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    pa_stream_ref(s);
+    request_auto_timing_update(s, FALSE);
+    pa_stream_unref(s);
+}
+
+static void create_stream_complete(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(s->state == PA_STREAM_CREATING);
+
+    pa_stream_set_state(s, PA_STREAM_READY);
+
+    if (s->requested_bytes > 0 && s->write_callback)
+        s->write_callback(s, s->requested_bytes, s->write_userdata);
+
+    if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
+        struct timeval tv;
+        pa_gettimeofday(&tv);
+        tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */
+        pa_assert(!s->auto_timing_update_event);
+        s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s);
+
+        request_auto_timing_update(s, TRUE);
+    }
+}
+
+static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_sample_spec *ss) {
+    pa_assert(s);
+    pa_assert(attr);
+    pa_assert(ss);
+
+    if (s->context->version >= 13)
+        return;
+
+    /* Version older than 0.9.10 didn't do server side buffer_attr
+     * selection, hence we have to fake it on the client side */
+
+    if (!attr->maxlength <= 0)
+        attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */
+
+    if (!attr->tlength <= 0)
+        attr->tlength = pa_bytes_per_second(ss)*2; /* 2s of buffering */
+
+    if (!attr->minreq <= 0)
+        attr->minreq = (2*attr->tlength)/10; /* Ask for more data when there are only 200ms left in the playback buffer */
+
+    if (!attr->prebuf)
+        attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */
+
+    if (!attr->fragsize)
+        attr->fragsize  = attr->tlength; /* Pass data to the app only when the buffer is filled up once */
+}
+
+void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_stream *s = userdata;
+
+    pa_assert(pd);
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(s->state == PA_STREAM_CREATING);
+
+    pa_stream_ref(s);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
+            goto finish;
+
+        pa_stream_set_state(s, PA_STREAM_FAILED);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
+        s->channel == PA_INVALID_INDEX ||
+        ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 ||  s->stream_index == PA_INVALID_INDEX)) ||
+        ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) {
+        pa_context_fail(s->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (s->context->version >= 9) {
+        if (s->direction == PA_STREAM_PLAYBACK) {
+            if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &s->buffer_attr.tlength) < 0 ||
+                pa_tagstruct_getu32(t, &s->buffer_attr.prebuf) < 0 ||
+                pa_tagstruct_getu32(t, &s->buffer_attr.minreq) < 0) {
+                pa_context_fail(s->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        } else if (s->direction == PA_STREAM_RECORD) {
+            if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &s->buffer_attr.fragsize) < 0) {
+                pa_context_fail(s->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        }
+    }
+
+    if (s->context->version >= 12 && s->direction != PA_STREAM_UPLOAD) {
+        pa_sample_spec ss;
+        pa_channel_map cm;
+        const char *dn = NULL;
+        pa_bool_t suspended;
+
+        if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+            pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+            pa_tagstruct_getu32(t, &s->device_index) < 0 ||
+            pa_tagstruct_gets(t, &dn) < 0 ||
+            pa_tagstruct_get_boolean(t, &suspended) < 0) {
+            pa_context_fail(s->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        if (!dn || s->device_index == PA_INVALID_INDEX ||
+            ss.channels != cm.channels ||
+            !pa_channel_map_valid(&cm) ||
+            !pa_sample_spec_valid(&ss) ||
+            (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
+            (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
+            (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) {
+            pa_context_fail(s->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        pa_xfree(s->device_name);
+        s->device_name = pa_xstrdup(dn);
+        s->suspended = suspended;
+
+        s->channel_map = cm;
+        s->sample_spec = ss;
+    }
+
+    if (s->context->version >= 13 && s->direction != PA_STREAM_UPLOAD) {
+        pa_usec_t usec;
+
+        if (pa_tagstruct_get_usec(t, &usec) < 0) {
+            pa_context_fail(s->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        if (s->direction == PA_STREAM_RECORD)
+            s->timing_info.configured_source_usec = usec;
+        else
+            s->timing_info.configured_sink_usec = usec;
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(s->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (s->direction == PA_STREAM_RECORD) {
+        pa_assert(!s->record_memblockq);
+
+        s->record_memblockq = pa_memblockq_new(
+                0,
+                s->buffer_attr.maxlength,
+                0,
+                pa_frame_size(&s->sample_spec),
+                1,
+                0,
+                0,
+                NULL);
+    }
+
+    s->channel_valid = TRUE;
+    pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
+
+    create_stream_complete(s);
+
+finish:
+    pa_stream_unref(s);
+}
+
+static int create_stream(
+        pa_stream_direction_t direction,
+        pa_stream *s,
+        const char *dev,
+        const pa_buffer_attr *attr,
+        pa_stream_flags_t flags,
+        const pa_cvolume *volume,
+        pa_stream *sync_stream) {
+
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(direction == PA_STREAM_PLAYBACK || direction == PA_STREAM_RECORD);
+
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direct_on_input == PA_INVALID_INDEX || direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED|
+                                              PA_STREAM_INTERPOLATE_TIMING|
+                                              PA_STREAM_NOT_MONOTONOUS|
+                                              PA_STREAM_AUTO_TIMING_UPDATE|
+                                              PA_STREAM_NO_REMAP_CHANNELS|
+                                              PA_STREAM_NO_REMIX_CHANNELS|
+                                              PA_STREAM_FIX_FORMAT|
+                                              PA_STREAM_FIX_RATE|
+                                              PA_STREAM_FIX_CHANNELS|
+                                              PA_STREAM_DONT_MOVE|
+                                              PA_STREAM_VARIABLE_RATE|
+                                              PA_STREAM_PEAK_DETECT|
+                                              PA_STREAM_START_MUTED|
+                                              PA_STREAM_ADJUST_LATENCY)), PA_ERR_INVALID);
+
+    PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
+    /* Althought some of the other flags are not supported on older
+     * version, we don't check for them here, because it doesn't hurt
+     * when they are passed but actually not supported. This makes
+     * client development easier */
+
+    PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_PLAYBACK || !(flags & (PA_STREAM_START_MUTED)), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
+
+    pa_stream_ref(s);
+
+    s->direction = direction;
+    s->flags = flags;
+
+    if (sync_stream)
+        s->syncid = sync_stream->syncid;
+
+    if (attr)
+        s->buffer_attr = *attr;
+    automatic_buffer_attr(s, &s->buffer_attr, &s->sample_spec);
+
+    if (flags & PA_STREAM_INTERPOLATE_TIMING) {
+        pa_usec_t x;
+
+        if (s->smoother)
+            pa_smoother_free(s->smoother);
+
+        s->smoother = pa_smoother_new(SMOOTHER_ADJUST_TIME, SMOOTHER_HISTORY_TIME, !(flags & PA_STREAM_NOT_MONOTONOUS), SMOOTHER_MIN_HISTORY);
+
+        x = pa_rtclock_usec();
+        pa_smoother_set_time_offset(s->smoother, x);
+        pa_smoother_pause(s->smoother, x);
+    }
+
+    if (!dev)
+        dev = s->direction == PA_STREAM_PLAYBACK ? s->context->conf->default_sink : s->context->conf->default_source;
+
+    t = pa_tagstruct_command(
+            s->context,
+            s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM,
+            &tag);
+
+    if (s->context->version < 13)
+        pa_tagstruct_puts(t, pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME));
+
+    pa_tagstruct_put(
+            t,
+            PA_TAG_SAMPLE_SPEC, &s->sample_spec,
+            PA_TAG_CHANNEL_MAP, &s->channel_map,
+            PA_TAG_U32, PA_INVALID_INDEX,
+            PA_TAG_STRING, dev,
+            PA_TAG_U32, s->buffer_attr.maxlength,
+            PA_TAG_BOOLEAN, !!(flags & PA_STREAM_START_CORKED),
+            PA_TAG_INVALID);
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+        pa_cvolume cv;
+
+        pa_tagstruct_put(
+                t,
+                PA_TAG_U32, s->buffer_attr.tlength,
+                PA_TAG_U32, s->buffer_attr.prebuf,
+                PA_TAG_U32, s->buffer_attr.minreq,
+                PA_TAG_U32, s->syncid,
+                PA_TAG_INVALID);
+
+        if (!volume)
+            volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
+
+        pa_tagstruct_put_cvolume(t, volume);
+    } else
+        pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
+
+    if (s->context->version >= 12) {
+        pa_tagstruct_put(
+                t,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMAP_CHANNELS,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMIX_CHANNELS,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_FORMAT,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_RATE,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_CHANNELS,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_DONT_MOVE,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_VARIABLE_RATE,
+                PA_TAG_INVALID);
+    }
+
+    if (s->context->version >= 13) {
+
+        if (s->direction == PA_STREAM_PLAYBACK)
+            pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED);
+        else
+            pa_tagstruct_put_boolean(t, flags & PA_STREAM_PEAK_DETECT);
+
+        pa_tagstruct_put(
+                t,
+                PA_TAG_BOOLEAN, flags & PA_STREAM_ADJUST_LATENCY,
+                PA_TAG_PROPLIST, s->proplist,
+                PA_TAG_INVALID);
+
+        if (s->direction == PA_STREAM_RECORD)
+            pa_tagstruct_putu32(t, s->direct_on_input);
+    }
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
+
+    pa_stream_set_state(s, PA_STREAM_CREATING);
+
+    pa_stream_unref(s);
+    return 0;
+}
+
+int pa_stream_connect_playback(
+        pa_stream *s,
+        const char *dev,
+        const pa_buffer_attr *attr,
+        pa_stream_flags_t flags,
+        pa_cvolume *volume,
+        pa_stream *sync_stream) {
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream);
+}
+
+int pa_stream_connect_record(
+        pa_stream *s,
+        const char *dev,
+        const pa_buffer_attr *attr,
+        pa_stream_flags_t flags) {
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
+}
+
+int pa_stream_write(
+        pa_stream *s,
+        const void *data,
+        size_t length,
+        void (*free_cb)(void *p),
+        int64_t offset,
+        pa_seek_mode_t seek) {
+
+    pa_memchunk chunk;
+    pa_seek_mode_t t_seek;
+    int64_t t_offset;
+    size_t t_length;
+    const void *t_data;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(data);
+
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || (seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID);
+
+    if (length <= 0)
+        return 0;
+
+    t_seek = seek;
+    t_offset = offset;
+    t_length = length;
+    t_data = data;
+
+    while (t_length > 0) {
+
+        chunk.index = 0;
+
+        if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
+            chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1);
+            chunk.length = t_length;
+        } else {
+            void *d;
+
+            chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool));
+            chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
+
+            d = pa_memblock_acquire(chunk.memblock);
+            memcpy(d, t_data, chunk.length);
+            pa_memblock_release(chunk.memblock);
+        }
+
+        pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk);
+
+        t_offset = 0;
+        t_seek = PA_SEEK_RELATIVE;
+
+        t_data = (const uint8_t*) t_data + chunk.length;
+        t_length -= chunk.length;
+
+        pa_memblock_unref(chunk.memblock);
+    }
+
+    if (free_cb && pa_pstream_get_shm(s->context->pstream))
+        free_cb((void*) data);
+
+    if (length < s->requested_bytes)
+        s->requested_bytes -= length;
+    else
+        s->requested_bytes = 0;
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+
+        /* Update latency request correction */
+        if (s->write_index_corrections[s->current_write_index_correction].valid) {
+
+            if (seek == PA_SEEK_ABSOLUTE) {
+                s->write_index_corrections[s->current_write_index_correction].corrupt = FALSE;
+                s->write_index_corrections[s->current_write_index_correction].absolute = TRUE;
+                s->write_index_corrections[s->current_write_index_correction].value = offset + length;
+            } else if (seek == PA_SEEK_RELATIVE) {
+                if (!s->write_index_corrections[s->current_write_index_correction].corrupt)
+                    s->write_index_corrections[s->current_write_index_correction].value += offset + length;
+            } else
+                s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
+        }
+
+        /* Update the write index in the already available latency data */
+        if (s->timing_info_valid) {
+
+            if (seek == PA_SEEK_ABSOLUTE) {
+                s->timing_info.write_index_corrupt = FALSE;
+                s->timing_info.write_index = offset + length;
+            } else if (seek == PA_SEEK_RELATIVE) {
+                if (!s->timing_info.write_index_corrupt)
+                    s->timing_info.write_index += offset + length;
+            } else
+                s->timing_info.write_index_corrupt = TRUE;
+        }
+
+        if (!s->timing_info_valid || s->timing_info.write_index_corrupt)
+            request_auto_timing_update(s, TRUE);
+    }
+
+    return 0;
+}
+
+int pa_stream_peek(pa_stream *s, const void **data, size_t *length) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(data);
+    pa_assert(length);
+
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
+
+    if (!s->peek_memchunk.memblock) {
+
+        if (pa_memblockq_peek(s->record_memblockq, &s->peek_memchunk) < 0) {
+            *data = NULL;
+            *length = 0;
+            return 0;
+        }
+
+        s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock);
+    }
+
+    pa_assert(s->peek_data);
+    *data = (uint8_t*) s->peek_data + s->peek_memchunk.index;
+    *length = s->peek_memchunk.length;
+    return 0;
+}
+
+int pa_stream_drop(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->peek_memchunk.memblock, PA_ERR_BADSTATE);
+
+    pa_memblockq_drop(s->record_memblockq, s->peek_memchunk.length);
+
+    /* Fix the simulated local read index */
+    if (s->timing_info_valid && !s->timing_info.read_index_corrupt)
+        s->timing_info.read_index += s->peek_memchunk.length;
+
+    pa_assert(s->peek_data);
+    pa_memblock_release(s->peek_memchunk.memblock);
+    pa_memblock_unref(s->peek_memchunk.memblock);
+    pa_memchunk_reset(&s->peek_memchunk);
+
+    return 0;
+}
+
+size_t pa_stream_writable_size(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
+
+    return s->requested_bytes;
+}
+
+size_t pa_stream_readable_size(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
+
+    return pa_memblockq_get_length(s->record_memblockq);
+}
+
+pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(s->context, PA_COMMAND_DRAIN_PLAYBACK_STREAM, &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+static pa_usec_t calc_time(pa_stream *s, pa_bool_t ignore_transport) {
+    pa_usec_t usec;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(s->state == PA_STREAM_READY);
+    pa_assert(s->direction != PA_STREAM_UPLOAD);
+    pa_assert(s->timing_info_valid);
+    pa_assert(s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt);
+    pa_assert(s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt);
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+        /* The last byte that was written into the output device
+         * had this time value associated */
+        usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
+
+        if (!s->corked && !s->suspended) {
+
+            if (!ignore_transport)
+                /* Because the latency info took a little time to come
+                 * to us, we assume that the real output time is actually
+                 * a little ahead */
+                usec += s->timing_info.transport_usec;
+
+            /* However, the output device usually maintains a buffer
+               too, hence the real sample currently played is a little
+               back  */
+            if (s->timing_info.sink_usec >= usec)
+                usec = 0;
+            else
+                usec -= s->timing_info.sink_usec;
+        }
+
+    } else if (s->direction == PA_STREAM_RECORD) {
+        /* The last byte written into the server side queue had
+         * this time value associated */
+        usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
+
+        if (!s->corked && !s->suspended) {
+
+            if (!ignore_transport)
+                /* Add transport latency */
+                usec += s->timing_info.transport_usec;
+
+            /* Add latency of data in device buffer */
+            usec += s->timing_info.source_usec;
+
+            /* If this is a monitor source, we need to correct the
+             * time by the playback device buffer */
+            if (s->timing_info.sink_usec >= usec)
+                usec = 0;
+            else
+                usec -= s->timing_info.sink_usec;
+        }
+    }
+
+    return usec;
+}
+
+static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    struct timeval local, remote, now;
+    pa_timing_info *i;
+    pa_bool_t playing = FALSE;
+    uint64_t underrun_for = 0, playing_for = 0;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context || !o->stream)
+        goto finish;
+
+    i = &o->stream->timing_info;
+
+/*     pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */
+
+    o->stream->timing_info_valid = FALSE;
+    i->write_index_corrupt = FALSE;
+    i->read_index_corrupt = FALSE;
+
+/*     pa_log("timing update %u\n", tag); */
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+    } else {
+
+        if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
+            pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
+            pa_tagstruct_get_boolean(t, &playing) < 0 ||
+            pa_tagstruct_get_timeval(t, &local) < 0 ||
+            pa_tagstruct_get_timeval(t, &remote) < 0 ||
+            pa_tagstruct_gets64(t, &i->write_index) < 0 ||
+            pa_tagstruct_gets64(t, &i->read_index) < 0) {
+
+            pa_context_fail(o->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        if (o->context->version >= 13 &&
+            o->stream->direction == PA_STREAM_PLAYBACK)
+            if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+                pa_tagstruct_getu64(t, &playing_for) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+
+        if (!pa_tagstruct_eof(t)) {
+            pa_context_fail(o->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        o->stream->timing_info_valid = TRUE;
+        i->playing = (int) playing;
+        i->since_underrun = playing ? playing_for : underrun_for;
+
+        pa_gettimeofday(&now);
+
+        /* Calculcate timestamps */
+        if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) {
+            /* local and remote seem to have synchronized clocks */
+
+            if (o->stream->direction == PA_STREAM_PLAYBACK)
+                i->transport_usec = pa_timeval_diff(&remote, &local);
+            else
+                i->transport_usec = pa_timeval_diff(&now, &remote);
+
+            i->synchronized_clocks = TRUE;
+            i->timestamp = remote;
+        } else {
+            /* clocks are not synchronized, let's estimate latency then */
+            i->transport_usec = pa_timeval_diff(&now, &local)/2;
+            i->synchronized_clocks = FALSE;
+            i->timestamp = local;
+            pa_timeval_add(&i->timestamp, i->transport_usec);
+        }
+
+        /* Invalidate read and write indexes if necessary */
+        if (tag < o->stream->read_index_not_before)
+            i->read_index_corrupt = TRUE;
+
+        if (tag < o->stream->write_index_not_before)
+            i->write_index_corrupt = TRUE;
+
+        if (o->stream->direction == PA_STREAM_PLAYBACK) {
+            /* Write index correction */
+
+            int n, j;
+            uint32_t ctag = tag;
+
+            /* Go through the saved correction values and add up the total correction.*/
+
+            for (n = 0, j = o->stream->current_write_index_correction+1;
+                 n < PA_MAX_WRITE_INDEX_CORRECTIONS;
+                 n++, j = (j + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS) {
+
+                /* Step over invalid data or out-of-date data */
+                if (!o->stream->write_index_corrections[j].valid ||
+                    o->stream->write_index_corrections[j].tag < ctag)
+                    continue;
+
+                /* Make sure that everything is in order */
+                ctag = o->stream->write_index_corrections[j].tag+1;
+
+                /* Now fix the write index */
+                if (o->stream->write_index_corrections[j].corrupt) {
+                    /* A corrupting seek was made */
+                    i->write_index = 0;
+                    i->write_index_corrupt = TRUE;
+                } else if (o->stream->write_index_corrections[j].absolute) {
+                    /* An absolute seek was made */
+                    i->write_index = o->stream->write_index_corrections[j].value;
+                    i->write_index_corrupt = FALSE;
+                } else if (!i->write_index_corrupt) {
+                    /* A relative seek was made */
+                    i->write_index += o->stream->write_index_corrections[j].value;
+                }
+            }
+        }
+
+        if (o->stream->direction == PA_STREAM_RECORD) {
+            /* Read index correction */
+
+            if (!i->read_index_corrupt)
+                i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq);
+        }
+
+/*     pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); */
+
+        /* Clear old correction entries */
+        if (o->stream->direction == PA_STREAM_PLAYBACK) {
+            int n;
+
+            for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
+                if (!o->stream->write_index_corrections[n].valid)
+                    continue;
+
+                if (o->stream->write_index_corrections[n].tag <= tag)
+                    o->stream->write_index_corrections[n].valid = FALSE;
+            }
+        }
+
+        /* Update smoother */
+        if (o->stream->smoother) {
+            pa_usec_t u, x;
+
+            u = x = pa_rtclock_usec() - i->transport_usec;
+
+            if (o->stream->direction == PA_STREAM_PLAYBACK &&
+                o->context->version >= 13) {
+                pa_usec_t su;
+
+                /* If we weren't playing then it will take some time
+                 * until the audio will actually come out through the
+                 * speakers. Since we follow that timing here, we need
+                 * to try to fix this up */
+
+                su = pa_bytes_to_usec(i->since_underrun, &o->stream->sample_spec);
+
+                if (su < i->sink_usec)
+                    x += i->sink_usec - su;
+            }
+
+            if (!i->playing)
+                pa_smoother_pause(o->stream->smoother, x);
+
+            /* Update the smoother */
+            if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) ||
+                (o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt))
+                pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, TRUE));
+
+            if (i->playing)
+                pa_smoother_resume(o->stream->smoother, x);
+        }
+    }
+
+    o->stream->auto_timing_update_requested = FALSE;
+
+    if (o->stream->latency_update_callback)
+        o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
+
+    if (o->callback && o->stream && o->stream->state == PA_STREAM_READY) {
+        pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+        cb(o->stream, o->stream->timing_info_valid, o->userdata);
+    }
+
+finish:
+
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+    struct timeval now;
+    int cidx = 0;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+        /* Find a place to store the write_index correction data for this entry */
+        cidx = (s->current_write_index_correction + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS;
+
+        /* Check if we could allocate a correction slot. If not, there are too many outstanding queries */
+        PA_CHECK_VALIDITY_RETURN_NULL(s->context, !s->write_index_corrections[cidx].valid, PA_ERR_INTERNAL);
+    }
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(
+            s->context,
+            s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY,
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_timing_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+        /* Fill in initial correction data */
+
+        s->current_write_index_correction = cidx;
+
+        s->write_index_corrections[cidx].valid = TRUE;
+        s->write_index_corrections[cidx].absolute = FALSE;
+        s->write_index_corrections[cidx].corrupt = FALSE;
+        s->write_index_corrections[cidx].tag = tag;
+        s->write_index_corrections[cidx].value = 0;
+    }
+
+    return o;
+}
+
+void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_stream *s = userdata;
+
+    pa_assert(pd);
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    pa_stream_ref(s);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
+            goto finish;
+
+        pa_stream_set_state(s, PA_STREAM_FAILED);
+        goto finish;
+    } else if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(s->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    pa_stream_set_state(s, PA_STREAM_TERMINATED);
+
+finish:
+    pa_stream_unref(s);
+}
+
+int pa_stream_disconnect(pa_stream *s) {
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    pa_stream_ref(s);
+
+    t = pa_tagstruct_command(
+            s->context,
+            s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM :
+            (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM),
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s, NULL);
+
+    pa_stream_unref(s);
+    return 0;
+}
+
+void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->read_callback = cb;
+    s->read_userdata = userdata;
+}
+
+void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->write_callback = cb;
+    s->write_userdata = userdata;
+}
+
+void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->state_callback = cb;
+    s->state_userdata = userdata;
+}
+
+void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->overflow_callback = cb;
+    s->overflow_userdata = userdata;
+}
+
+void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->underflow_callback = cb;
+    s->underflow_userdata = userdata;
+}
+
+void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->latency_update_callback = cb;
+    s->latency_update_userdata = userdata;
+}
+
+void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->moved_callback = cb;
+    s->moved_userdata = userdata;
+}
+
+void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->suspended_callback = cb;
+    s->suspended_userdata = userdata;
+}
+
+void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->started_callback = cb;
+    s->started_userdata = userdata;
+}
+
+void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int success = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        success = 0;
+    } else if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+        cb(o->stream, success, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    s->corked = b;
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(
+            s->context,
+            s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM,
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_tagstruct_put_boolean(t, !!b);
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    if (s->smoother) {
+        pa_usec_t x = pa_rtclock_usec();
+
+        if (s->timing_info_valid)
+            x += s->timing_info.transport_usec;
+
+        if (s->suspended || s->corked)
+            pa_smoother_pause(s->smoother, x);
+    }
+
+    if (s->direction == PA_STREAM_PLAYBACK)
+        invalidate_indexes(s, TRUE, FALSE);
+
+    return o;
+}
+
+static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, pa_stream_success_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(s->context, command, &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) {
+
+        if (s->direction == PA_STREAM_PLAYBACK) {
+            if (s->write_index_corrections[s->current_write_index_correction].valid)
+                s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
+
+            if (s->timing_info_valid)
+                s->timing_info.write_index_corrupt = TRUE;
+
+            if (s->buffer_attr.prebuf > 0)
+                invalidate_indexes(s, TRUE, FALSE);
+            else
+                request_auto_timing_update(s, TRUE);
+
+            if (s->smoother && s->buffer_attr.prebuf > 0) {
+                pa_usec_t x = pa_rtclock_usec();
+
+                if (s->timing_info_valid)
+                    x += s->timing_info.transport_usec;
+
+                pa_smoother_pause(s->smoother, x);
+            }
+
+        } else
+            invalidate_indexes(s, FALSE, TRUE);
+    }
+
+    return o;
+}
+
+pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
+
+    if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata)))
+        invalidate_indexes(s, TRUE, FALSE);
+
+    return o;
+}
+
+pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
+
+    if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata)))
+        invalidate_indexes(s, TRUE, FALSE);
+
+    return o;
+}
+
+pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(name);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    if (s->context->version >= 13) {
+        pa_proplist *p = pa_proplist_new();
+
+        pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+        o = pa_stream_proplist_update(s, PA_UPDATE_REPLACE, p, cb, userdata);
+        pa_proplist_free(p);
+    } else {
+        pa_tagstruct *t;
+        uint32_t tag;
+
+        o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+        t = pa_tagstruct_command(
+                s->context,
+                s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
+                &tag);
+        pa_tagstruct_putu32(t, s->channel);
+        pa_tagstruct_puts(t, name);
+        pa_pstream_send_tagstruct(s->context->pstream, t);
+        pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+    }
+
+    return o;
+}
+
+int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
+    pa_usec_t usec;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
+
+    if (s->smoother)
+        usec = pa_smoother_get(s->smoother, pa_rtclock_usec());
+    else
+        usec = calc_time(s, FALSE);
+
+    /* Make sure the time runs monotonically */
+    if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) {
+        if (usec < s->previous_time)
+            usec = s->previous_time;
+        else
+            s->previous_time = usec;
+    }
+
+    if (r_usec)
+        *r_usec = usec;
+
+    return 0;
+}
+
+static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (negative)
+        *negative = 0;
+
+    if (a >= b)
+        return a-b;
+    else {
+        if (negative && s->direction == PA_STREAM_RECORD) {
+            *negative = 1;
+            return b-a;
+        } else
+            return 0;
+    }
+}
+
+int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
+    pa_usec_t t, c;
+    int r;
+    int64_t cindex;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(r_usec);
+
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
+
+    if ((r = pa_stream_get_time(s, &t)) < 0)
+        return r;
+
+    if (s->direction == PA_STREAM_PLAYBACK)
+        cindex = s->timing_info.write_index;
+    else
+        cindex = s->timing_info.read_index;
+
+    if (cindex < 0)
+        cindex = 0;
+
+    c = pa_bytes_to_usec(cindex, &s->sample_spec);
+
+    if (s->direction == PA_STREAM_PLAYBACK)
+        *r_usec = time_counter_diff(s, c, t, negative);
+    else
+        *r_usec = time_counter_diff(s, t, c, negative);
+
+    return 0;
+}
+
+const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_BADSTATE);
+
+    return &s->timing_info;
+}
+
+const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    return &s->sample_spec;
+}
+
+const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    return &s->channel_map;
+}
+
+const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NOTSUPPORTED);
+
+    return &s->buffer_attr;
+}
+
+static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int success = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        success = 0;
+    } else {
+
+        if (o->stream->direction == PA_STREAM_PLAYBACK) {
+            if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &o->stream->buffer_attr.tlength) < 0 ||
+                pa_tagstruct_getu32(t, &o->stream->buffer_attr.prebuf) < 0 ||
+                pa_tagstruct_getu32(t, &o->stream->buffer_attr.minreq) < 0) {
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        } else if (o->stream->direction == PA_STREAM_RECORD) {
+            if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
+                pa_tagstruct_getu32(t, &o->stream->buffer_attr.fragsize) < 0) {
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        }
+
+        if (!pa_tagstruct_eof(t)) {
+            pa_context_fail(o->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    }
+
+    if (o->callback) {
+        pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+        cb(o->stream, success, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+
+pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(attr);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(
+            s->context,
+            s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR : PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+
+    pa_tagstruct_putu32(t, attr->maxlength);
+
+    if (s->direction == PA_STREAM_PLAYBACK)
+        pa_tagstruct_put(
+                t,
+                PA_TAG_U32, attr->tlength,
+                PA_TAG_U32, attr->prebuf,
+                PA_TAG_U32, attr->minreq,
+                PA_TAG_INVALID);
+    else
+        pa_tagstruct_putu32(t, attr->fragsize);
+
+    if (s->context->version >= 13)
+        pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_ADJUST_LATENCY));
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+uint32_t pa_stream_get_device_index(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->device_index != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
+    return s->device_index;
+}
+
+const char *pa_stream_get_device_name(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE);
+
+    return s->device_name;
+}
+
+int pa_stream_is_suspended(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+    return s->suspended;
+}
+
+int pa_stream_is_corked(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    return s->corked;
+}
+
+static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int success = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        success = 0;
+    } else {
+
+        if (!pa_tagstruct_eof(t)) {
+            pa_context_fail(o->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    }
+
+    o->stream->sample_spec.rate = PA_PTR_TO_UINT(o->private);
+    pa_assert(pa_sample_spec_valid(&o->stream->sample_spec));
+
+    if (o->callback) {
+        pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+        cb(o->stream, success, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+
+pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, rate > 0 && rate <= PA_RATE_MAX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+    o->private = PA_UINT_TO_PTR(rate);
+
+    t = pa_tagstruct_command(
+            s->context,
+            s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE : PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_tagstruct_putu32(t, rate);
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_update_sample_rate_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(
+            s->context,
+            s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST : PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+    pa_tagstruct_putu32(t, (uint32_t) mode);
+    pa_tagstruct_put_proplist(t, p);
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    /* Please note that we don't update s->proplist here, because we
+     * don't export that field */
+
+    return o;
+}
+
+pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+    const char * const*k;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, keys && keys[0], PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(
+            s->context,
+            s->direction == PA_STREAM_RECORD ? PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST : PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
+            &tag);
+    pa_tagstruct_putu32(t, s->channel);
+
+    for (k = keys; *k; k++)
+        pa_tagstruct_puts(t, *k);
+
+    pa_tagstruct_puts(t, NULL);
+
+    pa_pstream_send_tagstruct(s->context->pstream, t);
+    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    /* Please note that we don't update s->proplist here, because we
+     * don't export that field */
+
+    return o;
+}
+
+int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+    s->direct_on_input = sink_input_idx;
+
+    return 0;
+}
+
+uint32_t pa_stream_get_monitor_stream(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direct_on_input != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+
+    return s->direct_on_input;
+}
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
new file mode 100644 (file)
index 0000000..55f36b7
--- /dev/null
@@ -0,0 +1,559 @@
+#ifndef foostreamhfoo
+#define foostreamhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+#include <pulse/def.h>
+#include <pulse/cdecl.h>
+#include <pulse/operation.h>
+
+/** \page streams Audio Streams
+ *
+ * \section overv_sec Overview
+ *
+ * Audio streams form the central functionality of the sound server. Data is
+ * routed, converted and mixed from several sources before it is passed along
+ * to a final output. Currently, there are three forms of audio streams:
+ *
+ * \li Playback streams - Data flows from the client to the server.
+ * \li Record streams - Data flows from the server to the client.
+ * \li Upload streams - Similar to playback streams, but the data is stored in
+ *                      the sample cache. See \ref scache for more information
+ *                      about controlling the sample cache.
+ *
+ * \section create_sec Creating
+ *
+ * To access a stream, a pa_stream object must be created using
+ * pa_stream_new(). At this point the audio sample format and mapping of
+ * channels must be specified. See \ref sample and \ref channelmap for more
+ * information about those structures.
+ *
+ * This first step will only create a client-side object, representing the
+ * stream. To use the stream, a server-side object must be created and
+ * associated with the local object. Depending on which type of stream is
+ * desired, a different function is needed:
+ *
+ * \li Playback stream - pa_stream_connect_playback()
+ * \li Record stream - pa_stream_connect_record()
+ * \li Upload stream - pa_stream_connect_upload() (see \ref scache)
+ *
+ * Similar to how connections are done in contexts, connecting a stream will
+ * not generate a pa_operation object. Also like contexts, the application
+ * should register a state change callback, using
+ * pa_stream_set_state_callback(), and wait for the stream to enter an active
+ * state.
+ *
+ * \subsection bufattr_subsec Buffer Attributes
+ *
+ * Playback and record streams always have a server side buffer as
+ * part of the data flow.  The size of this buffer strikes a
+ * compromise between low latency and sensitivity for buffer
+ * overflows/underruns.
+ *
+ * The buffer metrics may be controlled by the application. They are
+ * described with a pa_buffer_attr structure which contains a number
+ * of fields:
+ *
+ * \li maxlength - The absolute maximum number of bytes that can be stored in
+ *                 the buffer. If this value is exceeded then data will be
+ *                 lost.
+ * \li tlength - The target length of a playback buffer. The server will only
+ *               send requests for more data as long as the buffer has less
+ *               than this number of bytes of data.
+ * \li prebuf - Number of bytes that need to be in the buffer before
+ * playback will commence. Start of playback can be forced using
+ * pa_stream_trigger() even though the prebuffer size hasn't been
+ * reached. If a buffer underrun occurs, this prebuffering will be
+ * again enabled. If the playback shall never stop in case of a buffer
+ * underrun, this value should be set to 0. In that case the read
+ * index of the output buffer overtakes the write index, and hence the
+ * fill level of the buffer is negative.
+ * \li minreq - Minimum free number of the bytes in the playback buffer before
+ *              the server will request more data.
+ * \li fragsize - Maximum number of bytes that the server will push in one
+ *                chunk for record streams.
+ *
+ * The server side playback buffers are indexed by a write and a read
+ * index. The application writes to the write index and the sound
+ * device reads from the read index. The read index is increased
+ * monotonically, while the write index may be freely controlled by
+ * the application. Substracting the read index from the write index
+ * will give you the current fill level of the buffer. The read/write
+ * indexes are 64bit values and measured in bytes, they will never
+ * wrap. The current read/write index may be queried using
+ * pa_stream_get_timing_info() (see below for more information). In
+ * case of a buffer underrun the read index is equal or larger than
+ * the write index. Unless the prebuf value is 0, PulseAudio will
+ * temporarily pause playback in such a case, and wait until the
+ * buffer is filled up to prebuf bytes again. If prebuf is 0, the
+ * read index may be larger than the write index, in which case
+ * silence is played. If the application writes data to indexes lower
+ * than the read index, the data is immediately lost.
+ *
+ * \section transfer_sec Transferring Data
+ *
+ * Once the stream is up, data can start flowing between the client and the
+ * server. Two different access models can be used to transfer the data:
+ *
+ * \li Asynchronous - The application register a callback using
+ *                    pa_stream_set_write_callback() and
+ *                    pa_stream_set_read_callback() to receive notifications
+ *                    that data can either be written or read.
+ * \li Polled - Query the library for available data/space using
+ *              pa_stream_writable_size() and pa_stream_readable_size() and
+ *              transfer data as needed. The sizes are stored locally, in the
+ *              client end, so there is no delay when reading them.
+ *
+ * It is also possible to mix the two models freely.
+ *
+ * Once there is data/space available, it can be transferred using either
+ * pa_stream_write() for playback, or pa_stream_peek() / pa_stream_drop() for
+ * record. Make sure you do not overflow the playback buffers as data will be
+ * dropped.
+ *
+ * \section bufctl_sec Buffer Control
+ *
+ * The transfer buffers can be controlled through a number of operations:
+ *
+ * \li pa_stream_cork() - Start or stop the playback or recording.
+ * \li pa_stream_trigger() - Start playback immediatly and do not wait for
+ *                           the buffer to fill up to the set trigger level.
+ * \li pa_stream_prebuf() - Reenable the playback trigger level.
+ * \li pa_stream_drain() - Wait for the playback buffer to go empty. Will
+ *                         return a pa_operation object that will indicate when
+ *                         the buffer is completely drained.
+ * \li pa_stream_flush() - Drop all data from the playback buffer and do not
+ *                         wait for it to finish playing.
+ *
+ * \section seek_modes Seeking in the Playback Buffer
+ *
+ * A client application may freely seek in the playback buffer. To
+ * accomplish that the pa_stream_write() function takes a seek mode
+ * and an offset argument. The seek mode is one of:
+ *
+ * \li PA_SEEK_RELATIVE - seek relative to the current write index
+ * \li PA_SEEK_ABSOLUTE - seek relative to the beginning of the playback buffer, (i.e. the first that was ever played in the stream)
+ * \li PA_SEEK_RELATIVE_ON_READ - seek relative to the current read index. Use this to write data to the output buffer that should be played as soon as possible
+ * \li PA_SEEK_RELATIVE_END - seek relative to the last byte ever written.
+ *
+ * If an application just wants to append some data to the output
+ * buffer, PA_SEEK_RELATIVE and an offset of 0 should be used.
+ *
+ * After a call to pa_stream_write() the write index will be left at
+ * the position right after the last byte of the written data.
+ *
+ * \section latency_sec Latency
+ *
+ * A major problem with networked audio is the increased latency caused by
+ * the network. To remedy this, PulseAudio supports an advanced system of
+ * monitoring the current latency.
+ *
+ * To get the raw data needed to calculate latencies, call
+ * pa_stream_get_timing_info(). This will give you a pa_timing_info
+ * structure that contains everything that is known about the server
+ * side buffer transport delays and the backend active in the
+ * server. (Besides other things it contains the write and read index
+ * values mentioned above.)
+ *
+ * This structure is updated every time a
+ * pa_stream_update_timing_info() operation is executed. (i.e. before
+ * the first call to this function the timing information structure is
+ * not available!) Since it is a lot of work to keep this structure
+ * up-to-date manually, PulseAudio can do that automatically for you:
+ * if PA_STREAM_AUTO_TIMING_UPDATE is passed when connecting the
+ * stream PulseAudio will automatically update the structure every
+ * 100ms and every time a function is called that might invalidate the
+ * previously known timing data (such as pa_stream_write() or
+ * pa_stream_flush()). Please note however, that there always is a
+ * short time window when the data in the timing information structure
+ * is out-of-date. PulseAudio tries to mark these situations by
+ * setting the write_index_corrupt and read_index_corrupt fields
+ * accordingly.
+ *
+ * The raw timing data in the pa_timing_info structure is usually hard
+ * to deal with. Therefore a more simplistic interface is available:
+ * you can call pa_stream_get_time() or pa_stream_get_latency(). The
+ * former will return the current playback time of the hardware since
+ * the stream has been started. The latter returns the time a sample
+ * that you write now takes to be played by the hardware. These two
+ * functions base their calculations on the same data that is returned
+ * by pa_stream_get_timing_info(). Hence the same rules for keeping
+ * the timing data up-to-date apply here. In case the write or read
+ * index is corrupted, these two functions will fail with
+ * PA_ERR_NODATA set.
+ *
+ * Since updating the timing info structure usually requires a full
+ * network round trip and some applications monitor the timing very
+ * often PulseAudio offers a timing interpolation system. If
+ * PA_STREAM_INTERPOLATE_TIMING is passed when connecting the stream,
+ * pa_stream_get_time() and pa_stream_get_latency() will try to
+ * interpolate the current playback time/latency by estimating the
+ * number of samples that have been played back by the hardware since
+ * the last regular timing update. It is espcially useful to combine
+ * this option with PA_STREAM_AUTO_TIMING_UPDATE, which will enable
+ * you to monitor the current playback time/latency very precisely and
+ * very frequently without requiring a network round trip every time.
+ *
+ * \section flow_sec Overflow and underflow
+ *
+ * Even with the best precautions, buffers will sometime over - or
+ * underflow.  To handle this gracefully, the application can be
+ * notified when this happens. Callbacks are registered using
+ * pa_stream_set_overflow_callback() and
+ * pa_stream_set_underflow_callback().
+ *
+ * \section sync_streams Sychronizing Multiple Playback Streams
+ *
+ * PulseAudio allows applications to fully synchronize multiple
+ * playback streams that are connected to the same output device. That
+ * means the streams will always be played back sample-by-sample
+ * synchronously. If stream operations like pa_stream_cork() are
+ * issued on one of the synchronized streams, they are simultaneously
+ * issued on the others.
+ *
+ * To synchronize a stream to another, just pass the "master" stream
+ * as last argument to pa_stream_connect_playack(). To make sure that
+ * the freshly created stream doesn't start playback right-away, make
+ * sure to pass PA_STREAM_START_CORKED and - after all streams have
+ * been created - uncork them all with a single call to
+ * pa_stream_cork() for the master stream.
+ *
+ * To make sure that a particular stream doesn't stop to play when a
+ * server side buffer underrun happens on it while the other
+ * synchronized streams continue playing and hence deviate you need to
+ * pass a "prebuf" pa_buffer_attr of 0 when connecting it.
+ *
+ * \section disc_sec Disconnecting
+ *
+ * When a stream has served is purpose it must be disconnected with
+ * pa_stream_disconnect(). If you only unreference it, then it will live on
+ * and eat resources both locally and on the server until you disconnect the
+ * context.
+ *
+ */
+
+/** \file
+ * Audio streams for input, output and sample upload */
+
+PA_C_DECL_BEGIN
+
+/** An opaque stream for playback or recording */
+typedef struct pa_stream pa_stream;
+
+/** A generic callback for operation completion */
+typedef void (*pa_stream_success_cb_t) (pa_stream*s, int success, void *userdata);
+
+/** A generic request callback */
+typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdata);
+
+/** A generic notification callback */
+typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
+
+/** Create a new, unconnected stream with the specified name and
+ * sample type. It is recommended to use pa_stream_new_with_proplist()
+ * instead and specify some initial properties. */
+pa_stream* pa_stream_new(
+        pa_context *c                     /**< The context to create this stream in */,
+        const char *name                  /**< A name for this stream */,
+        const pa_sample_spec *ss          /**< The desired sample format */,
+        const pa_channel_map *map         /**< The desired channel map, or NULL for default */);
+
+/** Create a new, unconnected stream with the specified name and
+ * sample type, and specify the the initial stream property
+ * list. \since 0.9.11 */
+pa_stream* pa_stream_new_with_proplist(
+        pa_context *c                     /**< The context to create this stream in */,
+        const char *name                  /**< A name for this stream */,
+        const pa_sample_spec *ss          /**< The desired sample format */,
+        const pa_channel_map *map         /**< The desired channel map, or NULL for default */,
+        pa_proplist *p                    /**< The initial property list */);
+
+/** Decrease the reference counter by one */
+void pa_stream_unref(pa_stream *s);
+
+/** Increase the reference counter by one */
+pa_stream *pa_stream_ref(pa_stream *s);
+
+/** Return the current state of the stream */
+pa_stream_state_t pa_stream_get_state(pa_stream *p);
+
+/** Return the context this stream is attached to */
+pa_context* pa_stream_get_context(pa_stream *p);
+
+/** Return the sink input resp. source output index this stream is
+ * identified in the server with. This is useful for usage with the
+ * introspection functions, such as pa_context_get_sink_input_info()
+ * resp. pa_context_get_source_output_info(). */
+uint32_t pa_stream_get_index(pa_stream *s);
+
+/** Return the index of the sink or source this stream is connected to
+ * in the server. This is useful for usage with the introspection
+ * functions, such as pa_context_get_sink_info_by_index()
+ * resp. pa_context_get_source_info_by_index(). Please note that
+ * streams may be moved between sinks/sources and thus it is
+ * recommended to use pa_stream_set_moved_callback() to be notified
+ * about this. This function will return with PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+uint32_t pa_stream_get_device_index(pa_stream *s);
+
+/** Return the name of the sink or source this stream is connected to
+ * in the server. This is useful for usage with the introspection
+ * functions, such as pa_context_get_sink_info_by_name()
+ * resp. pa_context_get_source_info_by_name(). Please note that
+ * streams may be moved between sinks/sources and thus it is
+ * recommended to use pa_stream_set_moved_callback() to be notified
+ * about this. This function will return with PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+const char *pa_stream_get_device_name(pa_stream *s);
+
+/** Return 1 if the sink or source this stream is connected to has
+ * been suspended. This will return 0 if not, and negative on
+ * error. This function will return with PA_ERR_NOTSUPPORTED when the
+ * server is older than 0.9.8. \since 0.9.8 */
+int pa_stream_is_suspended(pa_stream *s);
+
+/** Return 1 if the this stream has been corked. This will return 0 if
+ * not, and negative on error. \since 0.9.11 */
+int pa_stream_is_corked(pa_stream *s);
+
+/** Connect the stream to a sink */
+int pa_stream_connect_playback(
+        pa_stream *s                  /**< The stream to connect to a sink */,
+        const char *dev               /**< Name of the sink to connect to, or NULL for default */ ,
+        const pa_buffer_attr *attr    /**< Buffering attributes, or NULL for default */,
+        pa_stream_flags_t flags       /**< Additional flags, or 0 for default */,
+        pa_cvolume *volume            /**< Initial volume, or NULL for default */,
+        pa_stream *sync_stream        /**< Synchronize this stream with the specified one, or NULL for a standalone stream*/);
+
+/** Connect the stream to a source */
+int pa_stream_connect_record(
+        pa_stream *s                  /**< The stream to connect to a source */ ,
+        const char *dev               /**< Name of the source to connect to, or NULL for default */,
+        const pa_buffer_attr *attr    /**< Buffer attributes, or NULL for default */,
+        pa_stream_flags_t flags       /**< Additional flags, or 0 for default */);
+
+/** Disconnect a stream from a source/sink */
+int pa_stream_disconnect(pa_stream *s);
+
+/** Write some data to the server (for playback sinks), if free_cb is
+ * non-NULL this routine is called when all data has been written out
+ * and an internal reference to the specified data is kept, the data
+ * is not copied. If NULL, the data is copied into an internal
+ * buffer. The client my freely seek around in the output buffer. For
+ * most applications passing 0 and PA_SEEK_RELATIVE as arguments for
+ * offset and seek should be useful.*/
+int pa_stream_write(
+        pa_stream *p             /**< The stream to use */,
+        const void *data         /**< The data to write */,
+        size_t nbytes            /**< The length of the data to write in bytes*/,
+        pa_free_cb_t free_cb     /**< A cleanup routine for the data or NULL to request an internal copy */,
+        int64_t offset,          /**< Offset for seeking, must be 0 for upload streams */
+        pa_seek_mode_t seek      /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
+
+/** Read the next fragment from the buffer (for recording).
+ * data will point to the actual data and length will contain the size
+ * of the data in bytes (which can be less than a complete framgnet).
+ * Use pa_stream_drop() to actually remove the data from the
+ * buffer. If no data is available will return a NULL pointer */
+int pa_stream_peek(
+        pa_stream *p                 /**< The stream to use */,
+        const void **data            /**< Pointer to pointer that will point to data */,
+        size_t *nbytes               /**< The length of the data read in bytes */);
+
+/** Remove the current fragment on record streams. It is invalid to do this without first
+ * calling pa_stream_peek(). */
+int pa_stream_drop(pa_stream *p);
+
+/** Return the number of bytes that may be written using pa_stream_write() */
+size_t pa_stream_writable_size(pa_stream *p);
+
+/** Return the number of bytes that may be read using pa_stream_read()*/
+size_t pa_stream_readable_size(pa_stream *p);
+
+/** Drain a playback stream. Use this for notification when the buffer is empty */
+pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Request a timing info structure update for a stream. Use
+ * pa_stream_get_timing_info() to get access to the raw timing data,
+ * or pa_stream_get_time() or pa_stream_get_latency() to get cleaned
+ * up values. */
+pa_operation* pa_stream_update_timing_info(pa_stream *p, pa_stream_success_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever the state of the stream changes */
+void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when new data may be
+ * written to the stream. */
+void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when new data is available from the stream.
+ * Return the number of bytes read.*/
+void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) */
+void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) */
+void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when a the server starts
+ * playback after an underrun or on initial startup. This only informs
+ * that audio is flowing again, it is no indication that audio startet
+ * to reach the speakers already. (Only for playback streams). \since
+ * 0.9.11 */
+void pa_stream_set_started_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever a latency
+ * information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE
+ * streams only. (Only for playback streams) */
+void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever the stream is
+ * moved to a different sink/source. Use pa_stream_get_device_name()or
+ * pa_stream_get_device_index() to query the new sink/source. This
+ * notification is only generated when the server is at least
+ * 0.9.8. \since 0.9.8 */
+void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called whenever the sink/source
+ * this stream is connected to is suspended or resumed. Use
+ * pa_stream_is_suspended() to query the new suspend status. Please
+ * note that the suspend status might also change when the stream is
+ * moved between devices. Thus if you call this function you very
+ * likely want to call pa_stream_set_moved_callback, too. This
+ * notification is only generated when the server is at least
+ * 0.9.8. \since 0.9.8 */
+void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */
+pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
+
+/** Flush the playback buffer of this stream. Most of the time you're
+ * better off using the parameter delta of pa_stream_write() instead
+ * of this function. Available on both playback and recording
+ * streams. */
+pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Reenable prebuffering as specified in the pa_buffer_attr
+ * structure. Available for playback streams only. */
+pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Request immediate start of playback on this stream. This disables
+ * prebuffering as specified in the pa_buffer_attr structure,
+ * temporarily. Available for playback streams only. */
+pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
+
+/** Rename the stream. */
+pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
+
+/** Return the current playback/recording time. This is based on the
+ * data in the timing info structure returned by
+ * pa_stream_get_timing_info(). This function will usually only return
+ * new data if a timing info update has been recieved. Only if timing
+ * interpolation has been requested (PA_STREAM_INTERPOLATE_TIMING)
+ * the data from the last timing update is used for an estimation of
+ * the current playback/recording time based on the local time that
+ * passed since the timing info structure has been acquired. The time
+ * value returned by this function is guaranteed to increase
+ * monotonically. (that means: the returned value is always greater or
+ * equal to the value returned on the last call) This behaviour can
+ * be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be
+ * desirable to deal better with bad estimations of transport
+ * latencies, but may have strange effects if the application is not
+ * able to deal with time going 'backwards'. */
+int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec);
+
+/** Return the total stream latency. This function is based on
+ * pa_stream_get_time(). In case the stream is a monitoring stream the
+ * result can be negative, i.e. the captured samples are not yet
+ * played. In this case *negative is set to 1. */
+int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
+
+/** Return the latest raw timing data structure. The returned pointer
+ * points to an internal read-only instance of the timing
+ * structure. The user should make a copy of this structure if he
+ * wants to modify it. An in-place update to this data structure may
+ * be requested using pa_stream_update_timing_info(). If no
+ * pa_stream_update_timing_info() call was issued before, this
+ * function will fail with PA_ERR_NODATA. Please note that the
+ * write_index member field (and only this field) is updated on each
+ * pa_stream_write() call, not just when a timing update has been
+ * recieved. */
+const pa_timing_info* pa_stream_get_timing_info(pa_stream *s);
+
+/** Return a pointer to the stream's sample specification. */
+const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);
+
+/** Return a pointer to the stream's channel map. */
+const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
+
+/** Return the buffer metrics of the stream. Only valid after the
+ * stream has been connected successfuly and if the server is at least
+ * PulseAudio 0.9. \since 0.9.0 */
+const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s);
+
+/** Change the buffer metrics of the stream during playback. The
+ * server might have chosen different buffer metrics then
+ * requested. The selected metrics may be queried with
+ * pa_stream_get_buffer_attr() as soon as the callback is called. Only
+ * valid after the stream has been connected successfully and if the
+ * server is at least PulseAudio 0.9.8. \since 0.9.8 */
+pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata);
+
+/** Change the stream sampling rate during playback. You need to pass
+ * PA_STREAM_VARIABLE_RATE in the flags parameter of
+ * pa_stream_connect() if you plan to use this function. Only valid
+ * after the stream has been connected successfully and if the server
+ * is at least PulseAudio 0.9.8. \since 0.9.8 */
+pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata);
+
+/** Update the property list of the sink input/source output of this
+ * stream, adding new entries. Please note that it is highly
+ * recommended to set as much properties initially via
+ * pa_stream_new_with_proplist() as possible instead a posteriori with
+ * this function, since that information may then be used to route
+ * this stream to the right device. \since 0.9.11 */
+pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata);
+
+/** Update the property list of the sink input/source output of this
+ * stream, remove entries. \since 0.9.11 */
+pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata);
+
+/** For record streams connected to a monitor source: monitor only a
+ * very specific sink input of the sink. Thus function needs to be
+ * called before pa_stream_connect_record() is called. \since
+ * 0.9.11 */
+int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx);
+
+/** Return what has been set with pa_stream_set_monitor_stream()
+ * ebfore. \since 0.9.11 */
+uint32_t pa_stream_get_monitor_stream(pa_stream *s);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c
new file mode 100644 (file)
index 0000000..d9c06b7
--- /dev/null
@@ -0,0 +1,94 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+
+#include "subscribe.h"
+
+void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_subscription_event_type_t e;
+    uint32_t idx;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (pa_tagstruct_getu32(t, &e) < 0 ||
+        pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (c->subscribe_callback)
+        c->subscribe_callback(c, e, idx, c->subscribe_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+
+pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SUBSCRIBE, &tag);
+    pa_tagstruct_putu32(t, m);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+        return;
+
+    c->subscribe_callback = cb;
+    c->subscribe_userdata = userdata;
+}
diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h
new file mode 100644 (file)
index 0000000..0e4be8c
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef foosubscribehfoo
+#define foosubscribehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/def.h>
+#include <pulse/context.h>
+#include <pulse/cdecl.h>
+
+/** \page subscribe Event Subscription
+ *
+ * \section overv_sec Overview
+ *
+ * The application can be notified, asynchronously, whenever the internal
+ * layout of the server changes. Possible notifications are desribed in the
+ * \ref pa_subscription_event_type and \ref pa_subscription_mask
+ * enumerations.
+ *
+ * The application sets the notification mask using pa_context_subscribe()
+ * and the function that will be called whenever a notification occurs using
+ * pa_context_set_subscribe_callback().
+ */
+
+/** \file
+ * Daemon introspection event subscription subsystem. */
+
+PA_C_DECL_BEGIN
+
+/** Subscription event callback prototype */
+typedef void (*pa_context_subscribe_cb_t)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
+
+/** Enable event notification */
+pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the context specific call back function that is called whenever the state of the daemon changes */
+void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c
new file mode 100644 (file)
index 0000000..6b66696
--- /dev/null
@@ -0,0 +1,229 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdio.h>
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else
+#include <pulsecore/poll.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/mainloop.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/macro.h>
+
+#include "thread-mainloop.h"
+
+struct pa_threaded_mainloop {
+    pa_mainloop *real_mainloop;
+    int n_waiting;
+
+    pa_thread* thread;
+    pa_mutex* mutex;
+    pa_cond* cond, *accept_cond;
+};
+
+static inline int in_worker(pa_threaded_mainloop *m) {
+    return pa_thread_self() == m->thread;
+}
+
+static int poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
+    pa_mutex *mutex = userdata;
+    int r;
+
+    pa_assert(mutex);
+
+    /* Before entering poll() we unlock the mutex, so that
+     * avahi_simple_poll_quit() can succeed from another thread. */
+
+    pa_mutex_unlock(mutex);
+    r = poll(ufds, nfds, timeout);
+    pa_mutex_lock(mutex);
+
+    return r;
+}
+
+static void thread(void *userdata) {
+    pa_threaded_mainloop *m = userdata;
+
+#ifndef OS_IS_WIN32
+    sigset_t mask;
+
+    /* Make sure that signals are delivered to the main thread */
+    sigfillset(&mask);
+    pthread_sigmask(SIG_BLOCK, &mask, NULL);
+#endif
+
+    pa_mutex_lock(m->mutex);
+
+    pa_mainloop_run(m->real_mainloop, NULL);
+
+    pa_mutex_unlock(m->mutex);
+}
+
+pa_threaded_mainloop *pa_threaded_mainloop_new(void) {
+    pa_threaded_mainloop *m;
+
+    m = pa_xnew(pa_threaded_mainloop, 1);
+
+    if (!(m->real_mainloop = pa_mainloop_new())) {
+        pa_xfree(m);
+        return NULL;
+    }
+
+    m->mutex = pa_mutex_new(TRUE, TRUE);
+    m->cond = pa_cond_new();
+    m->accept_cond = pa_cond_new();
+    m->thread = NULL;
+
+    pa_mainloop_set_poll_func(m->real_mainloop, poll_func, m->mutex);
+
+    m->n_waiting = 0;
+
+    return m;
+}
+
+void pa_threaded_mainloop_free(pa_threaded_mainloop* m) {
+    pa_assert(m);
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert((m->thread && !pa_thread_is_running(m->thread)) || !in_worker(m));
+
+    pa_threaded_mainloop_stop(m);
+
+    if (m->thread)
+        pa_thread_free(m->thread);
+
+    pa_mainloop_free(m->real_mainloop);
+
+    pa_mutex_free(m->mutex);
+    pa_cond_free(m->cond);
+    pa_cond_free(m->accept_cond);
+
+    pa_xfree(m);
+}
+
+int pa_threaded_mainloop_start(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    pa_assert(!m->thread || !pa_thread_is_running(m->thread));
+
+    if (!(m->thread = pa_thread_new(thread, m)))
+        return -1;
+
+    return 0;
+}
+
+void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    if (!m->thread || !pa_thread_is_running(m->thread))
+        return;
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert(!in_worker(m));
+
+    pa_mutex_lock(m->mutex);
+    pa_mainloop_quit(m->real_mainloop, 0);
+    pa_mutex_unlock(m->mutex);
+
+    pa_thread_join(m->thread);
+}
+
+void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+
+    pa_mutex_lock(m->mutex);
+}
+
+void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+
+    pa_mutex_unlock(m->mutex);
+}
+
+void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
+    pa_assert(m);
+
+    pa_cond_signal(m->cond, 1);
+
+    if (wait_for_accept && m->n_waiting > 0)
+        pa_cond_wait(m->accept_cond, m->mutex);
+}
+
+void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+
+    m->n_waiting ++;
+
+    pa_cond_wait(m->cond, m->mutex);
+
+    pa_assert(m->n_waiting > 0);
+    m->n_waiting --;
+}
+
+void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    /* Make sure that this function is not called from the helper thread */
+    pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+
+    pa_cond_signal(m->accept_cond, 0);
+}
+
+int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    return pa_mainloop_get_retval(m->real_mainloop);
+}
+
+pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) {
+    pa_assert(m);
+
+    return pa_mainloop_get_api(m->real_mainloop);
+}
+
+int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) {
+    pa_assert(m);
+
+    return m->thread && pa_thread_self() == m->thread;
+}
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
new file mode 100644 (file)
index 0000000..521e29b
--- /dev/null
@@ -0,0 +1,303 @@
+#ifndef foothreadmainloophfoo
+#define foothreadmainloophfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/cdecl.h>
+
+PA_C_DECL_BEGIN
+
+/** \page threaded_mainloop Threaded Main Loop
+ *
+ * \section overv_sec Overview
+ *
+ * The threaded main loop implementation is a special version of the primary
+ * main loop implementation (see \ref mainloop). For the basic design, see
+ * its documentation.
+ *
+ * The added feature in the threaded main loop is that it spawns a new thread
+ * that runs the real main loop. This allows a synchronous application to use
+ * the asynchronous API without risking to stall the PulseAudio library.
+ *
+ * \section creat_sec Creation
+ *
+ * A pa_threaded_mainloop object is created using pa_threaded_mainloop_new().
+ * This will only allocate the required structures though, so to use it the
+ * thread must also be started. This is done through
+ * pa_threaded_mainloop_start(), after which you can start using the main loop.
+ *
+ * \section destr_sec Destruction
+ *
+ * When the PulseAudio connection has been terminated, the thread must be
+ * stopped and the resources freed. Stopping the thread is done using
+ * pa_threaded_mainloop_stop(), which must be called without the lock (see
+ * below) held. When that function returns, the thread is stopped and the
+ * pa_threaded_mainloop object can be freed using pa_threaded_mainloop_free().
+ *
+ * \section lock_sec Locking
+ *
+ * Since the PulseAudio API doesn't allow concurrent accesses to objects,
+ * a locking scheme must be used to guarantee safe usage. The threaded main
+ * loop API provides such a scheme through the functions
+ * pa_threaded_mainloop_lock() and pa_threaded_mainloop_unlock().
+ *
+ * The lock is recursive, so it's safe to use it multiple times from the same
+ * thread. Just make sure you call pa_threaded_mainloop_unlock() the same
+ * number of times you called pa_threaded_mainloop_lock().
+ *
+ * The lock needs to be held whenever you call any PulseAudio function that
+ * uses an object associated with this main loop. Make sure you do not hold
+ * on to the lock more than necessary though, as the threaded main loop stops
+ * while the lock is held.
+ *
+ * Example:
+ *
+ * \code
+ * void my_check_stream_func(pa_threaded_mainloop *m, pa_stream *s) {
+ *     pa_stream_state_t state;
+ *
+ *     pa_threaded_mainloop_lock(m);
+ *
+ *     state = pa_stream_get_state(s);
+ *
+ *     pa_threaded_mainloop_unlock(m);
+ *
+ *     if (state == PA_STREAM_READY)
+ *         printf("Stream is ready!");
+ *     else
+ *         printf("Stream is not ready!");
+ * }
+ * \endcode
+ *
+ * \section cb_sec Callbacks
+ *
+ * Callbacks in PulseAudio are asynchronous, so they require extra care when
+ * using them together with a threaded main loop.
+ *
+ * The easiest way to turn the callback based operations into synchronous
+ * ones, is to simply wait for the callback to be called and continue from
+ * there. This is the approach chosen in PulseAudio's threaded API.
+ *
+ * \subsection basic_subsec Basic callbacks
+ *
+ * For the basic case, where all that is required is to wait for the callback
+ * to be invoked, the code should look something like this:
+ *
+ * Example:
+ *
+ * \code
+ * static void my_drain_callback(pa_stream*s, int success, void *userdata) {
+ *     pa_threaded_mainloop *m;
+ *
+ *     m = (pa_threaded_mainloop*)userdata;
+ *     assert(m);
+ *
+ *     pa_threaded_mainloop_signal(m, 0);
+ * }
+ *
+ * void my_drain_stream_func(pa_threaded_mainloop *m, pa_stream *s) {
+ *     pa_operation *o;
+ *
+ *     pa_threaded_mainloop_lock(m);
+ *
+ *     o = pa_stream_drain(s, my_drain_callback, m);
+ *     assert(o);
+ *
+ *     while (pa_operation_get_state(o) != OPERATION_DONE)
+ *         pa_threaded_mainloop_wait(m);
+ *
+ *     pa_operation_unref(o);
+ *
+ *     pa_threaded_mainloop_unlock(m);
+ * }
+ * \endcode
+ *
+ * The main function, my_drain_stream_func(), will wait for the callback to
+ * be called using pa_threaded_mainloop_wait().
+ *
+ * If your application is multi-threaded, then this waiting must be done
+ * inside a while loop. The reason for this is that multiple threads might be
+ * using pa_threaded_mainloop_wait() at the same time. Each thread must
+ * therefore verify that it was its callback that was invoked.
+ *
+ * The callback, my_drain_callback(), indicates to the main function that it
+ * has been called using pa_threaded_mainloop_signal().
+ *
+ * As you can see, both pa_threaded_mainloop_wait() may only be called with
+ * the lock held. The same thing is true for pa_threaded_mainloop_signal(),
+ * but as the lock is held before the callback is invoked, you do not have to
+ * deal with that.
+ *
+ * The functions will not dead lock because the wait function will release
+ * the lock before waiting and then regrab it once it has been signaled.
+ * For those of you familiar with threads, the behaviour is that of a
+ * condition variable.
+ *
+ * \subsection data_subsec Data callbacks
+ *
+ * For many callbacks, simply knowing that they have been called is
+ * insufficient. The callback also receives some data that is desired. To
+ * access this data safely, we must extend our example a bit:
+ *
+ * \code
+ * static int *drain_result;
+ *
+ * static void my_drain_callback(pa_stream*s, int success, void *userdata) {
+ *     pa_threaded_mainloop *m;
+ *
+ *     m = (pa_threaded_mainloop*)userdata;
+ *     assert(m);
+ *
+ *     drain_result = &success;
+ *
+ *     pa_threaded_mainloop_signal(m, 1);
+ * }
+ *
+ * void my_drain_stream_func(pa_threaded_mainloop *m, pa_stream *s) {
+ *     pa_operation *o;
+ *
+ *     pa_threaded_mainloop_lock(m);
+ *
+ *     o = pa_stream_drain(s, my_drain_callback, m);
+ *     assert(o);
+ *
+ *     while (pa_operation_get_state(o) != OPERATION_DONE)
+ *         pa_threaded_mainloop_wait(m);
+ *
+ *     pa_operation_unref(o);
+ *
+ *     if (*drain_result)
+ *         printf("Success!");
+ *     else
+ *         printf("Bitter defeat...");
+ *
+ *     pa_threaded_mainloop_accept(m);
+ *
+ *     pa_threaded_mainloop_unlock(m);
+ * }
+ * \endcode
+ *
+ * The example is a bit silly as it would probably have been easier to just
+ * copy the contents of success, but for larger data structures this can be
+ * wasteful.
+ *
+ * The difference here compared to the basic callback is the 1 sent to
+ * pa_threaded_mainloop_signal() and the call to
+ * pa_threaded_mainloop_accept(). What will happen is that
+ * pa_threaded_mainloop_signal() will signal the main function and then stop.
+ * The main function is then free to use the data in the callback until
+ * pa_threaded_mainloop_accept() is called, which will allow the callback
+ * to continue.
+ *
+ * Note that pa_threaded_mainloop_accept() must be called some time between
+ * exiting the while loop and unlocking the main loop! Failure to do so will
+ * result in a race condition. I.e. it is not ok to release the lock and
+ * regrab it before calling pa_threaded_mainloop_accept().
+ *
+ * \subsection async_subsec Asynchronous callbacks
+ *
+ * PulseAudio also has callbacks that are completely asynchronous, meaning
+ * that they can be called at any time. The threading main loop API provides
+ * the locking mechanism to handle concurrent accesses, but nothing else.
+ * Applications will have to handle communication from the callback to the
+ * main program through some own system.
+ *
+ * The callbacks that are completely asynchronous are:
+ *
+ * \li State callbacks for contexts, streams, etc.
+ * \li Subscription notifications
+ */
+
+/** \file
+ *
+ * A thread based event loop implementation based on pa_mainloop. The
+ * event loop is run in a helper thread in the background. A few
+ * synchronization primitives are available to access the objects
+ * attached to the event loop safely. */
+
+/** An opaque threaded main loop object */
+typedef struct pa_threaded_mainloop pa_threaded_mainloop;
+
+/** Allocate a new threaded main loop object. You have to call
+ * pa_threaded_mainloop_start() before the event loop thread starts
+ * running. */
+pa_threaded_mainloop *pa_threaded_mainloop_new(void);
+
+/** Free a threaded main loop object. If the event loop thread is
+ * still running, it is terminated using pa_threaded_mainloop_stop()
+ * first. */
+void pa_threaded_mainloop_free(pa_threaded_mainloop* m);
+
+/** Start the event loop thread. */
+int pa_threaded_mainloop_start(pa_threaded_mainloop *m);
+
+/** Terminate the event loop thread cleanly. Make sure to unlock the
+ * mainloop object before calling this function. */
+void pa_threaded_mainloop_stop(pa_threaded_mainloop *m);
+
+/** Lock the event loop object, effectively blocking the event loop
+ * thread from processing events. You can use this to enforce
+ * exclusive access to all objects attached to the event loop. This
+ * lock is recursive. This function may not be called inside the event
+ * loop thread. Events that are dispatched from the event loop thread
+ * are executed with this lock held. */
+void pa_threaded_mainloop_lock(pa_threaded_mainloop *m);
+
+/** Unlock the event loop object, inverse of pa_threaded_mainloop_lock() */
+void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m);
+
+/** Wait for an event to be signalled by the event loop thread. You
+ * can use this to pass data from the event loop thread to the main
+ * thread in synchronized fashion. This function may not be called
+ * inside the event loop thread. Prior to this call the event loop
+ * object needs to be locked using pa_threaded_mainloop_lock(). While
+ * waiting the lock will be released, immediately before returning it
+ * will be acquired again. */
+void pa_threaded_mainloop_wait(pa_threaded_mainloop *m);
+
+/** Signal all threads waiting for a signalling event in
+ * pa_threaded_mainloop_wait(). If wait_for_release is non-zero, do
+ * not return before the signal was accepted by a
+ * pa_threaded_mainloop_accept() call. While waiting for that condition
+ * the event loop object is unlocked. */
+void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept);
+
+/** Accept a signal from the event thread issued with
+ * pa_threaded_mainloop_signal(). This call should only be used in
+ * conjunction with pa_threaded_mainloop_signal() with a non-zero
+ * wait_for_accept value.  */
+void pa_threaded_mainloop_accept(pa_threaded_mainloop *m);
+
+/** Return the return value as specified with the main loop's quit() routine. */
+int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m);
+
+/** Return the abstract main loop abstraction layer vtable for this main loop. */
+pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m);
+
+/** Returns non-zero when called from withing the event loop thread. \since 0.9.7 */
+int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c
new file mode 100644 (file)
index 0000000..9708a73
--- /dev/null
@@ -0,0 +1,182 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <stddef.h>
+#include <sys/time.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulsecore/winsock.h>
+#include <pulsecore/macro.h>
+
+#include "timeval.h"
+
+struct timeval *pa_gettimeofday(struct timeval *tv) {
+#ifdef HAVE_GETTIMEOFDAY
+    pa_assert(tv);
+
+    pa_assert_se(gettimeofday(tv, NULL) == 0);
+    return tv;
+#elif defined(OS_IS_WIN32)
+    /*
+     * Copied from implementation by Steven Edwards (LGPL).
+     * Found on wine mailing list.
+     */
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+#define EPOCHFILETIME (116444736000000000i64)
+#else
+#define EPOCHFILETIME (116444736000000000LL)
+#endif
+
+    FILETIME        ft;
+    LARGE_INTEGER   li;
+    __int64         t;
+
+    pa_assert(tv);
+
+    GetSystemTimeAsFileTime(&ft);
+    li.LowPart  = ft.dwLowDateTime;
+    li.HighPart = ft.dwHighDateTime;
+    t  = li.QuadPart;       /* In 100-nanosecond intervals */
+    t -= EPOCHFILETIME;     /* Offset to the Epoch time */
+    t /= 10;                /* In microseconds */
+    tv->tv_sec  = (time_t) (t / PA_USEC_PER_SEC);
+    tv->tv_usec = (suseconds_t) (t % PA_USEC_PER_SEC);
+
+    return tv;
+#else
+#error "Platform lacks gettimeofday() or equivalent function."
+#endif
+}
+
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
+    pa_usec_t r;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    /* Check which whan is the earlier time and swap the two arguments if required. */
+    if (pa_timeval_cmp(a, b) < 0) {
+        const struct timeval *c;
+        c = a;
+        a = b;
+        b = c;
+    }
+
+    /* Calculate the second difference*/
+    r = ((pa_usec_t) a->tv_sec - b->tv_sec) * PA_USEC_PER_SEC;
+
+    /* Calculate the microsecond difference */
+    if (a->tv_usec > b->tv_usec)
+        r += ((pa_usec_t) a->tv_usec - b->tv_usec);
+    else if (a->tv_usec < b->tv_usec)
+        r -= ((pa_usec_t) b->tv_usec - a->tv_usec);
+
+    return r;
+}
+
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    if (a->tv_sec < b->tv_sec)
+        return -1;
+
+    if (a->tv_sec > b->tv_sec)
+        return 1;
+
+    if (a->tv_usec < b->tv_usec)
+        return -1;
+
+    if (a->tv_usec > b->tv_usec)
+        return 1;
+
+    return 0;
+}
+
+pa_usec_t pa_timeval_age(const struct timeval *tv) {
+    struct timeval now;
+    pa_assert(tv);
+
+    return pa_timeval_diff(pa_gettimeofday(&now), tv);
+}
+
+struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) {
+    unsigned long secs;
+    pa_assert(tv);
+
+    secs = (unsigned long) (v/PA_USEC_PER_SEC);
+    tv->tv_sec += secs;
+    v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC;
+
+    tv->tv_usec += (suseconds_t) v;
+
+    /* Normalize */
+    while ((unsigned) tv->tv_usec >= PA_USEC_PER_SEC) {
+        tv->tv_sec++;
+        tv->tv_usec -= PA_USEC_PER_SEC;
+    }
+
+    return tv;
+}
+
+struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) {
+    unsigned long secs;
+    pa_assert(tv);
+
+    secs = (unsigned long) (v/PA_USEC_PER_SEC);
+    tv->tv_sec -= secs;
+    v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC;
+
+    if (tv->tv_usec >= (suseconds_t) v)
+        tv->tv_usec -= (suseconds_t) v;
+    else {
+        tv->tv_sec --;
+        tv->tv_usec = tv->tv_usec + PA_USEC_PER_SEC - v;
+    }
+
+    return tv;
+}
+
+struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) {
+    pa_assert(tv);
+
+    tv->tv_sec = v / PA_USEC_PER_SEC;
+    tv->tv_usec = v % PA_USEC_PER_SEC;
+
+    return tv;
+}
+
+pa_usec_t pa_timeval_load(const struct timeval *tv) {
+    pa_assert(tv);
+
+    return
+        (pa_usec_t) tv->tv_sec * PA_USEC_PER_SEC +
+        (pa_usec_t) tv->tv_usec;
+}
diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h
new file mode 100644 (file)
index 0000000..ee39829
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef footimevalhfoo
+#define footimevalhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/sample.h>
+
+/** \file
+ * Utility functions for handling timeval calculations */
+
+PA_C_DECL_BEGIN
+
+#define PA_MSEC_PER_SEC ((pa_usec_t) 1000ULL)
+#define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL)
+#define PA_NSEC_PER_SEC ((pa_usec_t) 1000000000ULL)
+#define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL)
+#define PA_NSEC_PER_MSEC ((pa_usec_t) 1000000ULL)
+#define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL)
+
+struct timeval;
+
+/** Return the current timestamp, just like UNIX gettimeofday() */
+struct timeval *pa_gettimeofday(struct timeval *tv);
+
+/** Calculate the difference between the two specified timeval
+ * structs. */
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) PA_GCC_PURE;
+
+/** Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) PA_GCC_PURE;
+
+/** Return the time difference between now and the specified timestamp */
+pa_usec_t pa_timeval_age(const struct timeval *tv);
+
+/** Add the specified time inmicroseconds to the specified timeval structure */
+struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v);
+
+/** Subtract the specified time inmicroseconds to the specified timeval structure. \since 0.9.11 */
+struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v);
+
+/** Store the specified uec value in the timeval struct. \since 0.9.7 */
+struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v);
+
+/** Load the specified tv value and return it in usec. \since 0.9.7 */
+pa_usec_t pa_timeval_load(const struct timeval *tv);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c
new file mode 100644 (file)
index 0000000..119be54
--- /dev/null
@@ -0,0 +1,265 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* This file is based on the GLIB utf8 validation functions. The
+ * original license text follows. */
+
+/* gutf8.c - Operations on UTF-8 strings.
+ *
+ * Copyright (C) 1999 Tom Tromey
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "utf8.h"
+
+#define FILTER_CHAR '_'
+
+static inline int is_unicode_valid(uint32_t ch) {
+
+    if (ch >= 0x110000) /* End of unicode space */
+        return 0;
+    if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
+        return 0;
+    if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
+        return 0;
+    if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
+        return 0;
+
+    return 1;
+}
+
+static inline int is_continuation_char(uint8_t ch) {
+    if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
+        return 0;
+    return 1;
+}
+
+static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
+    *u_ch <<= 6;
+    *u_ch |= ch & 0x3f;
+}
+
+static char* utf8_validate(const char *str, char *output) {
+    uint32_t val = 0;
+    uint32_t min = 0;
+    const uint8_t *p, *last;
+    int size;
+    uint8_t *o;
+
+    pa_assert(str);
+
+    o = (uint8_t*) output;
+    for (p = (const uint8_t*) str; *p; p++) {
+        if (*p < 128) {
+            if (o)
+                *o = *p;
+        } else {
+            last = p;
+
+            if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
+                size = 2;
+                min = 128;
+                val = *p & 0x1e;
+                goto ONE_REMAINING;
+            } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
+                size = 3;
+                min = (1 << 11);
+                val = *p & 0x0f;
+                goto TWO_REMAINING;
+            } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
+                size = 4;
+                min = (1 << 16);
+                val = *p & 0x07;
+            } else {
+                size = 1;
+                goto error;
+            }
+
+            p++;
+            if (!is_continuation_char(*p))
+                goto error;
+            merge_continuation_char(&val, *p);
+
+TWO_REMAINING:
+            p++;
+            if (!is_continuation_char(*p))
+                goto error;
+            merge_continuation_char(&val, *p);
+
+ONE_REMAINING:
+            p++;
+            if (!is_continuation_char(*p))
+                goto error;
+            merge_continuation_char(&val, *p);
+
+            if (val < min)
+                goto error;
+
+            if (!is_unicode_valid(val))
+                goto error;
+
+            if (o) {
+                memcpy(o, last, size);
+                o += size - 1;
+            }
+
+            if (o)
+                o++;
+
+            continue;
+
+error:
+            if (o) {
+                *o = FILTER_CHAR;
+                p = last; /* We retry at the next character */
+            } else
+                goto failure;
+        }
+
+        if (o)
+            o++;
+    }
+
+    if (o) {
+        *o = '\0';
+        return output;
+    }
+
+    return (char*) str;
+
+failure:
+    return NULL;
+}
+
+char* pa_utf8_valid (const char *str) {
+    return utf8_validate(str, NULL);
+}
+
+char* pa_utf8_filter (const char *str) {
+    char *new_str;
+
+    pa_assert(str);
+    new_str = pa_xnew(char, strlen(str) + 1);
+    return utf8_validate(str, new_str);
+}
+
+#ifdef HAVE_ICONV
+
+static char* iconv_simple(const char *str, const char *to, const char *from) {
+    char *new_str;
+    size_t len, inlen;
+    iconv_t cd;
+    ICONV_CONST char *inbuf;
+    char *outbuf;
+    size_t res, inbytes, outbytes;
+
+    pa_assert(str);
+    pa_assert(to);
+    pa_assert(from);
+
+    cd = iconv_open(to, from);
+    if (cd == (iconv_t)-1)
+        return NULL;
+
+    inlen = len = strlen(str) + 1;
+    new_str = pa_xnew(char, len);
+
+    for (;;) {
+        inbuf = (ICONV_CONST char*) str; /* Brain dead prototype for iconv() */
+        inbytes = inlen;
+        outbuf = new_str;
+        outbytes = len;
+
+        res = iconv(cd, &inbuf, &inbytes, &outbuf, &outbytes);
+
+        if (res != (size_t)-1)
+            break;
+
+        if (errno != E2BIG) {
+            pa_xfree(new_str);
+            new_str = NULL;
+            break;
+        }
+
+        pa_assert(inbytes != 0);
+
+        len += inbytes;
+        new_str = pa_xrealloc(new_str, len);
+    }
+
+    iconv_close(cd);
+
+    return new_str;
+}
+
+char* pa_utf8_to_locale (const char *str) {
+    return iconv_simple(str, "", "UTF-8");
+}
+
+char* pa_locale_to_utf8 (const char *str) {
+    return iconv_simple(str, "UTF-8", "");
+}
+
+#else
+
+char* pa_utf8_to_locale (const char *str) {
+    pa_assert(str);
+    return NULL;
+}
+
+char* pa_locale_to_utf8 (const char *str) {
+    pa_assert(str);
+    return NULL;
+}
+
+#endif
diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h
new file mode 100644 (file)
index 0000000..6c7e7a5
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef fooutf8hfoo
+#define fooutf8hfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+
+/** \file
+ * UTF8 Validation functions
+ */
+
+PA_C_DECL_BEGIN
+
+/** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */
+char *pa_utf8_valid(const char *str) PA_GCC_PURE;
+
+/** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */
+char *pa_utf8_filter(const char *str);
+
+/** Convert a UTF-8 string to the current locale. Free the string using pa_xfree(). */
+char* pa_utf8_to_locale (const char *str);
+
+/** Convert a string in the current locale to UTF-8. Free the string using pa_xfree(). */
+char* pa_locale_to_utf8 (const char *str);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/util.c b/src/pulse/util.c
new file mode 100644 (file)
index 0000000..c0911b5
--- /dev/null
@@ -0,0 +1,263 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/winsock.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "util.h"
+
+char *pa_get_user_name(char *s, size_t l) {
+    const char *p;
+    char buf[1024];
+
+#ifdef HAVE_PWD_H
+    struct passwd pw, *r;
+#endif
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    if (!(p = (getuid() == 0 ? "root" : NULL)) &&
+        !(p = getenv("USER")) &&
+        !(p = getenv("LOGNAME")) &&
+        !(p = getenv("USERNAME"))) {
+#ifdef HAVE_PWD_H
+
+#ifdef HAVE_GETPWUID_R
+        if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
+#else
+        /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
+            * that do not support getpwuid_r. */
+        if ((r = getpwuid(getuid())) == NULL) {
+#endif
+            pa_snprintf(s, l, "%lu", (unsigned long) getuid());
+            return s;
+        }
+
+        p = r->pw_name;
+
+#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */
+        DWORD size = sizeof(buf);
+
+        if (!GetUserName(buf, &size))
+            return NULL;
+
+        p = buf;
+
+#else /* HAVE_PWD_H */
+        return NULL;
+#endif /* HAVE_PWD_H */
+    }
+
+    return pa_strlcpy(s, p, l);
+}
+
+char *pa_get_host_name(char *s, size_t l) {
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    if (gethostname(s, l) < 0) {
+        pa_log("gethostname(): %s", pa_cstrerror(errno));
+        return NULL;
+    }
+
+    s[l-1] = 0;
+    return s;
+}
+
+char *pa_get_home_dir(char *s, size_t l) {
+    char *e;
+
+#ifdef HAVE_PWD_H
+    char buf[1024];
+    struct passwd pw, *r;
+#endif
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    if ((e = getenv("HOME")))
+        return pa_strlcpy(s, e, l);
+
+    if ((e = getenv("USERPROFILE")))
+        return pa_strlcpy(s, e, l);
+
+#ifdef HAVE_PWD_H
+#ifdef HAVE_GETPWUID_R
+    if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
+        pa_log("getpwuid_r() failed");
+#else
+    /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
+        * that do not support getpwuid_r. */
+    if ((r = getpwuid(getuid())) == NULL) {
+        pa_log("getpwuid_r() failed");
+#endif
+        return NULL;
+    }
+
+    return pa_strlcpy(s, r->pw_dir, l);
+#else /* HAVE_PWD_H */
+    return NULL;
+#endif
+}
+
+char *pa_get_binary_name(char *s, size_t l) {
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+#if defined(OS_IS_WIN32)
+    {
+        char path[PATH_MAX];
+
+        if (GetModuleFileName(NULL, path, PATH_MAX))
+            return pa_strlcpy(s, pa_path_get_filename(path), l);
+    }
+#endif
+
+#ifdef __linux__
+    {
+        char *rp;
+        /* This works on Linux only */
+
+        if ((rp = pa_readlink("/proc/self/exe"))) {
+            pa_strlcpy(s, pa_path_get_filename(rp), l);
+            pa_xfree(rp);
+            return s;
+        }
+    }
+
+#endif
+
+#if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME)
+    {
+
+        #ifndef TASK_COMM_LEN
+        /* Actually defined in linux/sched.h */
+        #define TASK_COMM_LEN 16
+        #endif
+
+        char tcomm[TASK_COMM_LEN+1];
+        memset(tcomm, 0, sizeof(tcomm));
+
+        /* This works on Linux only */
+        if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0)
+            return pa_strlcpy(s, tcomm, l);
+
+    }
+#endif
+
+    return NULL;
+}
+
+char *pa_path_get_filename(const char *p) {
+    char *fn;
+
+    pa_assert(p);
+
+    if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
+        return fn+1;
+
+    return (char*) p;
+}
+
+char *pa_get_fqdn(char *s, size_t l) {
+    char hn[256];
+#ifdef HAVE_GETADDRINFO
+    struct addrinfo *a, hints;
+#endif
+
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    if (!pa_get_host_name(hn, sizeof(hn)))
+        return NULL;
+
+#ifdef HAVE_GETADDRINFO
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_flags = AI_CANONNAME;
+
+    if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname)
+        return pa_strlcpy(s, hn, l);
+
+    pa_strlcpy(s, a->ai_canonname, l);
+    freeaddrinfo(a);
+    return s;
+#else
+    return pa_strlcpy(s, hn, l);
+#endif
+}
+
+int pa_msleep(unsigned long t) {
+#ifdef OS_IS_WIN32
+    Sleep(t);
+    return 0;
+#elif defined(HAVE_NANOSLEEP)
+    struct timespec ts;
+
+    ts.tv_sec = t/1000;
+    ts.tv_nsec = (t % 1000) * 1000000;
+
+    return nanosleep(&ts, NULL);
+#else
+#error "Platform lacks a sleep function."
+#endif
+}
diff --git a/src/pulse/util.h b/src/pulse/util.h
new file mode 100644 (file)
index 0000000..cf06d4f
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <stddef.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+
+/** \file
+ * Assorted utility functions */
+
+PA_C_DECL_BEGIN
+
+/** Return the current username in the specified string buffer. */
+char *pa_get_user_name(char *s, size_t l);
+
+/** Return the current hostname in the specified buffer. */
+char *pa_get_host_name(char *s, size_t l);
+
+/** Return the fully qualified domain name in s */
+char *pa_get_fqdn(char *s, size_t l);
+
+/** Return the home directory of the current user */
+char *pa_get_home_dir(char *s, size_t l);
+
+/** Return the binary file name of the current process. This is not
+ * supported on all architectures, in which case NULL is returned. */
+char *pa_get_binary_name(char *s, size_t l);
+
+/** Return a pointer to the filename inside a path (which is the last
+ * component). */
+char *pa_path_get_filename(const char *p);
+
+/** Wait t milliseconds */
+int pa_msleep(unsigned long t);
+
+PA_C_DECL_END
+
+#endif
similarity index 59%
rename from polyp/polyplib-version.h.in
rename to src/pulse/version.h.in
index 89e0a0e506fa6ecf68f28e7a76a23445e1281805..e6226c44580176fab0d48a10b37a9286f81a888e 100644 (file)
@@ -1,28 +1,31 @@
-#ifndef foopolyplibversionhfoo /*-*-C-*-*/
-#define foopolyplibversionhfoo
-
-/* $Id$ */
+#ifndef fooversionhfoo /*-*-C-*-*/
+#define fooversionhfoo
 
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-/* WARNING: Make sure to edit the real source file polyplib-version.h.in! */
+/* WARNING: Make sure to edit the real source file version.h.in! */
+
+#include <pulse/cdecl.h>
 
 /** \file
  * Define header version */
@@ -34,14 +37,19 @@ a macro and not a function, so it is impossible to get the pointer of
 it. */
 #define pa_get_headers_version() ("@PACKAGE_VERSION@")
 
-/** Return the version of the library the current application is linked to. */
+/** Return the version of the library the current application is
+ * linked to. */
 const char* pa_get_library_version(void);
 
-/** The current API version. Version 6 relates to polypaudio
+/** The current API version. Version 6 relates to Polypaudio
  * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have
  * PA_API_VERSION undefined.  */
 #define PA_API_VERSION @PA_API_VERSION@
 
+/** The current protocol version. Version 8 relates to Polypaudio
+ * 0.8/PulseAudio 0.9. */
+#define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@
+
 PA_C_DECL_END
 
 #endif
similarity index 50%
rename from polyp/volume.c
rename to src/pulse/volume.c
index bb9d30db3fab3acfca9b34620a805ad19afffd1f..70d6f86a99110ba770e68558dca848bd673bcf0e 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <config.h>
 #endif
 
-#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
 
 #include "volume.h"
 
-int pa_cvolume_equal(const struct pa_cvolume *a, const struct pa_cvolume *b) {
+int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
     int i;
-    assert(a);
-    assert(b);
+    pa_assert(a);
+    pa_assert(b);
 
     if (a->channels != b->channels)
         return 0;
-    
+
     for (i = 0; i < a->channels; i++)
         if (a->values[i] != b->values[i])
             return 0;
@@ -42,30 +46,25 @@ int pa_cvolume_equal(const struct pa_cvolume *a, const struct pa_cvolume *b) {
     return 1;
 }
 
-void pa_cvolume_set(struct pa_cvolume *a, pa_volume_t v) {
+pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
     int i;
-    assert(a);
 
-    a->channels = PA_CHANNELS_MAX;
+    pa_assert(a);
+    pa_assert(channels > 0);
+    pa_assert(channels <= PA_CHANNELS_MAX);
+
+    a->channels = channels;
 
     for (i = 0; i < a->channels; i++)
         a->values[i] = v;
-}
-
-void pa_cvolume_reset(struct pa_cvolume *a) {
-    assert(a);
-    pa_cvolume_set(a, PA_VOLUME_NORM);
-}
 
-void pa_cvolume_mute(struct pa_cvolume *a) {
-    assert(a);
-    pa_cvolume_set(a, PA_VOLUME_MUTED);
+    return a;
 }
 
-pa_volume_t pa_cvolume_avg(const struct pa_cvolume *a) {
+pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
     uint64_t sum = 0;
     int i;
-    assert(a);
+    pa_assert(a);
 
     for (i = 0; i < a->channels; i++)
         sum += a->values[i];
@@ -76,17 +75,13 @@ pa_volume_t pa_cvolume_avg(const struct pa_cvolume *a) {
 }
 
 pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
-    uint64_t p = a;
-    p *= b;
-    p /= PA_VOLUME_NORM;
-
     return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b));
 }
 
-#define USER_DECIBEL_RANGE 30
+#define USER_DECIBEL_RANGE 60
 
 pa_volume_t pa_sw_volume_from_dB(double dB) {
-    if (dB <= -USER_DECIBEL_RANGE)
+    if (isinf(dB) < 0 || dB <= -USER_DECIBEL_RANGE)
         return PA_VOLUME_MUTED;
 
     return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM);
@@ -104,7 +99,7 @@ pa_volume_t pa_sw_volume_from_linear(double v) {
     if (v <= 0)
         return PA_VOLUME_MUTED;
 
-    if (v == 1)
+    if (v > .999 && v < 1.001)
         return PA_VOLUME_NORM;
 
     return pa_sw_volume_from_dB(20*log10(v));
@@ -116,46 +111,68 @@ double pa_sw_volume_to_linear(pa_volume_t v) {
         return 0;
 
     return pow(10, pa_sw_volume_to_dB(v)/20);
-    
 }
 
-char *pa_cvolume_snprintf(char *s, size_t l, const struct pa_cvolume *c, unsigned channels) {
-    unsigned c;
+char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
+    unsigned channel;
     int first = 1;
-    
-    assert(s);
-    assert(l > 0);
-    assert(c);
+    char *e;
 
-    if (channels > PA_CHANNELS_MAX || channels <= 0)
-        channels = PA_CHANNELS_MAX;
+    pa_assert(s);
+    pa_assert(l > 0);
+    pa_assert(c);
 
-    *s = 0;
+    *(e = s) = 0;
 
-    for (c = 0; c < channels && l > 1; c++) {
-        l -= snprintf(s, l, "%s%u: %3u%%",
+    for (channel = 0; channel < c->channels && l > 1; channel++) {
+        l -= pa_snprintf(e, l, "%s%u: %3u%%",
                       first ? "" : " ",
-                      c,
-                      (c->channels[c]*100)/PA_VOLUME_NORM);
+                      channel,
+                      (c->values[channel]*100)/PA_VOLUME_NORM);
 
-        s = strchr(s, 0);
+        e = strchr(e, 0);
+        first = 0;
     }
 
     return s;
 }
 
-
 /** Return non-zero if the volume of all channels is equal to the specified value */
-int pa_cvolume_channels_equal_to(const struct pa_cvolume *a, uint8_t channels, pa_volume_t v) {
+int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
     unsigned c;
-    assert(a);
-
-    if (channels > PA_CHANNELS_MAX)
-        channels = PA_CHANNELS_MAX;
+    pa_assert(a);
 
-    for (c = 0; c < channels; c++)
-        if (a->map[c] != v)
+    for (c = 0; c < a->channels; c++)
+        if (a->values[c] != v)
             return 0;
 
     return 1;
 }
+
+pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
+    unsigned i;
+
+    pa_assert(dest);
+    pa_assert(a);
+    pa_assert(b);
+
+    for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) {
+
+        dest->values[i] = pa_sw_volume_multiply(
+            i < a->channels ? a->values[i] : PA_VOLUME_NORM,
+            i < b->channels ? b->values[i] : PA_VOLUME_NORM);
+    }
+
+    dest->channels = i;
+
+    return dest;
+}
+
+int pa_cvolume_valid(const pa_cvolume *v) {
+    pa_assert(v);
+
+    if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX)
+        return 0;
+
+    return 1;
+}
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
new file mode 100644 (file)
index 0000000..3befb1d
--- /dev/null
@@ -0,0 +1,175 @@
+#ifndef foovolumehfoo
+#define foovolumehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
+#include <pulse/sample.h>
+
+/** \page volume Volume Control
+ *
+ * \section overv_sec Overview
+ *
+ * Sinks, sources, sink inputs and samples can all have their own volumes.
+ * To deal with these, The PulseAudio libray contains a number of functions
+ * that ease handling.
+ *
+ * The basic volume type in PulseAudio is the \ref pa_volume_t type. Most of
+ * the time, applications will use the aggregated pa_cvolume structure that
+ * can store the volume of all channels at once.
+ *
+ * Volumes commonly span between muted (0%), and normal (100%). It is possible
+ * to set volumes to higher than 100%, but clipping might occur.
+ *
+ * \section calc_sec Calculations
+ *
+ * The volumes in PulseAudio are logarithmic in nature and applications
+ * shouldn't perform calculations with them directly. Instead, they should
+ * be converted to and from either dB or a linear scale:
+ *
+ * \li dB - pa_sw_volume_from_dB() / pa_sw_volume_to_dB()
+ * \li Linear - pa_sw_volume_from_linear() / pa_sw_volume_to_linear()
+ *
+ * For simple multiplication, pa_sw_volume_multiply() and
+ * pa_sw_cvolume_multiply() can be used.
+ *
+ * Calculations can only be reliably performed on software volumes
+ * as it is commonly unknown what scale hardware volumes relate to.
+ *
+ * The functions described above are only valid when used with
+ * software volumes. Hence it is usually a better idea to treat all
+ * volume values as opaque with a range from PA_VOLUME_MUTE (0%) to
+ * PA_VOLUME_NORM (100%) and to refrain from any calculations with
+ * them.
+ *
+ * \section conv_sec Convenience Functions
+ *
+ * To handle the pa_cvolume structure, the PulseAudio library provides a
+ * number of convenienc functions:
+ *
+ * \li pa_cvolume_valid() - Tests if a pa_cvolume structure is valid.
+ * \li pa_cvolume_equal() - Tests if two pa_cvolume structures are identical.
+ * \li pa_cvolume_channels_equal_to() - Tests if all channels of a pa_cvolume
+ *                             structure have a given volume.
+ * \li pa_cvolume_is_muted() - Tests if all channels of a pa_cvolume
+ *                             structure are muted.
+ * \li pa_cvolume_is_norm() - Tests if all channels of a pa_cvolume structure
+ *                            are at a normal volume.
+ * \li pa_cvolume_set() - Set all channels of a pa_cvolume structure to a
+ *                        certain volume.
+ * \li pa_cvolume_reset() - Set all channels of a pa_cvolume structure to a
+ *                          normal volume.
+ * \li pa_cvolume_mute() - Set all channels of a pa_cvolume structure to a
+ *                         muted volume.
+ * \li pa_cvolume_avg() - Return the average volume of all channels.
+ * \li pa_cvolume_snprint() - Pretty print a pa_cvolume structure.
+ */
+
+/** \file
+ * Constants and routines for volume handling */
+
+PA_C_DECL_BEGIN
+
+/** Volume specification:
+ *  PA_VOLUME_MUTED: silence;
+ * < PA_VOLUME_NORM: decreased volume;
+ *   PA_VOLUME_NORM: normal volume;
+ * > PA_VOLUME_NORM: increased volume */
+typedef uint32_t pa_volume_t;
+
+/** Normal volume (100%) */
+#define PA_VOLUME_NORM ((pa_volume_t) 0x10000U)
+
+/** Muted volume (0%) */
+#define PA_VOLUME_MUTED ((pa_volume_t) 0U)
+
+/** A structure encapsulating a per-channel volume */
+typedef struct pa_cvolume {
+    uint8_t channels;                     /**< Number of channels */
+    pa_volume_t values[PA_CHANNELS_MAX];  /**< Per-channel volume  */
+} pa_cvolume;
+
+/** Return non-zero when *a == *b */
+int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE;
+
+/** Set the volume of all channels to PA_VOLUME_NORM */
+#define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM)
+
+/** Set the volume of all channels to PA_VOLUME_MUTED */
+#define pa_cvolume_mute(a, n) pa_cvolume_set((a), (n), PA_VOLUME_MUTED)
+
+/** Set the volume of all channels to the specified parameter */
+pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v);
+
+/** Maximum length of the strings returned by pa_cvolume_snprint() */
+#define PA_CVOLUME_SNPRINT_MAX 64
+
+/** Pretty print a volume structure */
+char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c);
+
+/** Return the average volume of all channels */
+pa_volume_t pa_cvolume_avg(const pa_cvolume *a) PA_GCC_PURE;
+
+/** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */
+int pa_cvolume_valid(const pa_cvolume *v) PA_GCC_PURE;
+
+/** Return non-zero if the volume of all channels is equal to the specified value */
+int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE;
+
+/** Return 1 if the specified volume has all channels muted */
+#define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED)
+
+/** Return 1 if the specified volume has all channels on normal level */
+#define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM)
+
+/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. This is only valid for software volumes! */
+pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST;
+
+/** Multiply to per-channel volumes and return the result in *dest. This is only valid for software volumes! */
+pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
+
+/** Convert a decibel value to a volume. This is only valid for software volumes! */
+pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST;
+
+/** Convert a volume to a decibel value. This is only valid for software volumes! */
+double pa_sw_volume_to_dB(pa_volume_t v) PA_GCC_CONST;
+
+/** Convert a linear factor to a volume. This is only valid for software volumes! */
+pa_volume_t pa_sw_volume_from_linear(double v) PA_GCC_CONST;
+
+/** Convert a volume to a linear factor. This is only valid for software volumes! */
+double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;
+
+#ifdef INFINITY
+#define PA_DECIBEL_MININFTY ((double) -INFINITY)
+#else
+/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). */
+#define PA_DECIBEL_MININFTY ((double) -200.0)
+#endif
+
+PA_C_DECL_END
+
+#endif
similarity index 62%
rename from polyp/xmalloc.c
rename to src/pulse/xmalloc.c
index 7ddefa94fa499085e034cb41af2efa4967ed148e..90237013861f755d1c7b6b4d0bb0259ea795d5c7 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include <stdlib.h>
 #include <signal.h>
-#include <assert.h>
 #include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pulse/gccmacro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
 
-#include "memory.h"
-#include "util.h"
+#include "xmalloc.h"
 
 /* Make sure not to allocate more than this much memory. */
 #define MAX_ALLOC_SIZE (1024*1024*20) /* 20MB */
 /* #undef strndup */
 /* #undef strdup */
 
+static void oom(void) PA_GCC_NORETURN;
+
 /** called in case of an OOM situation. Prints an error message and
  * exits */
 static void oom(void) {
     static const char e[] = "Not enough memory\n";
-    pa_loop_write(STDERR_FILENO, e, sizeof(e)-1);
+    pa_loop_write(STDERR_FILENO, e, sizeof(e)-1, NULL);
+#ifdef SIGQUIT
     raise(SIGQUIT);
+#endif
     _exit(1);
 }
 
 void* pa_xmalloc(size_t size) {
     void *p;
-    assert(size > 0);
-    assert(size < MAX_ALLOC_SIZE);
-    
+    pa_assert(size > 0);
+    pa_assert(size < MAX_ALLOC_SIZE);
+
     if (!(p = malloc(size)))
         oom();
-        
+
     return p;
 }
 
 void* pa_xmalloc0(size_t size) {
     void *p;
-    assert(size > 0);
-    assert(size < MAX_ALLOC_SIZE);
-    
+    pa_assert(size > 0);
+    pa_assert(size < MAX_ALLOC_SIZE);
+
     if (!(p = calloc(1, size)))
         oom();
-        
+
     return p;
 }
-    
+
 void *pa_xrealloc(void *ptr, size_t size) {
     void *p;
-    assert(size > 0);
-    assert(size < MAX_ALLOC_SIZE);
-    
+    pa_assert(size > 0);
+    pa_assert(size < MAX_ALLOC_SIZE);
+
     if (!(p = realloc(ptr, size)))
         oom();
     return p;
@@ -99,18 +107,27 @@ char *pa_xstrdup(const char *s) {
 }
 
 char *pa_xstrndup(const char *s, size_t l) {
+    char *e, *r;
+
     if (!s)
         return NULL;
-    else {
-        char *r;
-        size_t t = strlen(s);
-
-        if (t > l)
-            t = l;
-        
-        r = pa_xmemdup(s, t+1);
-        r[t] = 0;
-        return r;
-    }
+
+    if ((e = memchr(s, 0, l)))
+        return pa_xmemdup(s, e-s+1);
+
+    r = pa_xmalloc(l+1);
+    memcpy(r, s, l);
+    r[l] = 0;
+    return r;
 }
 
+void pa_xfree(void *p) {
+    int saved_errno;
+
+    if (!p)
+        return;
+
+    saved_errno = errno;
+    free(p);
+    errno = saved_errno;
+}
diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h
new file mode 100644 (file)
index 0000000..c453138
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef foomemoryhfoo
+#define foomemoryhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
+#include <pulse/cdecl.h>
+
+/** \file
+ * Memory allocation functions.
+ */
+
+PA_C_DECL_BEGIN
+
+/** Allocate the specified number of bytes, just like malloc() does. However, in case of OOM, terminate */
+void* pa_xmalloc(size_t l);
+
+/** Same as pa_xmalloc(), but initialize allocated memory to 0 */
+void *pa_xmalloc0(size_t l);
+
+/**  The combination of pa_xmalloc() and realloc() */
+void *pa_xrealloc(void *ptr, size_t size);
+
+/** Free allocated memory */
+void pa_xfree(void *p);
+
+/** Duplicate the specified string, allocating memory with pa_xmalloc() */
+char *pa_xstrdup(const char *s);
+
+/** Duplicate the specified string, but truncate after l characters */
+char *pa_xstrndup(const char *s, size_t l);
+
+/** Duplicate the specified memory block */
+void* pa_xmemdup(const void *p, size_t l);
+
+/** Internal helper for pa_xnew() */
+static inline void* pa_xnew_internal(unsigned n, size_t k) {
+    assert(n < INT_MAX/k);
+    return pa_xmalloc(n*k);
+}
+
+/** Allocate n new structures of the specified type. */
+#define pa_xnew(type, n) ((type*) pa_xnew_internal((n), sizeof(type)))
+
+/** Internal helper for pa_xnew0() */
+static inline void* pa_xnew0_internal(unsigned n, size_t k) {
+    assert(n < INT_MAX/k);
+    return pa_xmalloc0(n*k);
+}
+
+/** Same as pa_xnew() but set the memory to zero */
+#define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type)))
+
+/** Internal helper for pa_xnew0() */
+static inline void* pa_xnewdup_internal(const void *p, unsigned n, size_t k) {
+    assert(n < INT_MAX/k);
+    return pa_xmemdup(p, n*k);
+}
+
+/** Same as pa_xnew() but set the memory to zero */
+#define pa_xnewdup(type, p, n) ((type*) pa_xnewdup_internal((p), (n), sizeof(type)))
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulsecore/Makefile b/src/pulsecore/Makefile
new file mode 120000 (symlink)
index 0000000..c110232
--- /dev/null
@@ -0,0 +1 @@
+../pulse/Makefile
\ No newline at end of file
diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
new file mode 100644 (file)
index 0000000..5c7af2a
--- /dev/null
@@ -0,0 +1,319 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/flist.h>
+#include <pulse/xmalloc.h>
+
+#include "asyncmsgq.h"
+
+PA_STATIC_FLIST_DECLARE(asyncmsgq, 0, pa_xfree);
+PA_STATIC_FLIST_DECLARE(semaphores, 0, (void(*)(void*)) pa_semaphore_free);
+
+struct asyncmsgq_item {
+    int code;
+    pa_msgobject *object;
+    void *userdata;
+    pa_free_cb_t free_cb;
+    int64_t offset;
+    pa_memchunk memchunk;
+    pa_semaphore *semaphore;
+    int ret;
+};
+
+struct pa_asyncmsgq {
+    PA_REFCNT_DECLARE;
+    pa_asyncq *asyncq;
+    pa_mutex *mutex; /* only for the writer side */
+
+    struct asyncmsgq_item *current;
+};
+
+pa_asyncmsgq *pa_asyncmsgq_new(unsigned size) {
+    pa_asyncmsgq *a;
+
+    a = pa_xnew(pa_asyncmsgq, 1);
+
+    PA_REFCNT_INIT(a);
+    pa_assert_se(a->asyncq = pa_asyncq_new(size));
+    pa_assert_se(a->mutex = pa_mutex_new(FALSE, TRUE));
+    a->current = NULL;
+
+    return a;
+}
+
+static void asyncmsgq_free(pa_asyncmsgq *a) {
+    struct asyncmsgq_item *i;
+    pa_assert(a);
+
+    while ((i = pa_asyncq_pop(a->asyncq, 0))) {
+
+        pa_assert(!i->semaphore);
+
+        if (i->object)
+            pa_msgobject_unref(i->object);
+
+        if (i->memchunk.memblock)
+            pa_memblock_unref(i->memchunk.memblock);
+
+        if (i->free_cb)
+            i->free_cb(i->userdata);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), i) < 0)
+            pa_xfree(i);
+    }
+
+    pa_asyncq_free(a->asyncq, NULL);
+    pa_mutex_free(a->mutex);
+    pa_xfree(a);
+}
+
+pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q) {
+    pa_assert(PA_REFCNT_VALUE(q) > 0);
+
+    PA_REFCNT_INC(q);
+    return q;
+}
+
+void pa_asyncmsgq_unref(pa_asyncmsgq* q) {
+    pa_assert(PA_REFCNT_VALUE(q) > 0);
+
+    if (PA_REFCNT_DEC(q) <= 0)
+        asyncmsgq_free(q);
+}
+
+void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk, pa_free_cb_t free_cb) {
+    struct asyncmsgq_item *i;
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(asyncmsgq))))
+        i = pa_xnew(struct asyncmsgq_item, 1);
+
+    i->code = code;
+    i->object = object ? pa_msgobject_ref(object) : NULL;
+    i->userdata = (void*) userdata;
+    i->free_cb = free_cb;
+    i->offset = offset;
+    if (chunk) {
+        pa_assert(chunk->memblock);
+        i->memchunk = *chunk;
+        pa_memblock_ref(i->memchunk.memblock);
+    } else
+        pa_memchunk_reset(&i->memchunk);
+    i->semaphore = NULL;
+
+    /* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
+    pa_mutex_lock(a->mutex);
+    pa_asyncq_post(a->asyncq, i);
+    pa_mutex_unlock(a->mutex);
+}
+
+int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk) {
+    struct asyncmsgq_item i;
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    i.code = code;
+    i.object = object;
+    i.userdata = (void*) userdata;
+    i.free_cb = NULL;
+    i.ret = -1;
+    i.offset = offset;
+    if (chunk) {
+        pa_assert(chunk->memblock);
+        i.memchunk = *chunk;
+    } else
+        pa_memchunk_reset(&i.memchunk);
+
+    if (!(i.semaphore = pa_flist_pop(PA_STATIC_FLIST_GET(semaphores))))
+        i.semaphore = pa_semaphore_new(0);
+
+    pa_assert_se(i.semaphore);
+
+    /* Thus mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
+    pa_mutex_lock(a->mutex);
+    pa_assert_se(pa_asyncq_push(a->asyncq, &i, TRUE) == 0);
+    pa_mutex_unlock(a->mutex);
+
+    pa_semaphore_wait(i.semaphore);
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(semaphores), i.semaphore) < 0)
+        pa_semaphore_free(i.semaphore);
+
+    return i.ret;
+}
+
+int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, pa_bool_t wait) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+    pa_assert(!a->current);
+
+    if (!(a->current = pa_asyncq_pop(a->asyncq, wait))) {
+/*         pa_log("failure"); */
+        return -1;
+    }
+
+/*     pa_log("success"); */
+
+    if (code)
+        *code = a->current->code;
+    if (userdata)
+        *userdata = a->current->userdata;
+    if (offset)
+        *offset = a->current->offset;
+    if (object) {
+        if ((*object = a->current->object))
+            pa_msgobject_assert_ref(*object);
+    }
+    if (chunk)
+        *chunk = a->current->memchunk;
+
+/*     pa_log_debug("Get q=%p object=%p (%s) code=%i data=%p chunk.length=%lu", (void*) a, (void*) a->current->object, a->current->object ? a->current->object->parent.type_name : NULL, a->current->code, (void*) a->current->userdata, (unsigned long) a->current->memchunk.length); */
+
+    return 0;
+}
+
+void pa_asyncmsgq_done(pa_asyncmsgq *a, int ret) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+    pa_assert(a);
+    pa_assert(a->current);
+
+    if (a->current->semaphore) {
+        a->current->ret = ret;
+        pa_semaphore_post(a->current->semaphore);
+    } else {
+
+        if (a->current->free_cb)
+            a->current->free_cb(a->current->userdata);
+
+        if (a->current->object)
+            pa_msgobject_unref(a->current->object);
+
+        if (a->current->memchunk.memblock)
+            pa_memblock_unref(a->current->memchunk.memblock);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(asyncmsgq), a->current) < 0)
+            pa_xfree(a->current);
+    }
+
+    a->current = NULL;
+}
+
+int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code) {
+    int c;
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    pa_asyncmsgq_ref(a);
+
+    do {
+        pa_msgobject *o;
+        void *data;
+        int64_t offset;
+        pa_memchunk chunk;
+        int ret;
+
+        if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, 1) < 0)
+            return -1;
+
+        ret = pa_asyncmsgq_dispatch(o, c, data, offset, &chunk);
+        pa_asyncmsgq_done(a, ret);
+
+    } while (c != code);
+
+    pa_asyncmsgq_unref(a);
+
+    return 0;
+}
+
+int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
+    pa_msgobject *object;
+    int code;
+    void *data;
+    pa_memchunk chunk;
+    int64_t offset;
+    int ret;
+
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, 0) < 0)
+        return 0;
+
+    pa_asyncmsgq_ref(a);
+    ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+    pa_asyncmsgq_done(a, ret);
+    pa_asyncmsgq_unref(a);
+
+    return 1;
+}
+
+int pa_asyncmsgq_read_fd(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    return pa_asyncq_read_fd(a->asyncq);
+}
+
+int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    return pa_asyncq_read_before_poll(a->asyncq);
+}
+
+void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    pa_asyncq_read_after_poll(a->asyncq);
+}
+
+int pa_asyncmsgq_write_fd(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    return pa_asyncq_write_fd(a->asyncq);
+}
+
+void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    pa_asyncq_write_before_poll(a->asyncq);
+}
+
+void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a) {
+    pa_assert(PA_REFCNT_VALUE(a) > 0);
+
+    pa_asyncq_write_after_poll(a->asyncq);
+}
+
+int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk) {
+
+    if (object)
+        return object->process_msg(object, code, userdata, offset, memchunk);
+
+    return 0;
+}
diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h
new file mode 100644 (file)
index 0000000..1f38207
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef foopulseasyncmsgqhfoo
+#define foopulseasyncmsgqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/asyncq.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/msgobject.h>
+
+/* A simple asynchronous message queue, based on pa_asyncq. In
+ * contrast to pa_asyncq this one is multiple-writer safe, though
+ * still not multiple-reader safe. This queue is intended to be used
+ * for controlling real-time threads from normal-priority
+ * threads. Multiple-writer-safety is accomplished by using a mutex on
+ * the writer side. This queue is thus not useful for communication
+ * between several real-time threads.
+ *
+ * The queue takes messages consisting of:
+ *    "Object" for which this messages is intended (may be NULL)
+ *    A numeric message code
+ *    Arbitrary userdata pointer (may be NULL)
+ *    A memchunk (may be NULL)
+ *
+ * There are two functions for submitting messages: _post and
+ * _send. The former just enqueues the message asynchronously, the
+ * latter waits for completion, synchronously. */
+
+enum {
+    PA_MESSAGE_SHUTDOWN = -1/* A generic message to inform the handler of this queue to quit */
+};
+
+typedef struct pa_asyncmsgq pa_asyncmsgq;
+
+pa_asyncmsgq* pa_asyncmsgq_new(unsigned size);
+pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q);
+
+void pa_asyncmsgq_unref(pa_asyncmsgq* q);
+
+void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk, pa_free_cb_t userdata_free_cb);
+int pa_asyncmsgq_send(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk);
+
+int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, pa_bool_t wait);
+int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk);
+void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret);
+int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code);
+int pa_asyncmsgq_process_one(pa_asyncmsgq *a);
+
+/* For the reading side */
+int pa_asyncmsgq_read_fd(pa_asyncmsgq *q);
+int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a);
+void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a);
+
+/* For the write side */
+int pa_asyncmsgq_write_fd(pa_asyncmsgq *q);
+void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a);
+void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a);
+
+#endif
diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c
new file mode 100644 (file)
index 0000000..03e9f0d
--- /dev/null
@@ -0,0 +1,321 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/flist.h>
+#include <pulse/xmalloc.h>
+
+#include "asyncq.h"
+#include "fdsem.h"
+
+#define ASYNCQ_SIZE 256
+
+/* For debugging purposes we can define _Y to put an extra thread
+ * yield between each operation. */
+
+/* #define PROFILE */
+
+#ifdef PROFILE
+#define _Y pa_thread_yield()
+#else
+#define _Y do { } while(0)
+#endif
+
+struct localq {
+    void *data;
+    PA_LLIST_FIELDS(struct localq);
+};
+
+struct pa_asyncq {
+    unsigned size;
+    unsigned read_idx;
+    unsigned write_idx;
+    pa_fdsem *read_fdsem, *write_fdsem;
+
+    PA_LLIST_HEAD(struct localq, localq);
+    struct localq *last_localq;
+    pa_bool_t waiting_for_post;
+};
+
+PA_STATIC_FLIST_DECLARE(localq, 0, pa_xfree);
+
+#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
+
+static int reduce(pa_asyncq *l, int value) {
+    return value & (unsigned) (l->size - 1);
+}
+
+pa_asyncq *pa_asyncq_new(unsigned size) {
+    pa_asyncq *l;
+
+    if (!size)
+        size = ASYNCQ_SIZE;
+
+    pa_assert(pa_is_power_of_two(size));
+
+    l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size));
+
+    l->size = size;
+
+    PA_LLIST_HEAD_INIT(struct localq, l->localq);
+    l->last_localq = NULL;
+    l->waiting_for_post = FALSE;
+
+    if (!(l->read_fdsem = pa_fdsem_new())) {
+        pa_xfree(l);
+        return NULL;
+    }
+
+    if (!(l->write_fdsem = pa_fdsem_new())) {
+        pa_fdsem_free(l->read_fdsem);
+        pa_xfree(l);
+        return NULL;
+    }
+
+    return l;
+}
+
+void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
+    struct localq *q;
+    pa_assert(l);
+
+    if (free_cb) {
+        void *p;
+
+        while ((p = pa_asyncq_pop(l, 0)))
+            free_cb(p);
+    }
+
+    while ((q = l->localq)) {
+        if (free_cb)
+            free_cb(q->data);
+
+        PA_LLIST_REMOVE(struct localq, l->localq, q);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
+            pa_xfree(q);
+    }
+
+    pa_fdsem_free(l->read_fdsem);
+    pa_fdsem_free(l->write_fdsem);
+    pa_xfree(l);
+}
+
+static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
+    int idx;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+    pa_assert(p);
+
+    cells = PA_ASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->write_idx);
+
+    if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
+
+        if (!wait)
+            return -1;
+
+/*         pa_log("sleeping on push"); */
+
+        do {
+            pa_fdsem_wait(l->read_fdsem);
+        } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
+    }
+
+    _Y;
+    l->write_idx++;
+
+    pa_fdsem_post(l->write_fdsem);
+
+    return 0;
+}
+
+static pa_bool_t flush_postq(pa_asyncq *l) {
+    struct localq *q;
+
+    pa_assert(l);
+
+    while ((q = l->last_localq)) {
+
+        if (push(l, q->data, FALSE) < 0)
+            return FALSE;
+
+        l->last_localq = q->prev;
+
+        PA_LLIST_REMOVE(struct localq, l->localq, q);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
+            pa_xfree(q);
+    }
+
+    return TRUE;
+}
+
+int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) {
+    pa_assert(l);
+
+    if (!flush_postq(l))
+        return -1;
+
+    return push(l, p, wait);
+}
+
+void pa_asyncq_post(pa_asyncq*l, void *p) {
+    struct localq *q;
+
+    pa_assert(l);
+    pa_assert(p);
+
+    if (pa_asyncq_push(l, p, FALSE) >= 0)
+        return;
+
+    /* OK, we couldn't push anything in the queue. So let's queue it
+     * locally and push it later */
+
+    pa_log("q overrun, queuing locally");
+
+    if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq))))
+        q = pa_xnew(struct localq, 1);
+
+    q->data = p;
+    PA_LLIST_PREPEND(struct localq, l->localq, q);
+
+    if (!l->last_localq)
+        l->last_localq = q;
+
+    return;
+}
+
+void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait) {
+    int idx;
+    void *ret;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+
+    cells = PA_ASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->read_idx);
+
+    if (!(ret = pa_atomic_ptr_load(&cells[idx]))) {
+
+        if (!wait)
+            return NULL;
+
+/*         pa_log("sleeping on pop"); */
+
+        do {
+            pa_fdsem_wait(l->write_fdsem);
+        } while (!(ret = pa_atomic_ptr_load(&cells[idx])));
+    }
+
+    pa_assert(ret);
+
+    /* Guaranteed to succeed if we only have a single reader */
+    pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL));
+
+    _Y;
+    l->read_idx++;
+
+    pa_fdsem_post(l->read_fdsem);
+
+    return ret;
+}
+
+int pa_asyncq_read_fd(pa_asyncq *q) {
+    pa_assert(q);
+
+    return pa_fdsem_get(q->write_fdsem);
+}
+
+int pa_asyncq_read_before_poll(pa_asyncq *l) {
+    int idx;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+
+    cells = PA_ASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->read_idx);
+
+    for (;;) {
+        if (pa_atomic_ptr_load(&cells[idx]))
+            return -1;
+
+        if (pa_fdsem_before_poll(l->write_fdsem) >= 0)
+            return 0;
+    }
+
+    return 0;
+}
+
+void pa_asyncq_read_after_poll(pa_asyncq *l) {
+    pa_assert(l);
+
+    pa_fdsem_after_poll(l->write_fdsem);
+}
+
+int pa_asyncq_write_fd(pa_asyncq *q) {
+    pa_assert(q);
+
+    return pa_fdsem_get(q->read_fdsem);
+}
+
+void pa_asyncq_write_before_poll(pa_asyncq *l) {
+    pa_assert(l);
+
+    for (;;) {
+
+        if (flush_postq(l))
+            break;
+
+        if (pa_fdsem_before_poll(l->read_fdsem) >= 0) {
+            l->waiting_for_post = TRUE;
+            break;
+        }
+    }
+}
+
+void pa_asyncq_write_after_poll(pa_asyncq *l) {
+    pa_assert(l);
+
+    if (l->waiting_for_post) {
+        pa_fdsem_after_poll(l->read_fdsem);
+        l->waiting_for_post = FALSE;
+    }
+}
diff --git a/src/pulsecore/asyncq.h b/src/pulsecore/asyncq.h
new file mode 100644 (file)
index 0000000..e6847ab
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef foopulseasyncqhfoo
+#define foopulseasyncqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+#include <pulse/def.h>
+#include <pulsecore/macro.h>
+
+/* A simple, asynchronous, lock-free (if requested also wait-free)
+ * queue. Not multiple-reader/multiple-writer safe. If that is
+ * required both sides can be protected by a mutex each. --- Which is
+ * not a bad thing in most cases, since this queue is intended for
+ * communication between a normal thread and a single real-time
+ * thread. Only the real-time side needs to be lock-free/wait-free.
+ *
+ * If the queue is full and another entry shall be pushed, or when the
+ * queue is empty and another entry shall be popped and the "wait"
+ * argument is non-zero, the queue will block on a UNIX FIFO object --
+ * that will probably require locking on the kernel side -- which
+ * however is probably not problematic, because we do it only on
+ * starvation or overload in which case we have to block anyway.  */
+
+typedef struct pa_asyncq pa_asyncq;
+
+pa_asyncq* pa_asyncq_new(unsigned size);
+void pa_asyncq_free(pa_asyncq* q, pa_free_cb_t free_cb);
+
+void* pa_asyncq_pop(pa_asyncq *q, pa_bool_t wait);
+int pa_asyncq_push(pa_asyncq *q, void *p, pa_bool_t wait);
+
+/* Similar to pa_asyncq_push(), but if the queue is full, postpone it
+ * locally and delay until pa_asyncq_before_poll_post() */
+void pa_asyncq_post(pa_asyncq*l, void *p);
+
+/* For the reading side */
+int pa_asyncq_read_fd(pa_asyncq *q);
+int pa_asyncq_read_before_poll(pa_asyncq *a);
+void pa_asyncq_read_after_poll(pa_asyncq *a);
+
+/* For the writing side */
+int pa_asyncq_write_fd(pa_asyncq *q);
+void pa_asyncq_write_before_poll(pa_asyncq *a);
+void pa_asyncq_write_after_poll(pa_asyncq *a);
+
+#endif
diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h
new file mode 100644 (file)
index 0000000..a91c4d5
--- /dev/null
@@ -0,0 +1,473 @@
+#ifndef foopulseatomichfoo
+#define foopulseatomichfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+  Copyright 2008 Nokia Corporation
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/*
+ * atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*).  It is
+ * not guaranteed however, that sizeof(AO_t) == sizeof(size_t).
+ * however very likely.
+ *
+ * For now we do only full memory barriers. Eventually we might want
+ * to support more elaborate memory barriers, in which case we will add
+ * suffixes to the function names.
+ *
+ * On gcc >= 4.1 we use the builtin atomic functions. otherwise we use
+ * libatomic_ops
+ */
+#
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifdef HAVE_ATOMIC_BUILTINS
+
+/* __sync based implementation */
+
+typedef struct pa_atomic {
+    volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    __sync_synchronize();
+    return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    a->value = i;
+    __sync_synchronize();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    return __sync_fetch_and_add(&a->value, i);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    return __sync_fetch_and_sub(&a->value, i);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return pa_atomic_add(a, 1);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return pa_atomic_sub(a, 1);
+}
+
+/* Returns non-zero when the operation was successful. */
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    return __sync_bool_compare_and_swap(&a->value, old_i, new_i);
+}
+
+typedef struct pa_atomic_ptr {
+    volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    __sync_synchronize();
+    return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    a->value = (unsigned long) p;
+    __sync_synchronize();
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    return __sync_bool_compare_and_swap(&a->value, (long) old_p, (long) new_p);
+}
+
+#elif defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__))
+
+#error "The native atomic operations implementation for AMD64 has not been tested. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: make the native atomic operations implementation for AMD64 work, fix libatomic_ops, or upgrade your GCC."
+
+/* Addapted from glibc */
+
+typedef struct pa_atomic {
+    volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    a->value = i;
+}
+
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    int result;
+
+    __asm __volatile ("lock; xaddl %0, %1"
+                      : "=r" (result), "=m" (a->value)
+                      : "0" (i), "m" (a->value));
+
+    return result;
+}
+
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    return pa_atomic_add(a, -i);
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return pa_atomic_add(a, 1);
+}
+
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return pa_atomic_sub(a, 1);
+}
+
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    int result;
+
+    __asm__ __volatile__ ("lock; cmpxchgl %2, %1"
+                          : "=a" (result), "=m" (a->value)
+                          : "r" (new_i), "m" (a->value), "0" (old_i));
+
+    return result == oldval;
+}
+
+typedef struct pa_atomic_ptr {
+    volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    a->value = (unsigned long) p;
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    void *result;
+
+    __asm__ __volatile__ ("lock; cmpxchgq %q2, %1"
+                          : "=a" (result), "=m" (a->value)
+                          : "r" (new_p), "m" (a->value), "0" (old_p));
+
+    return result;
+}
+
+#elif defined(ATOMIC_ARM_INLINE_ASM)
+
+/*
+   These should only be enabled if we have ARMv6 or better.
+*/
+
+typedef struct pa_atomic {
+    volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline void pa_memory_barrier(void) {
+#ifdef ATOMIC_ARM_MEMORY_BARRIER_ENABLED
+    asm volatile ("mcr  p15, 0, r0, c7, c10, 5  @ dmb");
+#endif
+}
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    pa_memory_barrier();
+    return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    a->value = i;
+    pa_memory_barrier();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    unsigned long not_exclusive;
+    int new_val, old_val;
+
+    pa_memory_barrier();
+    do {
+        asm volatile ("ldrex    %0, [%3]\n"
+                      "add      %2, %0, %4\n"
+                      "strex    %1, %2, [%3]\n"
+                      : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val)
+                      : "r" (&a->value), "Ir" (i)
+                      : "cc");
+    } while(not_exclusive);
+    pa_memory_barrier();
+
+    return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    unsigned long not_exclusive;
+    int new_val, old_val;
+
+    pa_memory_barrier();
+    do {
+        asm volatile ("ldrex    %0, [%3]\n"
+                      "sub      %2, %0, %4\n"
+                      "strex    %1, %2, [%3]\n"
+                      : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val)
+                      : "r" (&a->value), "Ir" (i)
+                      : "cc");
+    } while(not_exclusive);
+    pa_memory_barrier();
+
+    return old_val;
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return pa_atomic_add(a, 1);
+}
+
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return pa_atomic_sub(a, 1);
+}
+
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    unsigned long not_equal, not_exclusive;
+
+    pa_memory_barrier();
+    do {
+        asm volatile ("ldrex    %0, [%2]\n"
+                      "subs     %0, %0, %3\n"
+                      "mov      %1, %0\n"
+                      "strexeq %0, %4, [%2]\n"
+                      : "=&r" (not_exclusive), "=&r" (not_equal)
+                      : "r" (&a->value), "Ir" (old_i), "r" (new_i)
+                      : "cc");
+    } while(not_exclusive && !not_equal);
+    pa_memory_barrier();
+
+    return !not_equal;
+}
+
+typedef struct pa_atomic_ptr {
+    volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    pa_memory_barrier();
+    return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    a->value = (unsigned long) p;
+    pa_memory_barrier();
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    unsigned long not_equal, not_exclusive;
+
+    pa_memory_barrier();
+    do {
+        asm volatile ("ldrex    %0, [%2]\n"
+                      "subs     %0, %0, %3\n"
+                      "mov      %1, %0\n"
+                      "strexeq %0, %4, [%2]\n"
+                      : "=&r" (not_exclusive), "=&r" (not_equal)
+                      : "r" (&a->value), "Ir" (old_p), "r" (new_p)
+                      : "cc");
+    } while(not_exclusive && !not_equal);
+    pa_memory_barrier();
+
+    return !not_equal;
+}
+
+#elif defined(ATOMIC_ARM_LINUX_HELPERS)
+
+/* See file arch/arm/kernel/entry-armv.S in your kernel sources for more
+   information about these functions. The arm kernel helper functions first
+   appeared in 2.6.16.
+   Apply --disable-atomic-arm-linux-helpers flag to confugure if you prefere
+   inline asm implementation or you have an obsolete Linux kernel.
+*/
+/* Memory barrier */
+typedef void (__kernel_dmb_t)(void);
+#define __kernel_dmb (*(__kernel_dmb_t *)0xffff0fa0)
+
+static inline void pa_memory_barrier(void) {
+#ifndef ATOMIC_ARM_MEMORY_BARRIER_ENABLED
+    __kernel_dmb();
+#endif
+}
+
+/* Atomic exchange (__kernel_cmpxchg_t contains memory barriers if needed) */
+typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
+#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
+
+/* This is just to get rid of all warnings */
+typedef int (__kernel_cmpxchg_u_t)(unsigned long oldval, unsigned long newval, volatile unsigned long *ptr);
+#define __kernel_cmpxchg_u (*(__kernel_cmpxchg_u_t *)0xffff0fc0)
+
+typedef struct pa_atomic {
+    volatile int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    pa_memory_barrier();
+    return a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    a->value = i;
+    pa_memory_barrier();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    int old_val;
+    do {
+        old_val = a->value;
+    } while(__kernel_cmpxchg(old_val, old_val + i, &a->value));
+    return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    int old_val;
+    do {
+        old_val = a->value;
+    } while(__kernel_cmpxchg(old_val, old_val - i, &a->value));
+    return old_val;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return pa_atomic_add(a, 1);
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return pa_atomic_sub(a, 1);
+}
+
+/* Returns non-zero when the operation was successful. */
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    pa_bool_t failed;
+    do {
+      failed = !!__kernel_cmpxchg(old_i, new_i, &a->value);
+    } while(failed && a->value == old_i);
+    return !failed;
+}
+
+typedef struct pa_atomic_ptr {
+    volatile unsigned long value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (unsigned long) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    pa_memory_barrier();
+    return (void*) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    a->value = (unsigned long) p;
+    pa_memory_barrier();
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    pa_bool_t failed;
+    do {
+        failed = !!__kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value);
+    } while(failed && a->value == (unsigned long) old_p);
+    return !failed;
+}
+
+#else
+
+/* libatomic_ops based implementation */
+
+#include <atomic_ops.h>
+
+typedef struct pa_atomic {
+    volatile AO_t value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    return (int) AO_load_full((AO_t*) &a->value);
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    AO_store_full(&a->value, (AO_t) i);
+}
+
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    return AO_fetch_and_add_full(&a->value, (AO_t) i);
+}
+
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    return AO_fetch_and_add_full(&a->value, (AO_t) -i);
+}
+
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    return AO_fetch_and_add1_full(&a->value);
+}
+
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    return AO_fetch_and_sub1_full(&a->value);
+}
+
+static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    return AO_compare_and_swap_full(&a->value, old_i, new_i);
+}
+
+typedef struct pa_atomic_ptr {
+    volatile AO_t value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (AO_t) (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    return (void*) AO_load_full((AO_t*) &a->value);
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    AO_store_full(&a->value, (AO_t) p);
+}
+
+static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    return AO_compare_and_swap_full(&a->value, (AO_t) old_p, (AO_t) new_p);
+}
+
+#endif
+
+#endif
diff --git a/src/pulsecore/authkey-prop.c b/src/pulsecore/authkey-prop.c
new file mode 100644 (file)
index 0000000..a953bf7
--- /dev/null
@@ -0,0 +1,106 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/props.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+#include <pulsecore/refcnt.h>
+
+#include "authkey-prop.h"
+
+struct authkey_data {
+    PA_REFCNT_DECLARE;
+    size_t length;
+};
+
+int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) {
+    struct authkey_data *a;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(data);
+    pa_assert(len > 0);
+
+    if (!(a = pa_property_get(c, name)))
+        return -1;
+
+    pa_assert(a->length == len);
+    memcpy(data, (uint8_t*) a + PA_ALIGN(sizeof(struct authkey_data)), len);
+
+    return 0;
+}
+
+int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) {
+    struct authkey_data *a;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    if (pa_property_get(c, name))
+        return -1;
+
+    a = pa_xmalloc(PA_ALIGN(sizeof(struct authkey_data)) + len);
+    PA_REFCNT_INIT(a);
+    a->length = len;
+    memcpy((uint8_t*) a + PA_ALIGN(sizeof(struct authkey_data)), data, len);
+
+    pa_property_set(c, name, a);
+
+    return 0;
+}
+
+void pa_authkey_prop_ref(pa_core *c, const char *name) {
+    struct authkey_data *a;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    a = pa_property_get(c, name);
+    pa_assert(a);
+    pa_assert(PA_REFCNT_VALUE(a) >= 1);
+    PA_REFCNT_INC(a);
+}
+
+void pa_authkey_prop_unref(pa_core *c, const char *name) {
+    struct authkey_data *a;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    a = pa_property_get(c, name);
+    pa_assert(a);
+    pa_assert(PA_REFCNT_VALUE(a) >= 1);
+
+    if (PA_REFCNT_DEC(a) <= 0) {
+        pa_property_remove(c, name);
+        pa_xfree(a);
+    }
+}
+
+
similarity index 65%
rename from polyp/authkey-prop.h
rename to src/pulsecore/authkey-prop.h
index 1b1948c7f87db39c6c62b3f9f6e1121cc2179b7c..de02d2aa5f3ce1644a2d0c32b745abde71fff16e 100644 (file)
@@ -1,43 +1,43 @@
 #ifndef fooauthkeyprophfoo
 #define fooauthkeyprophfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "core.h"
+#include <pulsecore/core.h>
 
 /* The authkey-prop uses a central property to store a previously
  * loaded cookie in memory. Useful for sharing the same cookie between
  * several modules. */
 
 /* Return the data of the specified authorization key property. Doesn't alter the refernce count of the key */
-int pa_authkey_prop_get(struct pa_core *c, const char *name, void *data, size_t len);
+int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len);
 
 /* Store data in the specified authorization key property. The initial reference count is set to 1 */
-int pa_authkey_prop_put(struct pa_core *c, const char *name, const void *data, size_t len);
+int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len);
 
 /* Increase the reference count of the specified authorization key */
-void pa_authkey_prop_ref(struct pa_core *c, const char *name);
+void pa_authkey_prop_ref(pa_core *c, const char *name);
 
 /* Decrease the reference count of the specified authorization key */
-void pa_authkey_prop_unref(struct pa_core *c, const char *name);
+void pa_authkey_prop_unref(pa_core *c, const char *name);
 
 #endif
similarity index 53%
rename from polyp/authkey.c
rename to src/pulsecore/authkey.c
index e16883d389cfb3108cc0659535019adb18670ce0..f3f40f8004d5d6aba3f16b598d435ebb374bb351 100644 (file)
@@ -1,20 +1,21 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
@@ -23,7 +24,6 @@
 #include <config.h>
 #endif
 
-#include <assert.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <string.h>
 #include <limits.h>
 #include <sys/stat.h>
 
+#include <pulse/util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+
 #include "authkey.h"
-#include "util.h"
-#include "log.h"
-#include "random.h"
 
 /* Generate a new authorization key, store it in file fd and return it in *data  */
 static int generate(int fd, void *ret_data, size_t length) {
     ssize_t r;
-    assert(fd >= 0 && ret_data && length);
+
+    pa_assert(fd >= 0);
+    pa_assert(ret_data);
+    pa_assert(length > 0);
 
     pa_random(ret_data, length);
 
     lseek(fd, 0, SEEK_SET);
-    ftruncate(fd, 0);
+    (void) ftruncate(fd, 0);
 
-    if ((r = pa_loop_write(fd, ret_data, length)) < 0 || (size_t) r != length) {
-        pa_log(__FILE__": failed to write cookie file: %s\n", strerror(errno));
+    if ((r = pa_loop_write(fd, ret_data, length, NULL)) < 0 || (size_t) r != length) {
+        pa_log("Failed to write cookie file: %s", pa_cstrerror(errno));
         return -1;
     }
 
     return 0;
 }
 
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
 /* Load an euthorization cookie from file fn and store it in data. If
  * the cookie file doesn't exist, create it */
 static int load(const char *fn, void *data, size_t length) {
     int fd = -1;
     int writable = 1;
-    int unlock = 0, ret;
+    int unlock = 0, ret = -1;
     ssize_t r;
-    assert(fn && data && length);
 
-    if ((fd = open(fn, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
-        if (errno != EACCES || (fd = open(fn, O_RDONLY)) < 0) {
-            pa_log(__FILE__": failed to open cookie file '%s': %s\n", fn, strerror(errno));
+    pa_assert(fn);
+    pa_assert(data);
+    pa_assert(length > 0);
+
+    if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
+
+        if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY|O_NOCTTY)) < 0) {
+            pa_log("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
             goto finish;
         } else
             writable = 0;
@@ -77,32 +96,36 @@ static int load(const char *fn, void *data, size_t length) {
 
     unlock = pa_lock_fd(fd, 1) >= 0;
 
-    if ((r = pa_loop_read(fd, data, length)) < 0) {
-        pa_log(__FILE__": failed to read cookie file '%s': %s\n", fn, strerror(errno));
+    if ((r = pa_loop_read(fd, data, length, NULL)) < 0) {
+        pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
         goto finish;
     }
 
     if ((size_t) r != length) {
-        
+        pa_log_debug("Got %d bytes from cookie file '%s', expected %d", (int) r, fn, (int) length);
+
         if (!writable) {
-            pa_log(__FILE__": unable to write cookie to read only file\n");
+            pa_log("Unable to write cookie to read only file");
             goto finish;
         }
-        
+
         if (generate(fd, data, length) < 0)
             goto finish;
     }
 
     ret = 0;
-    
+
 finish:
 
     if (fd >= 0) {
-        
+
         if (unlock)
             pa_lock_fd(fd, 0);
-        
-        close(fd);
+
+        if (pa_close(fd) < 0) {
+            pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
+            ret = -1;
+        }
     }
 
     return ret;
@@ -112,13 +135,12 @@ finish:
 int pa_authkey_load(const char *path, void *data, size_t length) {
     int ret;
 
-    assert(path && data && length);
-
-    ret = load(path, data, length);
+    pa_assert(path);
+    pa_assert(data);
+    pa_assert(length > 0);
 
-    if (ret < 0)
-        pa_log(__FILE__": Failed to load authorization key '%s': %s\n", path,
-               (ret == -1) ? strerror(errno) : "file corrupt");
+    if ((ret = load(path, data, length)) < 0)
+        pa_log("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");
 
     return ret;
 }
@@ -126,14 +148,26 @@ int pa_authkey_load(const char *path, void *data, size_t length) {
 /* If the specified file path starts with / return it, otherwise
  * return path prepended with home directory */
 static const char *normalize_path(const char *fn, char *s, size_t l) {
-    assert(fn && s && l > 0);
 
+    pa_assert(fn);
+    pa_assert(s);
+    pa_assert(l > 0);
+
+#ifndef OS_IS_WIN32
     if (fn[0] != '/') {
+#else
+    if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
+#endif
         char homedir[PATH_MAX];
+
         if (!pa_get_home_dir(homedir, sizeof(homedir)))
             return NULL;
-        
-        snprintf(s, l, "%s/%s", homedir, fn);
+
+#ifndef OS_IS_WIN32
+        pa_snprintf(s, l, "%s/%s", homedir, fn);
+#else
+        pa_snprintf(s, l, "%s\\%s", homedir, fn);
+#endif
         return s;
     }
 
@@ -145,11 +179,14 @@ static const char *normalize_path(const char *fn, char *s, size_t l) {
 int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
     char path[PATH_MAX];
     const char *p;
-    assert(fn && data && length);
+
+    pa_assert(fn);
+    pa_assert(data);
+    pa_assert(length > 0);
 
     if (!(p = normalize_path(fn, path, sizeof(path))))
         return -2;
-        
+
     return pa_authkey_load(p, data, length);
 }
 
@@ -160,33 +197,39 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {
     ssize_t r;
     char path[PATH_MAX];
     const char *p;
-    assert(fn && data && length);
+
+    pa_assert(fn);
+    pa_assert(data);
+    pa_assert(length > 0);
 
     if (!(p = normalize_path(fn, path, sizeof(path))))
         return -2;
 
-    if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
-        pa_log(__FILE__": failed to open cookie file '%s': %s\n", fn, strerror(errno));
+    if ((fd = open(p, O_RDWR|O_CREAT|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
+        pa_log("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
         goto finish;
     }
 
     unlock = pa_lock_fd(fd, 1) >= 0;
 
-    if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) {
-        pa_log(__FILE__": failed to read cookie file '%s': %s\n", fn, strerror(errno));
+    if ((r = pa_loop_write(fd, data, length, NULL)) < 0 || (size_t) r != length) {
+        pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
         goto finish;
     }
 
     ret = 0;
-    
+
 finish:
 
     if (fd >= 0) {
-        
+
         if (unlock)
             pa_lock_fd(fd, 0);
-        
-        close(fd);
+
+        if (pa_close(fd) < 0) {
+            pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
+            ret = -1;
+        }
     }
 
     return ret;
similarity index 75%
rename from polyp/authkey.h
rename to src/pulsecore/authkey.h
index 81b1a578e4556a5986cc651f6ec6d0dd80f9e22e..8301db1e20c63028b12c4b5fd9018b1885f30e60 100644 (file)
@@ -1,23 +1,23 @@
 #ifndef fooauthkeyhfoo
 #define fooauthkeyhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
similarity index 56%
rename from polyp/autoload.c
rename to src/pulsecore/autoload.c
index 96fa750ae8c3ce952310899ff67e883639baaa8b..26c294b2b0fa9c0686a648310e233d8c1726f2d3 100644 (file)
@@ -1,20 +1,21 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <config.h>
 #endif
 
-#include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sound-file.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/core-subscribe.h>
+
 #include "autoload.h"
-#include "module.h"
-#include "xmalloc.h"
-#include "memchunk.h"
-#include "sound-file.h"
-#include "log.h"
-#include "scache.h"
-#include "subscribe.h"
-
-static void entry_free(struct pa_autoload_entry *e) {
-    assert(e);
+
+static void entry_free(pa_autoload_entry *e) {
+    pa_assert(e);
     pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX);
     pa_xfree(e->name);
     pa_xfree(e->module);
@@ -45,31 +48,34 @@ static void entry_free(struct pa_autoload_entry *e) {
     pa_xfree(e);
 }
 
-static void entry_remove_and_free(struct pa_autoload_entry *e) {
-    assert(e && e->core);
+static void entry_remove_and_free(pa_autoload_entry *e) {
+    pa_assert(e);
+    pa_assert(e->core);
 
     pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
     pa_hashmap_remove(e->core->autoload_hashmap, e->name);
     entry_free(e);
 }
 
-static struct pa_autoload_entry* entry_new(struct pa_core *c, const char *name) {
-    struct pa_autoload_entry *e = NULL;
-    assert(c && name);
-    
+static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
+    pa_autoload_entry *e = NULL;
+
+    pa_core_assert_ref(c);
+    pa_assert(name);
+
     if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name)))
         return NULL;
-    
-    e = pa_xmalloc(sizeof(struct pa_autoload_entry));
+
+    e = pa_xnew(pa_autoload_entry, 1);
     e->core = c;
     e->name = pa_xstrdup(name);
     e->module = e->argument = NULL;
     e->in_action = 0;
-    
+
     if (!c->autoload_hashmap)
         c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    assert(c->autoload_hashmap);
-    
+    pa_assert(c->autoload_hashmap);
+
     pa_hashmap_put(c->autoload_hashmap, e->name, e);
 
     if (!c->autoload_idxset)
@@ -77,30 +83,37 @@ static struct pa_autoload_entry* entry_new(struct pa_core *c, const char *name)
     pa_idxset_put(c->autoload_idxset, e, &e->index);
 
     pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index);
-    
+
     return e;
 }
 
-int pa_autoload_add(struct pa_core *c, const char*name, enum pa_namereg_type type, const char*module, const char *argument, uint32_t *index) {
-    struct pa_autoload_entry *e = NULL;
-    assert(c && name && module && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
-    
+int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) {
+    pa_autoload_entry *e = NULL;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(module);
+    pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
+
     if (!(e = entry_new(c, name)))
         return -1;
-        
+
     e->module = pa_xstrdup(module);
     e->argument = pa_xstrdup(argument);
     e->type = type;
 
-    if (index)
-        *index = e->index;
-    
+    if (idx)
+        *idx = e->index;
+
     return 0;
 }
 
-int pa_autoload_remove_by_name(struct pa_core *c, const char*name, enum pa_namereg_type type) {
-    struct pa_autoload_entry *e;
-    assert(c && name && type);
+int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
+    pa_autoload_entry *e;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
 
     if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
         return -1;
@@ -109,21 +122,25 @@ int pa_autoload_remove_by_name(struct pa_core *c, const char*name, enum pa_namer
     return 0;
 }
 
-int pa_autoload_remove_by_index(struct pa_core *c, uint32_t index) {
-    struct pa_autoload_entry *e;
-    assert(c && index != PA_IDXSET_INVALID);
+int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
+    pa_autoload_entry *e;
+
+    pa_assert(c);
+    pa_assert(idx != PA_IDXSET_INVALID);
 
-    if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, index)))
+    if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
         return -1;
 
     entry_remove_and_free(e);
     return 0;
 }
 
-void pa_autoload_request(struct pa_core *c, const char *name, enum pa_namereg_type type) {
-    struct pa_autoload_entry *e;
-    struct pa_module *m;
-    assert(c && name);
+void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
+    pa_autoload_entry *e;
+    pa_module *m;
+
+    pa_assert(c);
+    pa_assert(name);
 
     if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type))
         return;
@@ -137,43 +154,48 @@ void pa_autoload_request(struct pa_core *c, const char *name, enum pa_namereg_ty
         if ((m = pa_module_load(c, e->module, e->argument)))
             m->auto_unload = 1;
     }
-    
+
     e->in_action = 0;
 }
 
-static void free_func(void *p, void *userdata) {
-    struct pa_autoload_entry *e = p;
+static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
+    pa_autoload_entry *e = p;
     pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
     entry_free(e);
 }
 
-void pa_autoload_free(struct pa_core *c) {
+void pa_autoload_free(pa_core *c) {
+
     if (c->autoload_hashmap) {
         pa_hashmap_free(c->autoload_hashmap, free_func, NULL);
         c->autoload_hashmap = NULL;
     }
-    
+
     if (c->autoload_idxset) {
         pa_idxset_free(c->autoload_idxset, NULL, NULL);
         c->autoload_idxset = NULL;
     }
 }
 
-const struct pa_autoload_entry* pa_autoload_get_by_name(struct pa_core *c, const char*name, enum pa_namereg_type type) {
-    struct pa_autoload_entry *e;
-    assert(c && name);
-    
+const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
+    pa_autoload_entry *e;
+
+    pa_core_assert_ref(c);
+    pa_assert(name);
+
     if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
         return NULL;
 
     return e;
 }
 
-const struct pa_autoload_entry* pa_autoload_get_by_index(struct pa_core *c, uint32_t index) {
-    struct pa_autoload_entry *e;
-    assert(c && index != PA_IDXSET_INVALID);
-    
-    if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, index)))
+const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) {
+    pa_autoload_entry *e;
+
+    pa_core_assert_ref(c);
+    pa_assert(idx != PA_IDXSET_INVALID);
+
+    if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
         return NULL;
 
     return e;
similarity index 51%
rename from polyp/autoload.h
rename to src/pulsecore/autoload.h
index 23475684563f332efe85b30392d40b92fe73c28d..3926351f9389c794efa2d6f98c7ac2aa0ebaf49f 100644 (file)
@@ -1,28 +1,28 @@
 #ifndef fooautoloadhfoo
 #define fooautoloadhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "namereg.h"
+#include <pulsecore/namereg.h>
 
 /* Using the autoloading facility, modules by be loaded on-demand and
  * synchronously. The user may register a "ghost sink" or "ghost
  * specified module is loaded. */
 
 /* An autoload entry, or "ghost" sink/source */
-struct pa_autoload_entry {
-    struct pa_core *core;
+typedef struct pa_autoload_entry {
+    pa_core *core;
     uint32_t index;
     char *name;
-    enum pa_namereg_type type; /* Type of the autoload entry */
-    int in_action; /* Currently loaded */
-    char *module, *argument;   
-};
+    pa_namereg_type_t type; /* Type of the autoload entry */
+    int in_action; /* The module is currently being loaded */
+    char *module, *argument;
+} pa_autoload_entry;
 
 /* Add a new autoload entry of the given time, with the speicified
  * sink/source name, module name and argument. Return the entry's
  * index in *index */
-int pa_autoload_add(struct pa_core *c, const char*name, enum pa_namereg_type type, const char*module, const char *argument, uint32_t *index);
+int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx);
 
 /* Free all autoload entries */
-void pa_autoload_free(struct pa_core *c);
-int pa_autoload_remove_by_name(struct pa_core *c, const char*name, enum pa_namereg_type type);
-int pa_autoload_remove_by_index(struct pa_core *c, uint32_t index);
+void pa_autoload_free(pa_core *c);
+int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
+int pa_autoload_remove_by_index(pa_core *c, uint32_t idx);
 
 /* Request an autoload entry by its name, effectively causing a module to be loaded */
-void pa_autoload_request(struct pa_core *c, const char *name, enum pa_namereg_type type);
+void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type);
 
-const struct pa_autoload_entry* pa_autoload_get_by_name(struct pa_core *c, const char*name, enum pa_namereg_type type);
-const struct pa_autoload_entry* pa_autoload_get_by_index(struct pa_core *c, uint32_t index);
+const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
+const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx);
 
 #endif
diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c
new file mode 100644 (file)
index 0000000..d5f40d8
--- /dev/null
@@ -0,0 +1,195 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "avahi-wrap.h"
+
+typedef struct {
+    AvahiPoll api;
+    pa_mainloop_api *mainloop;
+} pa_avahi_poll;
+
+struct AvahiWatch {
+    pa_io_event *io_event;
+    pa_avahi_poll *avahi_poll;
+    AvahiWatchEvent current_event;
+    AvahiWatchCallback callback;
+    void *userdata;
+};
+
+static AvahiWatchEvent translate_io_flags_back(pa_io_event_flags_t e) {
+    return
+        (e & PA_IO_EVENT_INPUT ? AVAHI_WATCH_IN : 0) |
+        (e & PA_IO_EVENT_OUTPUT ? AVAHI_WATCH_OUT : 0) |
+        (e & PA_IO_EVENT_ERROR ? AVAHI_WATCH_ERR : 0) |
+        (e & PA_IO_EVENT_HANGUP ? AVAHI_WATCH_HUP : 0);
+}
+
+static pa_io_event_flags_t translate_io_flags(AvahiWatchEvent e) {
+    return
+        (e & AVAHI_WATCH_IN ? PA_IO_EVENT_INPUT : 0) |
+        (e & AVAHI_WATCH_OUT ? PA_IO_EVENT_OUTPUT : 0) |
+        (e & AVAHI_WATCH_ERR ? PA_IO_EVENT_ERROR : 0) |
+        (e & AVAHI_WATCH_HUP ? PA_IO_EVENT_HANGUP : 0);
+}
+
+static void watch_callback(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
+    AvahiWatch *w = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(w);
+
+    w->current_event = translate_io_flags_back(events);
+    w->callback(w, fd, w->current_event, w->userdata);
+    w->current_event = 0;
+}
+
+static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) {
+    pa_avahi_poll *p;
+    AvahiWatch *w;
+
+    pa_assert(api);
+    pa_assert(fd >= 0);
+    pa_assert(callback);
+    pa_assert_se(p = api->userdata);
+
+    w = pa_xnew(AvahiWatch, 1);
+    w->avahi_poll = p;
+    w->current_event = 0;
+    w->callback = callback;
+    w->userdata = userdata;
+    w->io_event = p->mainloop->io_new(p->mainloop, fd, translate_io_flags(event), watch_callback, w);
+
+    return w;
+}
+
+static void watch_update(AvahiWatch *w, AvahiWatchEvent event) {
+    pa_assert(w);
+
+    w->avahi_poll->mainloop->io_enable(w->io_event, translate_io_flags(event));
+}
+
+static AvahiWatchEvent watch_get_events(AvahiWatch *w) {
+    pa_assert(w);
+
+    return w->current_event;
+}
+
+static void watch_free(AvahiWatch *w) {
+    pa_assert(w);
+
+    w->avahi_poll->mainloop->io_free(w->io_event);
+    pa_xfree(w);
+}
+
+struct AvahiTimeout {
+    pa_time_event *time_event;
+    pa_avahi_poll *avahi_poll;
+    AvahiTimeoutCallback callback;
+    void *userdata;
+};
+
+static void timeout_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+    AvahiTimeout *t = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(t);
+
+    t->callback(t, t->userdata);
+}
+
+static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) {
+    pa_avahi_poll *p;
+    AvahiTimeout *t;
+
+    pa_assert(api);
+    pa_assert(callback);
+    pa_assert_se(p = api->userdata);
+
+    t = pa_xnew(AvahiTimeout, 1);
+    t->avahi_poll = p;
+    t->callback = callback;
+    t->userdata = userdata;
+
+    t->time_event = tv ? p->mainloop->time_new(p->mainloop, tv, timeout_callback, t) : NULL;
+
+    return t;
+}
+
+static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
+    pa_assert(t);
+
+    if (t->time_event && tv)
+        t->avahi_poll->mainloop->time_restart(t->time_event, tv);
+    else if (!t->time_event && tv)
+        t->time_event = t->avahi_poll->mainloop->time_new(t->avahi_poll->mainloop, tv, timeout_callback, t);
+    else if (t->time_event && !tv) {
+        t->avahi_poll->mainloop->time_free(t->time_event);
+        t->time_event = NULL;
+    }
+}
+
+static void timeout_free(AvahiTimeout *t) {
+    pa_assert(t);
+
+    if (t->time_event)
+        t->avahi_poll->mainloop->time_free(t->time_event);
+    pa_xfree(t);
+}
+
+AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *m) {
+    pa_avahi_poll *p;
+
+    pa_assert(m);
+
+    p = pa_xnew(pa_avahi_poll, 1);
+
+    p->api.userdata = p;
+    p->api.watch_new = watch_new;
+    p->api.watch_update = watch_update;
+    p->api.watch_get_events = watch_get_events;
+    p->api.watch_free = watch_free;
+    p->api.timeout_new = timeout_new;
+    p->api.timeout_update = timeout_update;
+    p->api.timeout_free = timeout_free;
+    p->mainloop = m;
+
+    return &p->api;
+}
+
+void pa_avahi_poll_free(AvahiPoll *api) {
+    pa_avahi_poll *p;
+    pa_assert(api);
+    pa_assert_se(p = api->userdata);
+
+    pa_xfree(p);
+}
+
diff --git a/src/pulsecore/avahi-wrap.h b/src/pulsecore/avahi-wrap.h
new file mode 100644 (file)
index 0000000..7d8995b
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef fooavahiwrapperhfoo
+#define fooavahiwrapperhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <avahi-client/client.h>
+
+#include <pulse/mainloop-api.h>
+
+AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *api);
+void pa_avahi_poll_free(AvahiPoll *p);
+
+#endif
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
new file mode 100644 (file)
index 0000000..cbdb602
--- /dev/null
@@ -0,0 +1,1528 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ltdl.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/tokenizer.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/cli-text.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/sound-file.h>
+#include <pulsecore/play-memchunk.h>
+#include <pulsecore/autoload.h>
+#include <pulsecore/sound-file-stream.h>
+#include <pulsecore/props.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+
+#include "cli-command.h"
+
+struct command {
+    const char *name;
+    int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, pa_bool_t *fail);
+    const char *help;
+    unsigned args;
+};
+
+#define META_INCLUDE ".include"
+#define META_FAIL ".fail"
+#define META_NOFAIL ".nofail"
+#define META_IFEXISTS ".ifexists"
+#define META_ELSE ".else"
+#define META_ENDIF ".endif"
+
+enum {
+    IFSTATE_NONE = -1,
+    IFSTATE_FALSE = 0,
+    IFSTATE_TRUE = 1,
+};
+
+/* Prototypes for all available commands */
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+
+/* A method table for all available commands */
+
+static const struct command commands[] = {
+    { "exit",                    pa_cli_command_exit,               "Terminate the daemon",         1 },
+    { "help",                    pa_cli_command_help,               "Show this help",               1 },
+    { "list-modules",            pa_cli_command_modules,            "List loaded modules",          1 },
+    { "list-sinks",              pa_cli_command_sinks,              "List loaded sinks",            1 },
+    { "list-sources",            pa_cli_command_sources,            "List loaded sources",          1 },
+    { "list-clients",            pa_cli_command_clients,            "List loaded clients",          1 },
+    { "list-sink-inputs",        pa_cli_command_sink_inputs,        "List sink inputs",             1 },
+    { "list-source-outputs",     pa_cli_command_source_outputs,     "List source outputs",          1 },
+    { "stat",                    pa_cli_command_stat,               "Show memory block statistics", 1 },
+    { "info",                    pa_cli_command_info,               "Show comprehensive status",    1 },
+    { "ls",                      pa_cli_command_info,               NULL,                           1 },
+    { "list",                    pa_cli_command_info,               NULL,                           1 },
+    { "load-module",             pa_cli_command_load,               "Load a module (args: name, arguments)", 3},
+    { "unload-module",           pa_cli_command_unload,             "Unload a module (args: index)", 2},
+    { "describe-module",         pa_cli_command_describe,           "Describe a module (arg: name)", 2},
+    { "set-sink-volume",         pa_cli_command_sink_volume,        "Set the volume of a sink (args: index|name, volume)", 3},
+    { "set-sink-input-volume",   pa_cli_command_sink_input_volume,  "Set the volume of a sink input (args: index, volume)", 3},
+    { "set-source-volume",       pa_cli_command_source_volume,      "Set the volume of a source (args: index|name, volume)", 3},
+    { "set-sink-mute",           pa_cli_command_sink_mute,          "Set the mute switch of a sink (args: index|name, bool)", 3},
+    { "set-sink-input-mute",     pa_cli_command_sink_input_mute,    "Set the mute switch of a sink input (args: index, bool)", 3},
+    { "set-source-mute",         pa_cli_command_source_mute,        "Set the mute switch of a source (args: index|name, bool)", 3},
+    { "set-default-sink",        pa_cli_command_sink_default,       "Set the default sink (args: index|name)", 2},
+    { "set-default-source",      pa_cli_command_source_default,     "Set the default source (args: index|name)", 2},
+    { "kill-client",             pa_cli_command_kill_client,        "Kill a client (args: index)", 2},
+    { "kill-sink-input",         pa_cli_command_kill_sink_input,    "Kill a sink input (args: index)", 2},
+    { "kill-source-output",      pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
+    { "list-samples",            pa_cli_command_scache_list,        "List all entries in the sample cache", 1},
+    { "play-sample",             pa_cli_command_scache_play,        "Play a sample from the sample cache (args: name, sink|index)", 3},
+    { "remove-sample",           pa_cli_command_scache_remove,      "Remove a sample from the sample cache (args: name)", 2},
+    { "load-sample",             pa_cli_command_scache_load,        "Load a sound file into the sample cache (args: name, filename)", 3},
+    { "load-sample-lazy",        pa_cli_command_scache_load,        "Lazily load a sound file into the sample cache (args: name, filename)", 3},
+    { "load-sample-dir-lazy",    pa_cli_command_scache_load_dir,    "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
+    { "play-file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3},
+    { "list-autoload",           pa_cli_command_autoload_list,      "List autoload entries", 1},
+    { "add-autoload-sink",       pa_cli_command_autoload_add,       NULL /*"Add autoload entry for a sink (args: sink, module name, arguments)"*/, 4},
+    { "add-autoload-source",     pa_cli_command_autoload_add,       NULL /*"Add autoload entry for a source (args: source, module name, arguments)"*/, 4},
+    { "remove-autoload-sink",    pa_cli_command_autoload_remove,    NULL /*"Remove autoload entry for a sink (args: name)"*/, 2},
+    { "remove-autoload-source",  pa_cli_command_autoload_remove,    NULL /*"Remove autoload entry for a source (args: name)"*/, 2},
+    { "dump",                    pa_cli_command_dump,               "Dump daemon configuration", 1},
+    { "list-props",              pa_cli_command_list_props,         NULL, 1},
+    { "move-sink-input",         pa_cli_command_move_sink_input,    "Move sink input to another sink (args: index, sink)", 3},
+    { "move-source-output",      pa_cli_command_move_source_output, "Move source output to another source (args: index, source)", 3},
+    { "vacuum",                  pa_cli_command_vacuum,             NULL, 1},
+    { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},
+    { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},
+    { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 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) {
+    uint32_t idx;
+
+    if (pa_atou(n, &idx) < 0)
+        return (uint32_t) PA_IDXSET_INVALID;
+
+    return idx;
+}
+
+static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    c->mainloop->quit(c->mainloop, 0);
+    return 0;
+}
+
+static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const struct command*command;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_strbuf_puts(buf, "Available commands:\n");
+
+    for (command = commands; command->name; command++)
+        if (command->help)
+            pa_strbuf_printf(buf, "    %-25s %s\n", command->name, command->help);
+    return 0;
+}
+
+static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_module_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_client_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_sink_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_source_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_sink_input_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_source_output_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
+static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    char s[256];
+    const pa_mempool_stat *stat;
+    unsigned k;
+    const char *def_sink, *def_source;
+
+    static const char* const type_table[PA_MEMBLOCK_TYPE_MAX] = {
+        [PA_MEMBLOCK_POOL] = "POOL",
+        [PA_MEMBLOCK_POOL_EXTERNAL] = "POOL_EXTERNAL",
+        [PA_MEMBLOCK_APPENDED] = "APPENDED",
+        [PA_MEMBLOCK_USER] = "USER",
+        [PA_MEMBLOCK_FIXED] = "FIXED",
+        [PA_MEMBLOCK_IMPORTED] = "IMPORTED",
+    };
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    stat = pa_mempool_get_stat(c->mempool);
+
+    pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
+                     (unsigned) pa_atomic_load(&stat->n_allocated),
+                     pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->allocated_size)));
+
+    pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
+                     (unsigned) pa_atomic_load(&stat->n_accumulated),
+                     pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->accumulated_size)));
+
+    pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",
+                     (unsigned) pa_atomic_load(&stat->n_imported),
+                     pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->imported_size)));
+
+    pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",
+                     (unsigned) pa_atomic_load(&stat->n_exported),
+                     pa_bytes_snprint(s, sizeof(s), (size_t) pa_atomic_load(&stat->exported_size)));
+
+    pa_strbuf_printf(buf, "Total sample cache size: %s.\n",
+                     pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c)));
+
+    pa_strbuf_printf(buf, "Default sample spec: %s\n",
+                     pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec));
+
+    def_sink = pa_namereg_get_default_sink_name(c);
+    def_source = pa_namereg_get_default_source_name(c);
+    pa_strbuf_printf(buf, "Default sink name: %s\n"
+                     "Default source name: %s\n",
+                     def_sink ? def_sink : "none",
+                     def_source ? def_source : "none");
+
+    for (k = 0; k < PA_MEMBLOCK_TYPE_MAX; k++)
+        pa_strbuf_printf(buf,
+                         "Memory blocks of type %s: %u allocated/%u accumulated.\n",
+                         type_table[k],
+                         (unsigned) pa_atomic_load(&stat->n_allocated_by_type[k]),
+                         (unsigned) pa_atomic_load(&stat->n_accumulated_by_type[k]));
+
+    return 0;
+}
+
+static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_cli_command_stat(c, t, buf, fail);
+    pa_cli_command_modules(c, t, buf, fail);
+    pa_cli_command_sinks(c, t, buf, fail);
+    pa_cli_command_sources(c, t, buf, fail);
+    pa_cli_command_clients(c, t, buf, fail);
+    pa_cli_command_sink_inputs(c, t, buf, fail);
+    pa_cli_command_source_outputs(c, t, buf, fail);
+    pa_cli_command_scache_list(c, t, buf, fail);
+/*     pa_cli_command_autoload_list(c, t, buf, fail); */
+    return 0;
+}
+
+static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    pa_module *m;
+    const char *name;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(name = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
+        return -1;
+    }
+
+    if (!(m = pa_module_load(c, name,  pa_tokenizer_get(t, 2)))) {
+        pa_strbuf_puts(buf, "Module load failed.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    pa_module *m;
+    uint32_t idx;
+    const char *i;
+    char *e;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(i = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify the module index.\n");
+        return -1;
+    }
+
+    idx = (uint32_t) strtoul(i, &e, 10);
+    if (*e || !(m = pa_idxset_get_by_index(c->modules, idx))) {
+        pa_strbuf_puts(buf, "Invalid module index.\n");
+        return -1;
+    }
+
+    pa_module_unload_request(m);
+    return 0;
+}
+
+static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *name;
+    pa_modinfo *i;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(name = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify the module name.\n");
+        return -1;
+    }
+
+    if ((i = pa_modinfo_get_by_name(name))) {
+
+        pa_strbuf_printf(buf, "Name: %s\n", name);
+
+        if (!i->description && !i->version && !i->author && !i->usage)
+            pa_strbuf_printf(buf, "No module information available\n");
+        else {
+            if (i->version)
+                pa_strbuf_printf(buf, "Version: %s\n", i->version);
+            if (i->description)
+                pa_strbuf_printf(buf, "Description: %s\n", i->description);
+            if (i->author)
+                pa_strbuf_printf(buf, "Author: %s\n", i->author);
+            if (i->usage)
+                pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
+            pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
+        }
+
+        pa_modinfo_free(i);
+    } else
+        pa_strbuf_puts(buf, "Failed to open module.\n");
+
+    return 0;
+}
+
+static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *v;
+    pa_sink *sink;
+    uint32_t volume;
+    pa_cvolume cvolume;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    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, 0x10000 is normal volume)\n");
+        return -1;
+    }
+
+    if (pa_atou(v, &volume) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse volume.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
+    pa_sink_set_volume(sink, &cvolume);
+    return 0;
+}
+
+static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *v;
+    pa_sink_input *si;
+    pa_volume_t volume;
+    pa_cvolume cvolume;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
+        return -1;
+    }
+
+    if (pa_atou(v, &volume) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse volume.\n");
+        return -1;
+    }
+
+    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No sink input found with this index.\n");
+        return -1;
+    }
+
+    pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
+    pa_sink_input_set_volume(si, &cvolume);
+    return 0;
+}
+
+static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *v;
+    pa_source *source;
+    uint32_t volume;
+    pa_cvolume cvolume;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    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 (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
+        return -1;
+    }
+
+    if (pa_atou(v, &volume) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse volume.\n");
+        return -1;
+    }
+
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+        pa_strbuf_puts(buf, "No source found by this name or index.\n");
+        return -1;
+    }
+
+    pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
+    pa_source_set_volume(source, &cvolume);
+    return 0;
+}
+
+static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *m;
+    pa_sink *sink;
+    int mute;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    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 (!(m = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((mute = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    pa_sink_set_mute(sink, mute);
+    return 0;
+}
+
+static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *m;
+    pa_source *source;
+    int mute;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    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 (!(m = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((mute = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
+        return -1;
+    }
+
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    pa_source_set_mute(source, mute);
+    return 0;
+}
+
+static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *v;
+    pa_sink_input *si;
+    uint32_t idx;
+    int mute;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(v = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((mute = pa_parse_boolean(v)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
+        return -1;
+    }
+
+    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No sink input found with this index.\n");
+        return -1;
+    }
+
+    pa_sink_input_set_mute(si, mute);
+    return 0;
+}
+
+static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+        return -1;
+    }
+
+    pa_namereg_set_default(c, n, PA_NAMEREG_SINK);
+    return 0;
+}
+
+static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+        return -1;
+    }
+
+    pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE);
+    return 0;
+}
+
+static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n;
+    pa_client *client;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(client = pa_idxset_get_by_index(c->clients, idx))) {
+        pa_strbuf_puts(buf, "No client found by this index.\n");
+        return -1;
+    }
+
+    pa_client_kill(client);
+    return 0;
+}
+
+static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n;
+    pa_sink_input *sink_input;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) {
+        pa_strbuf_puts(buf, "No sink input found by this index.\n");
+        return -1;
+    }
+
+    pa_sink_input_kill(sink_input);
+    return 0;
+}
+
+static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n;
+    pa_source_output *source_output;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) {
+        pa_strbuf_puts(buf, "No source output found by this index.\n");
+        return -1;
+    }
+
+    pa_source_output_kill(source_output);
+    return 0;
+}
+
+static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_scache_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+
+    return 0;
+}
+
+static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *sink_name;
+    pa_sink *sink;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
+        pa_strbuf_puts(buf, "No sink by that name.\n");
+        return -1;
+    }
+
+    if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM, NULL, &idx) < 0) {
+        pa_strbuf_puts(buf, "Failed to play sample.\n");
+        return -1;
+    }
+
+    pa_strbuf_printf(buf, "Playing on sink input #%i\n", idx);
+
+    return 0;
+}
+
+static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sample name.\n");
+        return -1;
+    }
+
+    if (pa_scache_remove_item(c, n) < 0) {
+        pa_strbuf_puts(buf, "Failed to remove sample.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *fname, *n;
+    int r;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
+        return -1;
+    }
+
+    if (strstr(pa_tokenizer_get(t, 0), "lazy"))
+        r = pa_scache_add_file_lazy(c, n, fname, NULL);
+    else
+        r = pa_scache_add_file(c, n, fname, NULL);
+
+    if (r < 0)
+        pa_strbuf_puts(buf, "Failed to load sound file.\n");
+
+    return 0;
+}
+
+static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *pname;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(pname = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a path name.\n");
+        return -1;
+    }
+
+    if (pa_scache_add_directory_lazy(c, pname) < 0) {
+        pa_strbuf_puts(buf, "Failed to load directory.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *fname, *sink_name;
+    pa_sink *sink;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
+        pa_strbuf_puts(buf, "No sink by that name.\n");
+        return -1;
+    }
+
+
+    return pa_play_file(sink, fname, NULL);
+}
+
+static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *a, *b;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
+    if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
+        return -1;
+    }
+
+    pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL);
+
+    return 0;
+}
+
+static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *name;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
+    if (!(name = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a device name\n");
+        return -1;
+    }
+
+    if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) {
+        pa_strbuf_puts(buf, "Failed to remove autload entry\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
+
+    pa_assert_se(s = pa_autoload_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+
+    return 0;
+}
+
+static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_property_dump(c, buf);
+    return 0;
+}
+
+static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_mempool_vacuum(c->mempool);
+
+    return 0;
+}
+
+static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *k;
+    pa_sink_input *si;
+    pa_sink *sink;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(k = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a sink.\n");
+        return -1;
+    }
+
+    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No sink input found with this index.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK, 1))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    if (pa_sink_input_move_to(si, sink) < 0) {
+        pa_strbuf_puts(buf, "Moved failed.\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *k;
+    pa_source_output *so;
+    pa_source *source;
+    uint32_t idx;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(k = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a source.\n");
+        return -1;
+    }
+
+    if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No source output found with this index.\n");
+        return -1;
+    }
+
+    if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE, 1))) {
+        pa_strbuf_puts(buf, "No source found by this name or index.\n");
+        return -1;
+    }
+
+    if (pa_source_output_move_to(so, source) < 0) {
+        pa_strbuf_puts(buf, "Moved failed.\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *m;
+    pa_sink *sink;
+    int suspend;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    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 (!(m = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((suspend = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    pa_sink_suspend(sink, suspend);
+    return 0;
+}
+
+static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *m;
+    pa_source *source;
+    int suspend;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    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 (!(m = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((suspend = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+        return -1;
+    }
+
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+        pa_strbuf_puts(buf, "No source found by this name or index.\n");
+        return -1;
+    }
+
+    pa_source_suspend(source, suspend);
+    return 0;
+}
+
+static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *m;
+    int suspend;
+    int ret;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(m = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
+        return -1;
+    }
+
+    if ((suspend = pa_parse_boolean(m)) < 0) {
+        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
+        return -1;
+    }
+
+    ret = - (pa_sink_suspend_all(c, suspend) < 0);
+    if (pa_source_suspend_all(c, suspend) < 0)
+        ret = -1;
+
+    if (ret < 0)
+        pa_strbuf_puts(buf, "Failed to resume/suspend all sinks/sources.\n");
+
+    return 0;
+}
+
+static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    pa_module *m;
+    pa_sink *sink;
+    pa_source *source;
+    int nl;
+    const char *p;
+    uint32_t idx;
+    char txt[256];
+    time_t now;
+    void *i;
+    pa_autoload_entry *a;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    time(&now);
+
+#ifdef HAVE_CTIME_R
+    pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt));
+#else
+    pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
+#endif
+
+    for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
+        if (m->auto_unload)
+            continue;
+
+        pa_strbuf_printf(buf, "load-module %s", m->name);
+
+        if (m->argument)
+            pa_strbuf_printf(buf, " %s", m->argument);
+
+        pa_strbuf_puts(buf, "\n");
+    }
+
+    nl = 0;
+
+    for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
+        if (sink->module && sink->module->auto_unload)
+            continue;
+
+        if (!nl) {
+            pa_strbuf_puts(buf, "\n");
+            nl = 1;
+        }
+
+        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink)));
+        pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink)));
+        pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
+    }
+
+    for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
+        if (source->module && source->module->auto_unload)
+            continue;
+
+        if (!nl) {
+            pa_strbuf_puts(buf, "\n");
+            nl = 1;
+        }
+
+        pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source)));
+        pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source)));
+        pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
+    }
+
+
+    if (c->autoload_hashmap) {
+        nl = 0;
+
+        i = NULL;
+        while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) {
+
+            if (!nl) {
+                pa_strbuf_puts(buf, "\n");
+                nl = 1;
+            }
+
+            pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module);
+
+            if (a->argument)
+                pa_strbuf_printf(buf, " %s", a->argument);
+
+            pa_strbuf_puts(buf, "\n");
+        }
+    }
+
+    nl = 0;
+
+    if ((p = pa_namereg_get_default_sink_name(c))) {
+        if (!nl) {
+            pa_strbuf_puts(buf, "\n");
+            nl = 1;
+        }
+        pa_strbuf_printf(buf, "set-default-sink %s\n", p);
+    }
+
+    if ((p = pa_namereg_get_default_source_name(c))) {
+        if (!nl) {
+            pa_strbuf_puts(buf, "\n");
+            nl = 1;
+        }
+        pa_strbuf_printf(buf, "set-default-source %s\n", p);
+    }
+
+    pa_strbuf_puts(buf, "\n### EOF\n");
+
+    return 0;
+}
+
+int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail, int *ifstate) {
+    const char *cs;
+
+    pa_assert(c);
+    pa_assert(s);
+    pa_assert(buf);
+
+    cs = s+strspn(s, whitespace);
+
+    if (*cs == '#' || !*cs)
+        return 0;
+    else if (*cs == '.') {
+        if (!strcmp(cs, META_ELSE)) {
+            if (!ifstate || *ifstate == IFSTATE_NONE) {
+                pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
+                return -1;
+            } else if (*ifstate == IFSTATE_TRUE)
+                *ifstate = IFSTATE_FALSE;
+            else
+                *ifstate = IFSTATE_TRUE;
+            return 0;
+        } else if (!strcmp(cs, META_ENDIF)) {
+            if (!ifstate || *ifstate == IFSTATE_NONE) {
+                pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
+                return -1;
+            } else
+                *ifstate = IFSTATE_NONE;
+            return 0;
+        }
+        if (ifstate && *ifstate == IFSTATE_FALSE)
+            return 0;
+        if (!strcmp(cs, META_FAIL))
+            *fail = TRUE;
+        else if (!strcmp(cs, META_NOFAIL))
+            *fail = FALSE;
+        else {
+            size_t l;
+            l = strcspn(cs, whitespace);
+
+            if (l == sizeof(META_INCLUDE)-1 && !strncmp(cs, META_INCLUDE, l)) {
+                const char *filename = cs+l+strspn(cs+l, whitespace);
+                if (pa_cli_command_execute_file(c, filename, buf, fail) < 0)
+                    if (*fail)
+                        return -1;
+            } else if (l == sizeof(META_IFEXISTS)-1 && !strncmp(cs, META_IFEXISTS, l)) {
+                if (!ifstate) {
+                    pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
+                    return -1;
+                } else if (*ifstate != IFSTATE_NONE) {
+                    pa_strbuf_printf(buf, "Nested %s commands not supported\n", cs);
+                    return -1;
+                } else {
+                    const char *filename = cs+l+strspn(cs+l, whitespace);
+
+                    /* Search DL_SEARCH_PATH unless the filename is absolute */
+                    if (filename[0] == PA_PATH_SEP_CHAR) {
+
+                        *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
+                        pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+
+                    } else {
+                        const char *paths, *state = NULL;
+                        char *p;
+
+                        if (!(paths = lt_dlgetsearchpath()))
+                            return -1;
+
+                        while ((p = pa_split(paths, ":", &state))) {
+                            char *pathname;
+
+                            pathname = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", p, filename);
+                            pa_xfree(p);
+
+                            *ifstate = access(pathname, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
+                            pa_log_debug("Checking for existance of '%s': %s", pathname, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+
+                            pa_xfree(pathname);
+
+                            if (*ifstate == IFSTATE_TRUE)
+                                break;
+                        }
+                    }
+
+                }
+            } 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;
+
+        if (ifstate && *ifstate == IFSTATE_FALSE)
+             return 0;
+
+        l = strcspn(cs, whitespace);
+
+        for (command = commands; command->name; command++)
+            if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
+                int ret;
+                pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
+                pa_assert(t);
+                ret = command->proc(c, t, buf, fail);
+                pa_tokenizer_free(t);
+                unknown = 0;
+
+                if (ret < 0 && *fail)
+                    return -1;
+
+                break;
+            }
+
+        if (unknown) {
+            pa_strbuf_printf(buf, "Unknown command: %s\n", cs);
+            if (*fail)
+                return -1;
+        }
+    }
+
+    return 0;
+}
+
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
+    return pa_cli_command_execute_line_stateful(c, s, buf, fail, NULL);
+}
+
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) {
+    char line[1024];
+    int ifstate = IFSTATE_NONE;
+    int ret = -1;
+    pa_bool_t _fail = TRUE;
+
+    pa_assert(c);
+    pa_assert(f);
+    pa_assert(buf);
+
+    if (!fail)
+        fail = &_fail;
+
+    while (fgets(line, sizeof(line), f)) {
+        pa_strip_nl(line);
+
+        if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
+            goto fail;
+    }
+
+    ret = 0;
+
+fail:
+
+    return ret;
+}
+
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) {
+    FILE *f = NULL;
+    int ret = -1;
+    pa_bool_t _fail = TRUE;
+
+    pa_assert(c);
+    pa_assert(fn);
+    pa_assert(buf);
+
+    if (!fail)
+        fail = &_fail;
+
+    if (!(f = fopen(fn, "r"))) {
+        pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
+        if (!*fail)
+            ret = 0;
+        goto fail;
+    }
+
+    ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
+
+    ret = 0;
+
+fail:
+    if (f)
+        fclose(f);
+
+    return ret;
+}
+
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *p;
+    int ifstate = IFSTATE_NONE;
+    pa_bool_t _fail = TRUE;
+
+    pa_assert(c);
+    pa_assert(s);
+    pa_assert(buf);
+
+    if (!fail)
+        fail = &_fail;
+
+    p = s;
+    while (*p) {
+        size_t l = strcspn(p, linebreak);
+        char *line = pa_xstrndup(p, l);
+
+        if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail) {
+            pa_xfree(line);
+            return -1;
+        }
+        pa_xfree(line);
+
+        p += l;
+        p += strspn(p, linebreak);
+    }
+
+    return 0;
+}
similarity index 50%
rename from polyp/cli-command.h
rename to src/pulsecore/cli-command.h
index 7af9d014b78e3348d9052a98e7fd5b4dd854dfae..9bf35dc330fc928842d86931a083b5a8338bddd1 100644 (file)
@@ -1,40 +1,46 @@
 #ifndef fooclicommandhfoo
 #define fooclicommandhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; 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"
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core.h>
 
 /* Execute a single CLI command. Write the results to the string
  * buffer *buf. If *fail is non-zero the function will return -1 when
  * one or more of the executed commands failed. *fail
  * may be modified by the function call. */
-int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail);
+int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail);
+
+/* Execute a whole file of CLI commands */
+int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail);
 
 /* Execute a whole file of CLI commands */
-int pa_cli_command_execute_file(struct pa_core *c, const char *fn, struct pa_strbuf *buf, int *fail);
+int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail);
 
 /* Split the specified string into lines and run pa_cli_command_execute_line() for each. */
-int pa_cli_command_execute(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail);
+int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail);
+
+/* Same as pa_cli_command_execute_line() but also take ifstate var. */
+int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail, int *ifstate);
 
 #endif
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
new file mode 100644 (file)
index 0000000..c92fca2
--- /dev/null
@@ -0,0 +1,520 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/volume.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/autoload.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "cli-text.h"
+
+char *pa_module_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_module *m;
+    uint32_t idx = PA_IDXSET_INVALID;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules));
+
+    for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
+        pa_strbuf_printf(s, "    index: %u\n"
+                         "\tname: <%s>\n"
+                         "\targument: <%s>\n"
+                         "\tused: %i\n"
+                         "\tauto unload: %s\n",
+                         m->index, m->name, m->argument ? m->argument : "", m->n_used,
+                         pa_yes_no(m->auto_unload));
+    }
+
+    return pa_strbuf_tostring_free(s);
+}
+
+char *pa_client_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_client *client;
+    uint32_t idx = PA_IDXSET_INVALID;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
+
+    for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) {
+        char *t;
+        pa_strbuf_printf(
+                s,
+                "    index: %u\n"
+                "\tdriver: <%s>\n",
+                client->index,
+                client->driver);
+
+        if (client->module)
+            pa_strbuf_printf(s, "\towner module: %u\n", client->module->index);
+
+        t = pa_proplist_to_string(client->proplist);
+        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_tostring_free(s);
+}
+
+char *pa_sink_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_sink *sink;
+    uint32_t idx = PA_IDXSET_INVALID;
+    static const char* const state_table[] = {
+        [PA_SINK_INIT] = "INIT",
+        [PA_SINK_RUNNING] = "RUNNING",
+        [PA_SINK_SUSPENDED] = "SUSPENDED",
+        [PA_SINK_IDLE] = "IDLE",
+        [PA_SINK_UNLINKED] = "UNLINKED"
+    };
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
+
+    for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
+        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
+        pa_usec_t min_latency, max_latency;
+
+        pa_sink_get_latency_range(sink, &min_latency, &max_latency);
+
+        pa_strbuf_printf(
+            s,
+            "  %c index: %u\n"
+            "\tname: <%s>\n"
+            "\tdriver: <%s>\n"
+            "\tflags: %s%s%s%s%s%s\n"
+            "\tstate: %s\n"
+            "\tvolume: %s\n"
+            "\tmuted: %s\n"
+            "\tcurrent latency: %0.2f ms\n"
+            "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
+            "\tmax request: %lu KiB\n"
+            "\tmax rewind: %lu KiB\n"
+            "\tmonitor source: %u\n"
+            "\tsample spec: %s\n"
+            "\tchannel map: %s\n"
+            "\tused by: %u\n"
+            "\tlinked by: %u\n",
+            c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
+            sink->index,
+            sink->name,
+            sink->driver,
+            sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
+            sink->flags & PA_SINK_NETWORK ? "NETWORK " : "",
+            sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
+            sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+            sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
+            sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
+            state_table[pa_sink_get_state(sink)],
+            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)),
+            pa_yes_no(pa_sink_get_mute(sink)),
+            (double) pa_sink_get_latency(sink) / PA_USEC_PER_MSEC,
+            (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC,
+            (unsigned long) pa_sink_get_max_request(sink) / 1024,
+            (unsigned long) pa_sink_get_max_rewind(sink) / 1024,
+            sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
+            pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
+            pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
+            pa_sink_used_by(sink),
+            pa_sink_linked_by(sink));
+
+        if (sink->module)
+            pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);
+
+        t = pa_proplist_to_string(sink->proplist);
+        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_tostring_free(s);
+}
+
+char *pa_source_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_source *source;
+    uint32_t idx = PA_IDXSET_INVALID;
+    static const char* const state_table[] = {
+        [PA_SOURCE_INIT] = "INIT",
+        [PA_SOURCE_RUNNING] = "RUNNING",
+        [PA_SOURCE_SUSPENDED] = "SUSPENDED",
+        [PA_SOURCE_IDLE] = "IDLE",
+        [PA_SOURCE_UNLINKED] = "UNLINKED"
+    };
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));
+
+    for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
+        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], *t;
+        pa_usec_t min_latency, max_latency;
+
+        pa_source_get_latency_range(source, &min_latency, &max_latency);
+
+        pa_strbuf_printf(
+            s,
+            "  %c index: %u\n"
+            "\tname: <%s>\n"
+            "\tdriver: <%s>\n"
+            "\tflags: %s%s%s%s%s%s\n"
+            "\tstate: %s\n"
+            "\tvolume: %s\n"
+            "\tmuted: %s\n"
+            "\tcurrent latency: %0.2f ms\n"
+            "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
+            "\tmax rewind: %lu KiB\n"
+            "\tsample spec: %s\n"
+            "\tchannel map: %s\n"
+            "\tused by: %u\n"
+            "\tlinked by: %u\n",
+            c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
+            source->index,
+            source->name,
+            source->driver,
+            source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
+            source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
+            source->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
+            source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+            source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
+            source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
+            state_table[pa_source_get_state(source)],
+            pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)),
+            pa_yes_no(pa_source_get_mute(source)),
+            (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
+            (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC,
+            (unsigned long) pa_source_get_max_rewind(source) / 1024,
+            pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
+            pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
+            pa_source_used_by(source),
+            pa_source_linked_by(source));
+
+        if (source->monitor_of)
+            pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
+        if (source->module)
+            pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index);
+
+        t = pa_proplist_to_string(source->proplist);
+        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_tostring_free(s);
+}
+
+
+char *pa_source_output_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_source_output *o;
+    uint32_t idx = PA_IDXSET_INVALID;
+    static const char* const state_table[] = {
+        [PA_SOURCE_OUTPUT_INIT] = "INIT",
+        [PA_SOURCE_OUTPUT_RUNNING] = "RUNNING",
+        [PA_SOURCE_OUTPUT_CORKED] = "CORKED",
+        [PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED"
+    };
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs));
+
+    for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {
+        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+        pa_usec_t cl;
+
+        if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1)
+            pa_snprintf(clt, sizeof(clt), "n/a");
+        else
+            pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
+
+        pa_assert(o->source);
+
+        pa_strbuf_printf(
+            s,
+            "    index: %u\n"
+            "\tdriver: <%s>\n"
+            "\tflags: %s%s%s%s%s%s%s%s\n"
+            "\tstate: %s\n"
+            "\tsource: %u <%s>\n"
+            "\tcurrent latency: %0.2f ms\n"
+            "\trequested latency: %s\n"
+            "\tsample spec: %s\n"
+            "\tchannel map: %s\n"
+            "\tresample method: %s\n",
+            o->index,
+            o->driver,
+            o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
+            o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "",
+            o->flags & PA_SOURCE_OUTPUT_START_CORKED ? "START_CORKED " : "",
+            o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "",
+            o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "",
+            o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+            o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
+            o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
+            state_table[pa_source_output_get_state(o)],
+            o->source->index, o->source->name,
+            (double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
+            clt,
+            pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
+            pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
+            pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
+        if (o->module)
+            pa_strbuf_printf(s, "\towner module: %u\n", o->module->index);
+        if (o->client)
+            pa_strbuf_printf(s, "\tclient: %u <%s>\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME)));
+        if (o->direct_on_input)
+            pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index);
+
+        t = pa_proplist_to_string(o->proplist);
+        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_tostring_free(s);
+}
+
+char *pa_sink_input_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_sink_input *i;
+    uint32_t idx = PA_IDXSET_INVALID;
+    static const char* const state_table[] = {
+        [PA_SINK_INPUT_INIT] = "INIT",
+        [PA_SINK_INPUT_RUNNING] = "RUNNING",
+        [PA_SINK_INPUT_DRAINED] = "DRAINED",
+        [PA_SINK_INPUT_CORKED] = "CORKED",
+        [PA_SINK_INPUT_UNLINKED] = "UNLINKED"
+    };
+
+    pa_assert(c);
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
+
+    for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
+        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+        pa_usec_t cl;
+
+        if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1)
+            pa_snprintf(clt, sizeof(clt), "n/a");
+        else
+            pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
+
+        pa_assert(i->sink);
+
+        pa_strbuf_printf(
+            s,
+            "    index: %u\n"
+            "\tdriver: <%s>\n"
+            "\tflags: %s%s%s%s%s%s%s%s\n"
+            "\tstate: %s\n"
+            "\tsink: %u <%s>\n"
+            "\tvolume: %s\n"
+            "\tmuted: %s\n"
+            "\tcurrent latency: %0.2f ms\n"
+            "\trequested latency: %s\n"
+            "\tsample spec: %s\n"
+            "\tchannel map: %s\n"
+            "\tresample method: %s\n",
+            i->index,
+            i->driver,
+            i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
+            i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "",
+            i->flags & PA_SINK_INPUT_START_CORKED ? "START_CORKED " : "",
+            i->flags & PA_SINK_INPUT_NO_REMAP ? "NO_REMAP " : "",
+            i->flags & PA_SINK_INPUT_NO_REMIX ? "NO_REMIX " : "",
+            i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+            i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
+            i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
+            state_table[pa_sink_input_get_state(i)],
+            i->sink->index, i->sink->name,
+            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
+            pa_yes_no(pa_sink_input_get_mute(i)),
+            (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
+            clt,
+            pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
+            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+            pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
+
+        if (i->module)
+            pa_strbuf_printf(s, "\tmodule: %u\n", i->module->index);
+        if (i->client)
+            pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME)));
+
+        t = pa_proplist_to_string(i->proplist);
+        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_tostring_free(s);
+}
+
+char *pa_scache_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
+
+    if (c->scache) {
+        pa_scache_entry *e;
+        uint32_t idx = PA_IDXSET_INVALID;
+
+        for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
+            double l = 0;
+            char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
+
+            if (e->memchunk.memblock) {
+                pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
+                pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map);
+                l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec);
+            }
+
+            pa_strbuf_printf(
+                s,
+                "    name: <%s>\n"
+                "\tindex: %u\n"
+                "\tsample spec: %s\n"
+                "\tchannel map: %s\n"
+                "\tlength: %lu\n"
+                "\tduration: %0.1f s\n"
+                "\tvolume: %s\n"
+                "\tlazy: %s\n"
+                "\tfilename: <%s>\n",
+                e->name,
+                e->index,
+                ss,
+                cm,
+                (long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
+                l,
+                pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
+                pa_yes_no(e->lazy),
+                e->filename ? e->filename : "n/a");
+
+            t = pa_proplist_to_string(e->proplist);
+            pa_strbuf_printf(s, "\tproperties:\n%s", t);
+            pa_xfree(t);
+        }
+    }
+
+    return pa_strbuf_tostring_free(s);
+}
+
+char *pa_autoload_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0);
+
+    if (c->autoload_hashmap) {
+        pa_autoload_entry *e;
+        void *state = NULL;
+
+        while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
+            pa_strbuf_printf(
+                s,
+                "    name: <%s>\n"
+                "\ttype: %s\n"
+                "\tindex: %u\n"
+                "\tmodule_name: <%s>\n"
+                "\targuments: <%s>\n",
+                e->name,
+                e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
+                e->index,
+                e->module,
+                e->argument ? e->argument : "");
+
+        }
+    }
+
+    return pa_strbuf_tostring_free(s);
+}
+
+char *pa_full_status_string(pa_core *c) {
+    pa_strbuf *s;
+    int i;
+
+    s = pa_strbuf_new();
+
+    for (i = 0; i < 8; i++) {
+        char *t = NULL;
+
+        switch (i) {
+            case 0:
+                t = pa_sink_list_to_string(c);
+                break;
+            case 1:
+                t = pa_source_list_to_string(c);
+                break;
+            case 2:
+                t = pa_sink_input_list_to_string(c);
+                break;
+            case 3:
+                t = pa_source_output_list_to_string(c);
+                break;
+            case 4:
+                t = pa_client_list_to_string(c);
+                break;
+            case 5:
+                t = pa_module_list_to_string(c);
+                break;
+            case 6:
+                t = pa_scache_list_to_string(c);
+                break;
+            case 7:
+                t = pa_autoload_list_to_string(c);
+                break;
+        }
+
+        pa_strbuf_puts(s, t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_tostring_free(s);
+}
diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h
new file mode 100644 (file)
index 0000000..f4cb97a
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef fooclitexthfoo
+#define fooclitexthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/core.h>
+
+/* Some functions to generate pretty formatted listings of
+ * entities. The returned strings have to be freed manually. */
+
+char *pa_sink_input_list_to_string(pa_core *c);
+char *pa_source_output_list_to_string(pa_core *c);
+char *pa_sink_list_to_string(pa_core *core);
+char *pa_source_list_to_string(pa_core *c);
+char *pa_client_list_to_string(pa_core *c);
+char *pa_module_list_to_string(pa_core *c);
+char *pa_scache_list_to_string(pa_core *c);
+char *pa_autoload_list_to_string(pa_core *c);
+
+char *pa_full_status_string(pa_core *c);
+
+#endif
+
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
new file mode 100644 (file)
index 0000000..b3c639f
--- /dev/null
@@ -0,0 +1,153 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/ioline.h>
+#include <pulsecore/module.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/tokenizer.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/cli-text.h>
+#include <pulsecore/cli-command.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cli.h"
+
+#define PROMPT ">>> "
+
+struct pa_cli {
+    pa_core *core;
+    pa_ioline *line;
+
+    void (*eof_callback)(pa_cli *c, void *userdata);
+    void *userdata;
+
+    pa_client *client;
+
+    pa_bool_t fail, kill_requested;
+    int defer_kill;
+};
+
+static void line_callback(pa_ioline *line, const char *s, void *userdata);
+static void client_kill(pa_client *c);
+
+pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
+    char cname[256];
+    pa_cli *c;
+    pa_assert(io);
+
+    c = pa_xnew(pa_cli, 1);
+    c->core = core;
+    pa_assert_se(c->line = pa_ioline_new(io));
+
+    c->userdata = NULL;
+    c->eof_callback = NULL;
+
+    pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
+    pa_assert_se(c->client = pa_client_new(core, __FILE__, cname));
+    c->client->kill = client_kill;
+    c->client->userdata = c;
+    c->client->module = m;
+
+    pa_ioline_set_callback(c->line, line_callback, c);
+    pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT);
+
+    c->fail = c->kill_requested = FALSE;
+    c->defer_kill = 0;
+
+    return c;
+}
+
+void pa_cli_free(pa_cli *c) {
+    pa_assert(c);
+
+    pa_ioline_close(c->line);
+    pa_ioline_unref(c->line);
+    pa_client_free(c->client);
+    pa_xfree(c);
+}
+
+static void client_kill(pa_client *client) {
+    pa_cli *c;
+
+    pa_assert(client);
+    pa_assert_se(c = client->userdata);
+
+    pa_log_debug("CLI client killed.");
+    if (c->defer_kill)
+        c->kill_requested = TRUE;
+    else {
+        if (c->eof_callback)
+            c->eof_callback(c, c->userdata);
+    }
+}
+
+static void line_callback(pa_ioline *line, const char *s, void *userdata) {
+    pa_strbuf *buf;
+    pa_cli *c = userdata;
+    char *p;
+
+    pa_assert(line);
+    pa_assert(c);
+
+    if (!s) {
+        pa_log_debug("CLI got EOF from user.");
+        if (c->eof_callback)
+            c->eof_callback(c, c->userdata);
+
+        return;
+    }
+
+    pa_assert_se(buf = pa_strbuf_new());
+    c->defer_kill++;
+    pa_cli_command_execute_line(c->core, s, buf, &c->fail);
+    c->defer_kill--;
+    pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf));
+    pa_xfree(p);
+
+    if (c->kill_requested) {
+        if (c->eof_callback)
+            c->eof_callback(c, c->userdata);
+    } else
+        pa_ioline_puts(line, PROMPT);
+}
+
+void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) {
+    pa_assert(c);
+
+    c->eof_callback = cb;
+    c->userdata = userdata;
+}
similarity index 56%
rename from polyp/cli.h
rename to src/pulsecore/cli.h
index 44ed706cc25887aa2f76e7022798e01e3b17d286..6077a8e8bdd58952312cd610a4f60124d5d3038d 100644 (file)
@@ -1,38 +1,38 @@
 #ifndef fooclihfoo
 #define fooclihfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; 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"
+#include <pulsecore/iochannel.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
 
-struct pa_cli;
+typedef struct pa_cli pa_cli;
 
 /* Create a new command line session on the specified io channel owned by the specified module */
-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);
+pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m);
+void pa_cli_free(pa_cli *cli);
 
 /* Set a callback function that is called whenever the command line session is terminated */
-void pa_cli_set_eof_callback(struct pa_cli *cli, void (*cb)(struct pa_cli*c, void *userdata), void *userdata);
+void pa_cli_set_eof_callback(pa_cli *cli, void (*cb)(pa_cli*c, void *userdata), void *userdata);
 
 #endif
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
new file mode 100644 (file)
index 0000000..0ffd233
--- /dev/null
@@ -0,0 +1,101 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "client.h"
+
+pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
+    pa_client *c;
+
+    pa_core_assert_ref(core);
+
+    c = pa_xnew(pa_client, 1);
+    c->core = core;
+    c->proplist = pa_proplist_new();
+    if (name)
+        pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+    c->driver = pa_xstrdup(driver);
+    c->module = NULL;
+
+    c->kill = NULL;
+    c->userdata = NULL;
+
+    pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
+
+    pa_log_info("Created %u \"%s\"", c->index, pa_strnull(name));
+    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
+
+    pa_core_check_quit(core);
+
+    return c;
+}
+
+void pa_client_free(pa_client *c) {
+    pa_core *core;
+
+    pa_assert(c);
+    pa_assert(c->core);
+
+    core = c->core;
+    pa_idxset_remove_by_data(c->core->clients, c, NULL);
+
+    pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
+    pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
+    pa_proplist_free(c->proplist);
+    pa_xfree(c->driver);
+    pa_xfree(c);
+
+    pa_core_check_quit(core);
+}
+
+void pa_client_kill(pa_client *c) {
+    pa_assert(c);
+
+    if (!c->kill) {
+        pa_log_warn("kill() operation not implemented for client %u", c->index);
+        return;
+    }
+
+    c->kill(c);
+}
+
+void pa_client_set_name(pa_client *c, const char *name) {
+    pa_assert(c);
+
+    pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)), name);
+    pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+    pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
+}
similarity index 55%
rename from polyp/client.h
rename to src/pulsecore/client.h
index 082dc29f328bdc457d00d8ecc38e5bdceb288885..28d1fe5f448b887531128ad1dc1a55501ebb2e16 100644 (file)
@@ -1,30 +1,34 @@
-#ifndef fooclienthfoo
-#define fooclienthfoo
-
-/* $Id$ */
+#ifndef foopulseclienthfoo
+#define foopulseclienthfoo
 
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; 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"
-#include "typeid.h"
+#include <inttypes.h>
+
+typedef struct pa_client pa_client;
+
+#include <pulse/proplist.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
 
 /* Every connection to the server should have a pa_client
  * attached. That way the user may generate a listing of all connected
 
 struct pa_client {
     uint32_t index;
+    pa_core *core;
 
-    struct pa_module *owner;
-    char *name, *driver;
-    struct pa_core *core;
+    pa_proplist *proplist;
+    pa_module *module;
+    char *driver;
 
-    void (*kill)(struct pa_client *c);
+    void (*kill)(pa_client *c);
     void *userdata;
 };
 
-struct pa_client *pa_client_new(struct pa_core *c, const char *name, const char *driver);
+pa_client *pa_client_new(pa_core *c, const char *driver, const char *name);
 
 /* This function should be called only by the code that created the client */
-void pa_client_free(struct pa_client *c);
+void pa_client_free(pa_client *c);
 
 /* Code that didn't create the client should call this function to
  * request destruction of the client */
-void pa_client_kill(struct pa_client *c);
+void pa_client_kill(pa_client *c);
 
 /* Rename the client */
-void pa_client_set_name(struct pa_client *c, const char *name);
+void pa_client_set_name(pa_client *c, const char *name);
 
 #endif
similarity index 58%
rename from polyp/conf-parser.c
rename to src/pulsecore/conf-parser.c
index b25508e2d49349f99631ff632040c3d715db05ae..4aec45d7474901b8b42967ae198622ce79219010 100644 (file)
@@ -1,58 +1,68 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
 
-  polypaudio is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
 
-  polypaudio is distributed in the hope that it will be useful, but
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
 
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
 #include "conf-parser.h"
-#include "log.h"
-#include "util.h"
-#include "xmalloc.h"
 
 #define WHITESPACE " \t\n"
 #define COMMENTS "#;\n"
 
 /* Run the user supplied parser for an assignment */
-static int next_assignment(const char *filename, unsigned line, const struct pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
-    assert(filename && t && lvalue && rvalue);
-    
+static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
+    pa_assert(filename);
+    pa_assert(t);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+
     for (; t->parse; t++)
         if (!strcmp(lvalue, t->lvalue))
             return t->parse(filename, line, lvalue, rvalue, t->data, userdata);
 
-    pa_log(__FILE__": [%s:%u] Unknown lvalue '%s'.\n", filename, line, lvalue);
-    
+    pa_log("[%s:%u] Unknown lvalue '%s'.", filename, line, lvalue);
+
     return -1;
 }
 
 /* Returns non-zero when c is contained in s */
 static int in_string(char c, const char *s) {
-    assert(s);
-    
+    pa_assert(s);
+
     for (; *s; s++)
         if (*s == c)
             return 1;
-    
+
     return 0;
 }
 
@@ -73,17 +83,17 @@ static char *strip(char *s) {
 }
 
 /* Parse a variable assignment line */
-static int parse_line(const char *filename, unsigned line, const struct pa_config_item *t, char *l, void *userdata) {
+static int parse_line(const char *filename, unsigned line, const pa_config_item *t, char *l, void *userdata) {
     char *e, *c, *b = l+strspn(l, WHITESPACE);
 
     if ((c = strpbrk(b, COMMENTS)))
         *c = 0;
-    
+
     if (!*b)
         return 0;
 
     if (!(e = strchr(b, '='))) {
-        pa_log(__FILE__": [%s:%u] Missing '='.\n", filename, line);
+        pa_log("[%s:%u] Missing '='.", filename, line);
         return -1;
     }
 
@@ -94,19 +104,22 @@ static int parse_line(const char *filename, unsigned line, const struct pa_confi
 }
 
 /* Go through the file and parse each line */
-int pa_config_parse(const char *filename, FILE *f, const struct pa_config_item *t, void *userdata) {
+int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata) {
     int r = -1;
     unsigned line = 0;
     int do_close = !f;
-    assert(filename && t);
-    
+
+    pa_assert(filename);
+    pa_assert(t);
+
     if (!f && !(f = fopen(filename, "r"))) {
         if (errno == ENOENT) {
             r = 0;
             goto finish;
         }
-        
-        pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno));
+
+        pa_log_warn("Failed to open configuration file '%s': %s",
+            filename, pa_cstrerror(errno));
         goto finish;
     }
 
@@ -115,56 +128,70 @@ int pa_config_parse(const char *filename, FILE *f, const struct pa_config_item *
         if (!fgets(l, sizeof(l), f)) {
             if (feof(f))
                 break;
-            
-            pa_log(__FILE__": WARNING: failed to read configuration file '%s': %s\n", filename, strerror(errno));
+
+            pa_log_warn("Failed to read configuration file '%s': %s",
+                filename, pa_cstrerror(errno));
             goto finish;
         }
-            
+
         if (parse_line(filename, ++line, t,  l, userdata) < 0)
             goto finish;
     }
-    
+
     r = 0;
-    
+
 finish:
 
     if (do_close && f)
         fclose(f);
-    
+
     return r;
 }
 
-int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
     int *i = data;
     int32_t k;
-    assert(filename && lvalue && rvalue && data);
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
 
     if (pa_atoi(rvalue, &k) < 0) {
-        pa_log(__FILE__": [%s:%u] Failed to parse numeric value: %s\n", filename, line, rvalue);
+        pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
         return -1;
     }
-    
+
     *i = (int) k;
-    return 0; 
+    return 0;
 }
 
-int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
-    int *b = data, k;
-    assert(filename && lvalue && rvalue && data);
-    
+int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
+    int k;
+    pa_bool_t *b = data;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
     if ((k = pa_parse_boolean(rvalue)) < 0) {
-        pa_log(__FILE__": [%s:%u] Failed to parse boolean value: %s\n", filename, line, rvalue);
+        pa_log("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
         return -1;
     }
-    
-    *b = k;
-    
+
+    *b = !!k;
+
     return 0;
 }
 
-int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
     char **s = data;
-    assert(filename && lvalue && rvalue && data);
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
 
     pa_xfree(*s);
     *s = *rvalue ? pa_xstrdup(rvalue) : NULL;
similarity index 80%
rename from polyp/conf-parser.h
rename to src/pulsecore/conf-parser.h
index 9add0be0535381356b7e8ed1e7f0e104369da1b1..7eb1fae2251c89e5389bd3cf35a76e036823606a 100644 (file)
@@ -1,23 +1,23 @@
 #ifndef fooconfparserhfoo
 #define fooconfparserhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
 
-  polypaudio is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
 
-  polypaudio is distributed in the hope that it will be useful, but
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
  * files consisting of variable assignments only. */
 
 /* Wraps info for parsing a specific configuration variable */
-struct pa_config_item {
+typedef struct pa_config_item {
     const char *lvalue; /* name of the variable */
     int (*parse)(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); /* Function that is called to parse the variable's value */
     void *data; /* Where to store the variable's data */
-};
+} pa_config_item;
 
 /* The configuration file parsing routine. Expects a table of
  * pa_config_items in *t that is terminated by an item where lvalue is
  * NULL */
-int pa_config_parse(const char *filename, FILE *f, const struct pa_config_item *t, void *userdata);
+int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata);
 
 /* Generic parsers for integers, booleans and strings */
 int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c
new file mode 100644 (file)
index 0000000..3d6c2c3
--- /dev/null
@@ -0,0 +1,78 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <string.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+#include "core-error.h"
+
+PA_STATIC_TLS_DECLARE(cstrerror, pa_xfree);
+
+const char* pa_cstrerror(int errnum) {
+    const char *original = NULL;
+    char *translated, *t;
+    char errbuf[128];
+
+    if ((t = PA_STATIC_TLS_GET(cstrerror)))
+        pa_xfree(t);
+
+#if defined(HAVE_STRERROR_R) && defined(__GLIBC__)
+    original = strerror_r(errnum, errbuf, sizeof(errbuf));
+#elif defined(HAVE_STRERROR_R)
+    if (strerror_r(errnum, errbuf, sizeof(errbuf)) == 0) {
+        errbuf[sizeof(errbuf) - 1] = 0;
+        original = errbuf;
+    }
+#else
+    /* This might not be thread safe, but we hope for the best */
+    original = strerror(errnum);
+#endif
+
+    if (!original) {
+        pa_snprintf(errbuf, sizeof(errbuf), "Unknown error %i", errnum);
+        original = errbuf;
+    }
+
+    if (!(translated = pa_locale_to_utf8(original))) {
+        pa_log_warn("Unable to convert error string to locale, filtering.");
+        translated = pa_utf8_filter(original);
+    }
+
+    PA_STATIC_TLS_SET(cstrerror, translated);
+
+    return translated;
+}
diff --git a/src/pulsecore/core-error.h b/src/pulsecore/core-error.h
new file mode 100644 (file)
index 0000000..b0c306c
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef foocoreerrorhfoo
+#define foocoreerrorhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+#include <pulse/cdecl.h>
+
+/** \file
+ * Error management */
+
+PA_C_DECL_BEGIN
+
+/** A wrapper around the standard strerror() function that converts the
+ * string to UTF-8. The function is thread safe but the returned string is
+ * only guaranteed to exist until the thread exits or pa_cstrerror() is
+ * called again from the same thread. */
+const char* pa_cstrerror(int errnum);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
new file mode 100644 (file)
index 0000000..75fa2ff
--- /dev/null
@@ -0,0 +1,498 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulse/mainloop.h>
+#include <pulse/channelmap.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/play-memchunk.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sound-file.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/macro.h>
+
+#include "core-scache.h"
+
+#define UNLOAD_POLL_TIME 60
+
+static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+    pa_core *c = userdata;
+    struct timeval ntv;
+
+    pa_assert(c);
+    pa_assert(c->mainloop == m);
+    pa_assert(c->scache_auto_unload_event == e);
+
+    pa_scache_unload_unused(c);
+
+    pa_gettimeofday(&ntv);
+    ntv.tv_sec += UNLOAD_POLL_TIME;
+    m->time_restart(e, &ntv);
+}
+
+static void free_entry(pa_scache_entry *e) {
+    pa_assert(e);
+
+    pa_namereg_unregister(e->core, e->name);
+    pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);
+    pa_xfree(e->name);
+    pa_xfree(e->filename);
+    if (e->memchunk.memblock)
+        pa_memblock_unref(e->memchunk.memblock);
+    if (e->proplist)
+        pa_proplist_free(e->proplist);
+    pa_xfree(e);
+}
+
+static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
+    pa_scache_entry *e;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) {
+        if (e->memchunk.memblock)
+            pa_memblock_unref(e->memchunk.memblock);
+
+        pa_xfree(e->filename);
+        pa_proplist_clear(e->proplist);
+
+        pa_assert(e->core == c);
+
+        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
+    } else {
+        e = pa_xnew(pa_scache_entry, 1);
+
+        if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) {
+            pa_xfree(e);
+            return NULL;
+        }
+
+        e->name = pa_xstrdup(name);
+        e->core = c;
+        e->proplist = pa_proplist_new();
+
+        if (!c->scache)
+            c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+        pa_idxset_put(c->scache, e, &e->index);
+
+        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index);
+    }
+
+    e->last_used_time = 0;
+    pa_memchunk_reset(&e->memchunk);
+    e->filename = NULL;
+    e->lazy = FALSE;
+    e->last_used_time = 0;
+
+    memset(&e->sample_spec, 0, sizeof(e->sample_spec));
+    pa_channel_map_init(&e->channel_map);
+    pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX);
+
+    pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event");
+
+    return e;
+}
+
+int pa_scache_add_item(
+        pa_core *c,
+        const char *name,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        const pa_memchunk *chunk,
+        pa_proplist *p,
+        uint32_t *idx) {
+
+    pa_scache_entry *e;
+    char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+    pa_channel_map tmap;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(!ss || pa_sample_spec_valid(ss));
+    pa_assert(!map || (pa_channel_map_valid(map) && ss && ss->channels == map->channels));
+
+    if (ss && !map)
+        pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+
+    if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
+        return -1;
+
+    if (!(e = scache_add_item(c, name)))
+        return -1;
+
+    memset(&e->sample_spec, 0, sizeof(e->sample_spec));
+    pa_channel_map_init(&e->channel_map);
+
+    if (ss) {
+        e->sample_spec = *ss;
+        e->volume.channels = e->sample_spec.channels;
+    }
+
+    if (map)
+        e->channel_map = *map;
+
+    if (chunk) {
+        e->memchunk = *chunk;
+        pa_memblock_ref(e->memchunk.memblock);
+    }
+
+    if (p)
+        pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);
+
+    if (idx)
+        *idx = e->index;
+
+    pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s",
+                 name, e->index, (unsigned long) e->memchunk.length,
+                 pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec));
+
+    return 0;
+}
+
+int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_memchunk chunk;
+    int r;
+    pa_proplist *p;
+
+#ifdef OS_IS_WIN32
+    char buf[MAX_PATH];
+
+    if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
+        filename = buf;
+#endif
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(filename);
+
+    if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0)
+        return -1;
+
+    p = pa_proplist_new();
+    pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename);
+    r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx);
+    pa_memblock_unref(chunk.memblock);
+    pa_proplist_free(p);
+
+    return r;
+}
+
+int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
+    pa_scache_entry *e;
+
+#ifdef OS_IS_WIN32
+    char buf[MAX_PATH];
+
+    if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
+        filename = buf;
+#endif
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(filename);
+
+    if (!(e = scache_add_item(c, name)))
+        return -1;
+
+    e->lazy = TRUE;
+    e->filename = pa_xstrdup(filename);
+
+    pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename);
+
+    if (!c->scache_auto_unload_event) {
+        struct timeval ntv;
+        pa_gettimeofday(&ntv);
+        ntv.tv_sec += UNLOAD_POLL_TIME;
+        c->scache_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
+    }
+
+    if (idx)
+        *idx = e->index;
+
+    return 0;
+}
+
+int pa_scache_remove_item(pa_core *c, const char *name) {
+    pa_scache_entry *e;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
+        return -1;
+
+    pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e);
+
+    pa_log_debug("Removed sample \"%s\"", name);
+
+    free_entry(e);
+
+    return 0;
+}
+
+static void free_cb(void *p, PA_GCC_UNUSED void *userdata) {
+    pa_scache_entry *e = p;
+    pa_assert(e);
+
+    free_entry(e);
+}
+
+void pa_scache_free(pa_core *c) {
+    pa_assert(c);
+
+    if (c->scache) {
+        pa_idxset_free(c->scache, free_cb, NULL);
+        c->scache = NULL;
+    }
+
+    if (c->scache_auto_unload_event)
+        c->mainloop->time_free(c->scache_auto_unload_event);
+}
+
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
+    pa_scache_entry *e;
+    pa_cvolume r;
+    pa_proplist *merged;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(sink);
+
+    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1)))
+        return -1;
+
+    if (e->lazy && !e->memchunk.memblock) {
+        if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk) < 0)
+            return -1;
+
+        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
+
+        if (e->volume.channels > e->sample_spec.channels)
+            e->volume.channels = e->sample_spec.channels;
+    }
+
+    if (!e->memchunk.memblock)
+        return -1;
+
+    pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
+
+    pa_cvolume_set(&r, e->volume.channels, volume);
+    pa_sw_cvolume_multiply(&r, &r, &e->volume);
+
+    merged = pa_proplist_new();
+
+    pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
+
+    pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
+
+    if (p)
+        pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
+
+    if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, &r, merged, sink_input_idx) < 0) {
+        pa_proplist_free(merged);
+        return -1;
+    }
+
+    pa_proplist_free(merged);
+
+    if (e->lazy)
+        time(&e->last_used_time);
+
+    return 0;
+}
+
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
+    pa_sink *sink;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload)))
+        return -1;
+
+    return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx);
+}
+
+const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
+    pa_scache_entry *e;
+
+    pa_assert(c);
+    pa_assert(id != PA_IDXSET_INVALID);
+
+    if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id)))
+        return NULL;
+
+    return e->name;
+}
+
+uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
+    pa_scache_entry *e;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
+        return PA_IDXSET_INVALID;
+
+    return e->index;
+}
+
+size_t pa_scache_total_size(pa_core *c) {
+    pa_scache_entry *e;
+    uint32_t idx;
+    size_t sum = 0;
+
+    pa_assert(c);
+
+    if (!c->scache || !pa_idxset_size(c->scache))
+        return 0;
+
+    for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx))
+        if (e->memchunk.memblock)
+            sum += e->memchunk.length;
+
+    return sum;
+}
+
+void pa_scache_unload_unused(pa_core *c) {
+    pa_scache_entry *e;
+    time_t now;
+    uint32_t idx;
+
+    pa_assert(c);
+
+    if (!c->scache || !pa_idxset_size(c->scache))
+        return;
+
+    time(&now);
+
+    for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
+
+        if (!e->lazy || !e->memchunk.memblock)
+            continue;
+
+        if (e->last_used_time + c->scache_idle_time > now)
+            continue;
+
+        pa_memblock_unref(e->memchunk.memblock);
+        pa_memchunk_reset(&e->memchunk);
+
+        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
+    }
+}
+
+static void add_file(pa_core *c, const char *pathname) {
+    struct stat st;
+    const char *e;
+
+    pa_core_assert_ref(c);
+    pa_assert(pathname);
+
+    e = pa_path_get_filename(pathname);
+
+    if (stat(pathname, &st) < 0) {
+        pa_log("stat('%s'): %s", pathname, pa_cstrerror(errno));
+        return;
+    }
+
+#if defined(S_ISREG) && defined(S_ISLNK)
+    if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
+#endif
+        pa_scache_add_file_lazy(c, e, pathname, NULL);
+}
+
+int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
+    DIR *dir;
+
+    pa_core_assert_ref(c);
+    pa_assert(pathname);
+
+    /* First try to open this as directory */
+    if (!(dir = opendir(pathname))) {
+#ifdef HAVE_GLOB_H
+        glob_t p;
+        unsigned int i;
+        /* If that fails, try to open it as shell glob */
+
+        if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) {
+            pa_log("failed to open directory '%s': %s", pathname, pa_cstrerror(errno));
+            return -1;
+        }
+
+        for (i = 0; i < p.gl_pathc; i++)
+            add_file(c, p.gl_pathv[i]);
+
+        globfree(&p);
+#else
+        return -1;
+#endif
+    } else {
+        struct dirent *e;
+
+        while ((e = readdir(dir))) {
+            char p[PATH_MAX];
+
+            if (e->d_name[0] == '.')
+                continue;
+
+            pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
+            add_file(c, p);
+        }
+
+        closedir(dir);
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h
new file mode 100644 (file)
index 0000000..80e0fd0
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef foocorescachehfoo
+#define foocorescachehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+
+#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*16)
+
+typedef struct pa_scache_entry {
+    uint32_t index;
+    pa_core *core;
+
+    char *name;
+
+    pa_cvolume volume;
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    pa_memchunk memchunk;
+
+    char *filename;
+
+    pa_bool_t lazy;
+    time_t last_used_time;
+
+    pa_proplist *proplist;
+} pa_scache_entry;
+
+int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, pa_proplist *p, uint32_t *idx);
+int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx);
+int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx);
+
+int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
+
+int pa_scache_remove_item(pa_core *c, const char *name);
+int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
+void pa_scache_free(pa_core *c);
+
+const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
+uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name);
+
+size_t pa_scache_total_size(pa_core *c);
+
+void pa_scache_unload_unused(pa_core *c);
+
+#endif
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
new file mode 100644 (file)
index 0000000..6107002
--- /dev/null
@@ -0,0 +1,264 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/queue.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "core-subscribe.h"
+
+/* The subscription subsystem may be used to be notified whenever an
+ * entity (sink, source, ...) is created or deleted. Modules may
+ * register a callback function that is called whenever an event
+ * matching a subscription mask happens. The execution of the callback
+ * function is postponed to the next main loop iteration, i.e. is not
+ * called from within the stack frame the entity was created in. */
+
+struct pa_subscription {
+    pa_core *core;
+    int dead;
+
+    pa_subscription_cb_t callback;
+    void *userdata;
+    pa_subscription_mask_t mask;
+
+    PA_LLIST_FIELDS(pa_subscription);
+};
+
+struct pa_subscription_event {
+    pa_core *core;
+
+    pa_subscription_event_type_t type;
+    uint32_t index;
+
+    PA_LLIST_FIELDS(pa_subscription_event);
+};
+
+static void sched_event(pa_core *c);
+
+/* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */
+pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_subscription_cb_t callback, void *userdata) {
+    pa_subscription *s;
+
+    pa_assert(c);
+    pa_assert(m);
+    pa_assert(callback);
+
+    s = pa_xnew(pa_subscription, 1);
+    s->core = c;
+    s->dead = 0;
+    s->callback = callback;
+    s->userdata = userdata;
+    s->mask = m;
+
+    PA_LLIST_PREPEND(pa_subscription, c->subscriptions, s);
+    return s;
+}
+
+/* Free a subscription object, effectively marking it for deletion */
+void pa_subscription_free(pa_subscription*s) {
+    pa_assert(s);
+    pa_assert(!s->dead);
+
+    s->dead = 1;
+    sched_event(s->core);
+}
+
+static void free_subscription(pa_subscription *s) {
+    pa_assert(s);
+    pa_assert(s->core);
+
+    PA_LLIST_REMOVE(pa_subscription, s->core->subscriptions, s);
+    pa_xfree(s);
+}
+
+static void free_event(pa_subscription_event *s) {
+    pa_assert(s);
+    pa_assert(s->core);
+
+    if (!s->next)
+        s->core->subscription_event_last = s->prev;
+
+    PA_LLIST_REMOVE(pa_subscription_event, s->core->subscription_event_queue, s);
+    pa_xfree(s);
+}
+
+/* Free all subscription objects */
+void pa_subscription_free_all(pa_core *c) {
+    pa_assert(c);
+
+    while (c->subscriptions)
+        free_subscription(c->subscriptions);
+
+    while (c->subscription_event_queue)
+        free_event(c->subscription_event_queue);
+
+    if (c->subscription_defer_event) {
+        c->mainloop->defer_free(c->subscription_defer_event);
+        c->subscription_defer_event = NULL;
+    }
+}
+
+#ifdef DEBUG
+static void dump_event(const char * prefix, pa_subscription_event*e) {
+    const char * const fac_table[] = {
+        [PA_SUBSCRIPTION_EVENT_SINK] = "SINK",
+        [PA_SUBSCRIPTION_EVENT_SOURCE] = "SOURCE",
+        [PA_SUBSCRIPTION_EVENT_SINK_INPUT] = "SINK_INPUT",
+        [PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT] = "SOURCE_OUTPUT",
+        [PA_SUBSCRIPTION_EVENT_MODULE] = "MODULE",
+        [PA_SUBSCRIPTION_EVENT_CLIENT] = "CLIENT",
+        [PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE] = "SAMPLE_CACHE",
+        [PA_SUBSCRIPTION_EVENT_SERVER] = "SERVER",
+        [PA_SUBSCRIPTION_EVENT_AUTOLOAD] = "AUTOLOAD"
+    };
+
+    const char * const type_table[] = {
+        [PA_SUBSCRIPTION_EVENT_NEW] = "NEW",
+        [PA_SUBSCRIPTION_EVENT_CHANGE] = "CHANGE",
+        [PA_SUBSCRIPTION_EVENT_REMOVE] = "REMOVE"
+    };
+
+    pa_log("%s event (%s|%s|%u)",
+           prefix,
+           fac_table[e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK],
+           type_table[e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK],
+           e->index);
+}
+#endif
+
+/* Deferred callback for dispatching subscirption events */
+static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
+    pa_core *c = userdata;
+    pa_subscription *s;
+
+    pa_assert(c->mainloop == m);
+    pa_assert(c);
+    pa_assert(c->subscription_defer_event == de);
+
+    c->mainloop->defer_enable(c->subscription_defer_event, 0);
+
+    /* Dispatch queued events */
+
+    while (c->subscription_event_queue) {
+        pa_subscription_event *e = c->subscription_event_queue;
+
+        for (s = c->subscriptions; s; s = s->next) {
+
+            if (!s->dead && pa_subscription_match_flags(s->mask, e->type))
+                s->callback(c, e->type, e->index, s->userdata);
+        }
+
+#ifdef DEBUG
+        dump_event("Dispatched", e);
+#endif
+        free_event(e);
+    }
+
+    /* Remove dead subscriptions */
+
+    s = c->subscriptions;
+    while (s) {
+        pa_subscription *n = s->next;
+        if (s->dead)
+            free_subscription(s);
+        s = n;
+    }
+}
+
+/* Schedule an mainloop event so that a pending subscription event is dispatched */
+static void sched_event(pa_core *c) {
+    pa_assert(c);
+
+    if (!c->subscription_defer_event) {
+        c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c);
+        pa_assert(c->subscription_defer_event);
+    }
+
+    c->mainloop->defer_enable(c->subscription_defer_event, 1);
+}
+
+/* Append a new subscription event to the subscription event queue and schedule a main loop event */
+void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx) {
+    pa_subscription_event *e;
+    pa_assert(c);
+
+    /* No need for queuing subscriptions of noone is listening */
+    if (!c->subscriptions)
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_NEW) {
+        pa_subscription_event *i, *n;
+
+        /* Check for duplicates */
+        for (i = c->subscription_event_last; i; i = n) {
+            n = i->prev;
+
+            /* not the same object type */
+            if (((t ^ i->type) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))
+                continue;
+
+            /* not the same object */
+            if (i->index != idx)
+                continue;
+
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                /* This object is being removed, hence there is no
+                 * point in keeping the old events regarding this
+                 * entry in the queue. */
+
+                free_event(i);
+                pa_log_debug("dropped redundant event.");
+                continue;
+            }
+
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
+                /* This object has changed. If a "new" or "change" event for
+                 * this object is still in the queue we can exit. */
+
+                pa_log_debug("dropped redundant event.");
+                return;
+            }
+        }
+    }
+
+    e = pa_xnew(pa_subscription_event, 1);
+    e->core = c;
+    e->type = t;
+    e->index = idx;
+
+    PA_LLIST_INSERT_AFTER(pa_subscription_event, c->subscription_event_queue, c->subscription_event_last, e);
+    c->subscription_event_last = e;
+
+#ifdef DEBUG
+    dump_event("Queued", e);
+#endif
+
+    sched_event(c);
+}
diff --git a/src/pulsecore/core-subscribe.h b/src/pulsecore/core-subscribe.h
new file mode 100644 (file)
index 0000000..2f9730d
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef foocoresubscribehfoo
+#define foocoresubscribehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+typedef struct pa_subscription pa_subscription;
+typedef struct pa_subscription_event pa_subscription_event;
+
+#include <pulsecore/core.h>
+#include <pulsecore/native-common.h>
+
+typedef void (*pa_subscription_cb_t)(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
+
+pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m,  pa_subscription_cb_t cb, void *userdata);
+void pa_subscription_free(pa_subscription*s);
+void pa_subscription_free_all(pa_core *c);
+
+void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx);
+
+#endif
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
new file mode 100644 (file)
index 0000000..d259fb1
--- /dev/null
@@ -0,0 +1,2045 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2004 Joe Marcus Clarke
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <dirent.h>
+
+#ifdef HAVE_STRTOF_L
+#include <locale.h>
+#endif
+
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_LIBSAMPLERATE
+#include <samplerate.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/utf8.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/winsock.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+
+#include "core-util.h"
+
+/* Not all platforms have this */
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+#ifdef OS_IS_WIN32
+
+#define PULSE_ROOTENV "PULSE_ROOT"
+
+int pa_set_root(HANDLE handle) {
+    char library_path[MAX_PATH + sizeof(PULSE_ROOTENV) + 1], *sep;
+
+    strcpy(library_path, PULSE_ROOTENV "=");
+
+    if (!GetModuleFileName(handle, library_path + sizeof(PULSE_ROOTENV), MAX_PATH))
+        return 0;
+
+    sep = strrchr(library_path, PA_PATH_SEP_CHAR);
+    if (sep)
+        *sep = '\0';
+
+    if (_putenv(library_path) < 0)
+        return 0;
+
+    return 1;
+}
+
+#endif
+
+/** Make a file descriptor nonblock. Doesn't do any error checking */
+void pa_make_fd_nonblock(int fd) {
+
+#ifdef O_NONBLOCK
+    int v;
+    pa_assert(fd >= 0);
+
+    pa_assert_se((v = fcntl(fd, F_GETFL)) >= 0);
+
+    if (!(v & O_NONBLOCK))
+        pa_assert_se(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0);
+
+#elif defined(OS_IS_WIN32)
+    u_long arg = 1;
+    if (ioctlsocket(fd, FIONBIO, &arg) < 0) {
+        pa_assert_se(WSAGetLastError() == WSAENOTSOCK);
+        pa_log_warn("Only sockets can be made non-blocking!");
+    }
+#else
+    pa_log_warn("Non-blocking I/O not supported.!");
+#endif
+
+}
+
+/* Set the FD_CLOEXEC flag for a fd */
+void pa_make_fd_cloexec(int fd) {
+
+#ifdef FD_CLOEXEC
+    int v;
+    pa_assert(fd >= 0);
+
+    pa_assert_se((v = fcntl(fd, F_GETFD, 0)) >= 0);
+
+    if (!(v & FD_CLOEXEC))
+        pa_assert_se(fcntl(fd, F_SETFD, v|FD_CLOEXEC) >= 0);
+#endif
+
+}
+
+/** Creates a directory securely */
+int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
+    struct stat st;
+    int r;
+
+    pa_assert(dir);
+
+#ifdef OS_IS_WIN32
+    r = mkdir(dir);
+#else
+    {
+    mode_t u;
+    u = umask(~m);
+    r = mkdir(dir, m);
+    umask(u);
+    }
+#endif
+
+    if (r < 0 && errno != EEXIST)
+        return -1;
+
+#ifdef HAVE_CHOWN
+    if (uid == (uid_t)-1)
+        uid = getuid();
+    if (gid == (gid_t)-1)
+        gid = getgid();
+    (void) chown(dir, uid, gid);
+#endif
+
+#ifdef HAVE_CHMOD
+    chmod(dir, m);
+#endif
+
+#ifdef HAVE_LSTAT
+    if (lstat(dir, &st) < 0)
+#else
+    if (stat(dir, &st) < 0)
+#endif
+        goto fail;
+
+#ifndef OS_IS_WIN32
+    if (!S_ISDIR(st.st_mode) ||
+        (st.st_uid != uid) ||
+        (st.st_gid != gid) ||
+        ((st.st_mode & 0777) != m)) {
+        errno = EACCES;
+        goto fail;
+    }
+#else
+    pa_log_warn("Secure directory creation not supported on Win32.");
+#endif
+
+    return 0;
+
+fail:
+    rmdir(dir);
+    return -1;
+}
+
+/* Return a newly allocated sting containing the parent directory of the specified file */
+char *pa_parent_dir(const char *fn) {
+    char *slash, *dir = pa_xstrdup(fn);
+
+    if ((slash = (char*) pa_path_get_filename(dir)) == dir) {
+        pa_xfree(dir);
+        return NULL;
+    }
+
+    *(slash-1) = 0;
+    return dir;
+}
+
+/* Creates a the parent directory of the specified path securely */
+int pa_make_secure_parent_dir(const char *fn, mode_t m, uid_t uid, gid_t gid) {
+    int ret = -1;
+    char *dir;
+
+    if (!(dir = pa_parent_dir(fn)))
+        goto finish;
+
+    if (pa_make_secure_dir(dir, m, uid, gid) < 0)
+        goto finish;
+
+    ret = 0;
+
+finish:
+    pa_xfree(dir);
+    return ret;
+}
+
+/** Platform independent read function. Necessary since not all
+ * systems treat all file descriptors equal. If type is
+ * non-NULL it is used to cache the type of the fd. This is
+ * useful for making sure that only a single syscall is executed per
+ * function call. The variable pointed to should be initialized to 0
+ * by the caller. */
+ssize_t pa_read(int fd, void *buf, size_t count, int *type) {
+
+#ifdef OS_IS_WIN32
+
+    if (!type || *type == 0) {
+        ssize_t r;
+
+        if ((r = recv(fd, buf, count, 0)) >= 0)
+            return r;
+
+        if (WSAGetLastError() != WSAENOTSOCK) {
+            errno = WSAGetLastError();
+            return r;
+        }
+
+        if (type)
+            *type = 1;
+    }
+
+#endif
+
+    return read(fd, buf, count);
+}
+
+/** Similar to pa_read(), but handles writes */
+ssize_t pa_write(int fd, const void *buf, size_t count, int *type) {
+
+    if (!type || *type == 0) {
+        ssize_t r;
+
+        if ((r = send(fd, buf, count, MSG_NOSIGNAL)) >= 0)
+            return r;
+
+#ifdef OS_IS_WIN32
+        if (WSAGetLastError() != WSAENOTSOCK) {
+            errno = WSAGetLastError();
+            return r;
+        }
+#else
+        if (errno != ENOTSOCK)
+            return r;
+#endif
+
+        if (type)
+            *type = 1;
+    }
+
+    return write(fd, buf, count);
+}
+
+/** Calls read() in a loop. Makes sure that as much as 'size' bytes,
+ * unless EOF is reached or an error occured */
+ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {
+    ssize_t ret = 0;
+    int _type;
+
+    pa_assert(fd >= 0);
+    pa_assert(data);
+    pa_assert(size);
+
+    if (!type) {
+        _type = 0;
+        type = &_type;
+    }
+
+    while (size > 0) {
+        ssize_t r;
+
+        if ((r = pa_read(fd, data, size, type)) < 0)
+            return r;
+
+        if (r == 0)
+            break;
+
+        ret += r;
+        data = (uint8_t*) data + r;
+        size -= r;
+    }
+
+    return ret;
+}
+
+/** Similar to pa_loop_read(), but wraps write() */
+ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type) {
+    ssize_t ret = 0;
+    int _type;
+
+    pa_assert(fd >= 0);
+    pa_assert(data);
+    pa_assert(size);
+
+    if (!type) {
+        _type = 0;
+        type = &_type;
+    }
+
+    while (size > 0) {
+        ssize_t r;
+
+        if ((r = pa_write(fd, data, size, type)) < 0)
+            return r;
+
+        if (r == 0)
+            break;
+
+        ret += r;
+        data = (const uint8_t*) data + r;
+        size -= r;
+    }
+
+    return ret;
+}
+
+/** Platform independent read function. Necessary since not all
+ * systems treat all file descriptors equal. */
+int pa_close(int fd) {
+
+#ifdef OS_IS_WIN32
+    int ret;
+
+    if ((ret = closesocket(fd)) == 0)
+        return 0;
+
+    if (WSAGetLastError() != WSAENOTSOCK) {
+        errno = WSAGetLastError();
+        return ret;
+    }
+#endif
+
+    return close(fd);
+}
+
+/* Print a warning messages in case that the given signal is not
+ * blocked or trapped */
+void pa_check_signal_is_blocked(int sig) {
+#ifdef HAVE_SIGACTION
+    struct sigaction sa;
+    sigset_t set;
+
+    /* If POSIX threads are supported use thread-aware
+     * pthread_sigmask() function, to check if the signal is
+     * blocked. Otherwise fall back to sigprocmask() */
+
+#ifdef HAVE_PTHREAD
+    if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) {
+#endif
+        if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) {
+            pa_log("sigprocmask(): %s", pa_cstrerror(errno));
+            return;
+        }
+#ifdef HAVE_PTHREAD
+    }
+#endif
+
+    if (sigismember(&set, sig))
+        return;
+
+    /* Check whether the signal is trapped */
+
+    if (sigaction(sig, NULL, &sa) < 0) {
+        pa_log("sigaction(): %s", pa_cstrerror(errno));
+        return;
+    }
+
+    if (sa.sa_handler != SIG_DFL)
+        return;
+
+    pa_log_warn("%s is not trapped. This might cause malfunction!", pa_sig2str(sig));
+#else /* HAVE_SIGACTION */
+    pa_log_warn("%s might not be trapped. This might cause malfunction!", pa_sig2str(sig));
+#endif
+}
+
+/* The following function is based on an example from the GNU libc
+ * documentation. This function is similar to GNU's asprintf(). */
+char *pa_sprintf_malloc(const char *format, ...) {
+    int  size = 100;
+    char *c = NULL;
+
+    pa_assert(format);
+
+    for(;;) {
+        int r;
+        va_list ap;
+
+        c = pa_xrealloc(c, size);
+
+        va_start(ap, format);
+        r = vsnprintf(c, size, format, ap);
+        va_end(ap);
+
+        c[size-1] = 0;
+
+        if (r > -1 && r < size)
+            return c;
+
+        if (r > -1)    /* glibc 2.1 */
+            size = r+1;
+        else           /* glibc 2.0 */
+            size *= 2;
+    }
+}
+
+/* Same as the previous function, but use a va_list instead of an
+ * ellipsis */
+char *pa_vsprintf_malloc(const char *format, va_list ap) {
+    int  size = 100;
+    char *c = NULL;
+
+    pa_assert(format);
+
+    for(;;) {
+        int r;
+        va_list aq;
+
+        c = pa_xrealloc(c, size);
+
+        va_copy(aq, ap);
+        r = vsnprintf(c, size, format, aq);
+        va_end(aq);
+
+        c[size-1] = 0;
+
+        if (r > -1 && r < size)
+            return c;
+
+        if (r > -1)    /* glibc 2.1 */
+            size = r+1;
+        else           /* glibc 2.0 */
+            size *= 2;
+    }
+}
+
+/* Similar to OpenBSD's strlcpy() function */
+char *pa_strlcpy(char *b, const char *s, size_t l) {
+    pa_assert(b);
+    pa_assert(s);
+    pa_assert(l > 0);
+
+    strncpy(b, s, l);
+    b[l-1] = 0;
+    return b;
+}
+
+/* Make the current thread a realtime thread, and acquire the highest
+ * rtprio we can get that is less or equal the specified parameter. If
+ * the thread is already realtime, don't do anything. */
+int pa_make_realtime(int rtprio) {
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+    struct sched_param sp;
+    int r, policy;
+
+    memset(&sp, 0, sizeof(sp));
+    policy = 0;
+
+    if ((r = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) {
+        pa_log("pthread_getschedgetparam(): %s", pa_cstrerror(r));
+        return -1;
+    }
+
+    if (policy == SCHED_FIFO && sp.sched_priority >= rtprio) {
+        pa_log_info("Thread already being scheduled with SCHED_FIFO with priority %i.", sp.sched_priority);
+        return 0;
+    }
+
+    sp.sched_priority = rtprio;
+    if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) != 0) {
+
+        while (sp.sched_priority > 1) {
+            sp.sched_priority --;
+
+            if ((r = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) == 0) {
+                pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i, which is lower than the requested %i.", sp.sched_priority, rtprio);
+                return 0;
+            }
+        }
+
+        pa_log_warn("pthread_setschedparam(): %s", pa_cstrerror(r));
+        return -1;
+    }
+
+    pa_log_info("Successfully enabled SCHED_FIFO scheduling for thread, with priority %i.", sp.sched_priority);
+    return 0;
+#else
+    return -1;
+#endif
+}
+
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_realtime(void) {
+
+    if (geteuid() == 0)
+        return TRUE;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+    {
+        struct rlimit rl;
+
+        if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
+            if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY)
+                return TRUE;
+    }
+#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
+    {
+        cap_t cap;
+
+        if ((cap = cap_get_proc())) {
+            cap_flag_value_t flag = CAP_CLEAR;
+
+            if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+                if (flag == CAP_SET) {
+                    cap_free(cap);
+                    return TRUE;
+                }
+
+            cap_free(cap);
+        }
+    }
+#endif
+
+    return FALSE;
+}
+
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_high_priority(void) {
+
+    if (geteuid() == 0)
+        return TRUE;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+    {
+        struct rlimit rl;
+
+        if (getrlimit(RLIMIT_NICE, &rl) >= 0)
+            if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY)
+                return TRUE;
+    }
+#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
+    {
+        cap_t cap;
+
+        if ((cap = cap_get_proc())) {
+            cap_flag_value_t flag = CAP_CLEAR;
+
+            if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+                if (flag == CAP_SET) {
+                    cap_free(cap);
+                    return TRUE;
+                }
+
+            cap_free(cap);
+        }
+    }
+#endif
+
+    return FALSE;
+}
+
+/* Raise the priority of the current process as much as possible that
+ * is <= the specified nice level..*/
+int pa_raise_priority(int nice_level) {
+
+#ifdef HAVE_SYS_RESOURCE_H
+    if (setpriority(PRIO_PROCESS, 0, nice_level) < 0) {
+        int n;
+
+        for (n = nice_level+1; n < 0; n++) {
+
+            if (setpriority(PRIO_PROCESS, 0, n) == 0) {
+                pa_log_info("Successfully acquired nice level %i, which is lower than the requested %i.", n, nice_level);
+                return 0;
+            }
+        }
+
+        pa_log_warn("setpriority(): %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_log_info("Successfully gained nice level %i.", nice_level);
+#endif
+
+#ifdef OS_IS_WIN32
+    if (nice_level < 0) {
+        if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
+            pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
+            return .-1;
+        } else
+            pa_log_info("Successfully gained high priority class.");
+    }
+#endif
+
+    return 0;
+}
+
+/* Reset the priority to normal, inverting the changes made by
+ * pa_raise_priority() and pa_make_realtime()*/
+void pa_reset_priority(void) {
+#ifdef HAVE_SYS_RESOURCE_H
+    struct sched_param sp;
+
+    setpriority(PRIO_PROCESS, 0, 0);
+
+    memset(&sp, 0, sizeof(sp));
+    pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp) == 0);
+#endif
+
+#ifdef OS_IS_WIN32
+    SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+#endif
+}
+
+/* Try to parse a boolean string value.*/
+int pa_parse_boolean(const char *v) {
+    pa_assert(v);
+
+    if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+        return 1;
+    else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+        return 0;
+
+    return -1;
+}
+
+/* Split the specified string wherever one of the strings in delimiter
+ * occurs. Each time it is called returns a newly allocated string
+ * with pa_xmalloc(). The variable state points to, should be
+ * initiallized to NULL before the first call. */
+char *pa_split(const char *c, const char *delimiter, const char**state) {
+    const char *current = *state ? *state : c;
+    size_t l;
+
+    if (!*current)
+        return NULL;
+
+    l = strcspn(current, delimiter);
+    *state = current+l;
+
+    if (**state)
+        (*state)++;
+
+    return pa_xstrndup(current, l);
+}
+
+/* What is interpreted as whitespace? */
+#define WHITESPACE " \t\n"
+
+/* Split a string into words. Otherwise similar to pa_split(). */
+char *pa_split_spaces(const char *c, const char **state) {
+    const char *current = *state ? *state : c;
+    size_t l;
+
+    if (!*current || *c == 0)
+        return NULL;
+
+    current += strspn(current, WHITESPACE);
+    l = strcspn(current, WHITESPACE);
+
+    *state = current+l;
+
+    return pa_xstrndup(current, l);
+}
+
+PA_STATIC_TLS_DECLARE(signame, pa_xfree);
+
+/* Return the name of an UNIX signal. Similar to Solaris sig2str() */
+const char *pa_sig2str(int sig) {
+    char *t;
+
+    if (sig <= 0)
+        goto fail;
+
+#ifdef NSIG
+    if (sig >= NSIG)
+        goto fail;
+#endif
+
+#ifdef HAVE_SIG2STR
+    {
+        char buf[SIG2STR_MAX];
+
+        if (sig2str(sig, buf) == 0) {
+            pa_xfree(PA_STATIC_TLS_GET(signame));
+            t = pa_sprintf_malloc("SIG%s", buf);
+            PA_STATIC_TLS_SET(signame, t);
+            return t;
+        }
+    }
+#else
+
+    switch(sig) {
+#ifdef SIGHUP
+        case SIGHUP:    return "SIGHUP";
+#endif
+        case SIGINT:    return "SIGINT";
+#ifdef SIGQUIT
+        case SIGQUIT:   return "SIGQUIT";
+#endif
+        case SIGILL:    return "SIGULL";
+#ifdef SIGTRAP
+        case SIGTRAP:   return "SIGTRAP";
+#endif
+        case SIGABRT:   return "SIGABRT";
+#ifdef SIGBUS
+        case SIGBUS:    return "SIGBUS";
+#endif
+        case SIGFPE:    return "SIGFPE";
+#ifdef SIGKILL
+        case SIGKILL:   return "SIGKILL";
+#endif
+#ifdef SIGUSR1
+        case SIGUSR1:   return "SIGUSR1";
+#endif
+        case SIGSEGV:   return "SIGSEGV";
+#ifdef SIGUSR2
+        case SIGUSR2:   return "SIGUSR2";
+#endif
+#ifdef SIGPIPE
+        case SIGPIPE:   return "SIGPIPE";
+#endif
+#ifdef SIGALRM
+        case SIGALRM:   return "SIGALRM";
+#endif
+        case SIGTERM:   return "SIGTERM";
+#ifdef SIGSTKFLT
+        case SIGSTKFLT: return "SIGSTKFLT";
+#endif
+#ifdef SIGCHLD
+        case SIGCHLD:   return "SIGCHLD";
+#endif
+#ifdef SIGCONT
+        case SIGCONT:   return "SIGCONT";
+#endif
+#ifdef SIGSTOP
+        case SIGSTOP:   return "SIGSTOP";
+#endif
+#ifdef SIGTSTP
+        case SIGTSTP:   return "SIGTSTP";
+#endif
+#ifdef SIGTTIN
+        case SIGTTIN:   return "SIGTTIN";
+#endif
+#ifdef SIGTTOU
+        case SIGTTOU:   return "SIGTTOU";
+#endif
+#ifdef SIGURG
+        case SIGURG:    return "SIGURG";
+#endif
+#ifdef SIGXCPU
+        case SIGXCPU:   return "SIGXCPU";
+#endif
+#ifdef SIGXFSZ
+        case SIGXFSZ:   return "SIGXFSZ";
+#endif
+#ifdef SIGVTALRM
+        case SIGVTALRM: return "SIGVTALRM";
+#endif
+#ifdef SIGPROF
+        case SIGPROF:   return "SIGPROF";
+#endif
+#ifdef SIGWINCH
+        case SIGWINCH:  return "SIGWINCH";
+#endif
+#ifdef SIGIO
+        case SIGIO:     return "SIGIO";
+#endif
+#ifdef SIGPWR
+        case SIGPWR:    return "SIGPWR";
+#endif
+#ifdef SIGSYS
+        case SIGSYS:    return "SIGSYS";
+#endif
+    }
+
+#ifdef SIGRTMIN
+    if (sig >= SIGRTMIN && sig <= SIGRTMAX) {
+        pa_xfree(PA_STATIC_TLS_GET(signame));
+        t = pa_sprintf_malloc("SIGRTMIN+%i", sig - SIGRTMIN);
+        PA_STATIC_TLS_SET(signame, t);
+        return t;
+    }
+#endif
+
+#endif
+
+fail:
+
+    pa_xfree(PA_STATIC_TLS_GET(signame));
+    t = pa_sprintf_malloc("SIG%i", sig);
+    PA_STATIC_TLS_SET(signame, t);
+    return t;
+}
+
+#ifdef HAVE_GRP_H
+
+/* Check whether the specified GID and the group name match */
+static int is_group(gid_t gid, const char *name) {
+    struct group group, *result = NULL;
+    long n;
+    void *data;
+    int r = -1;
+
+#ifdef HAVE_GETGRGID_R
+#ifdef _SC_GETGR_R_SIZE_MAX
+    n = sysconf(_SC_GETGR_R_SIZE_MAX);
+#else
+    n = -1;
+#endif
+    if (n < 0) n = 512;
+    data = pa_xmalloc(n);
+
+    if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) {
+        pa_log("getgrgid_r(%u): %s", (unsigned)gid, pa_cstrerror(errno));
+        goto finish;
+    }
+
+    r = strcmp(name, result->gr_name) == 0;
+
+finish:
+    pa_xfree(data);
+#else
+    /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not
+     * support getgrgid_r. */
+    if ((result = getgrgid(gid)) == NULL) {
+        pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno));
+        goto finish;
+    }
+
+    r = strcmp(name, result->gr_name) == 0;
+
+finish:
+#endif
+
+    return r;
+}
+
+/* Check the current user is member of the specified group */
+int pa_own_uid_in_group(const char *name, gid_t *gid) {
+    GETGROUPS_T *gids, tgid;
+    int n = sysconf(_SC_NGROUPS_MAX);
+    int r = -1, i;
+
+    pa_assert(n > 0);
+
+    gids = pa_xmalloc(sizeof(GETGROUPS_T)*n);
+
+    if ((n = getgroups(n, gids)) < 0) {
+        pa_log("getgroups(): %s", pa_cstrerror(errno));
+        goto finish;
+    }
+
+    for (i = 0; i < n; i++) {
+        if (is_group(gids[i], name) > 0) {
+            *gid = gids[i];
+            r = 1;
+            goto finish;
+        }
+    }
+
+    if (is_group(tgid = getgid(), name) > 0) {
+        *gid = tgid;
+        r = 1;
+        goto finish;
+    }
+
+    r = 0;
+
+finish:
+
+    pa_xfree(gids);
+    return r;
+}
+
+/* Check whether the specifc user id is a member of the specified group */
+int pa_uid_in_group(uid_t uid, const char *name) {
+    char *g_buf, *p_buf;
+    long g_n, p_n;
+    struct group grbuf, *gr;
+    char **i;
+    int r = -1;
+
+    g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
+    g_buf = pa_xmalloc(g_n);
+
+    p_n = sysconf(_SC_GETPW_R_SIZE_MAX);
+    p_buf = pa_xmalloc(p_n);
+
+    if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
+        goto finish;
+
+    r = 0;
+    for (i = gr->gr_mem; *i; i++) {
+        struct passwd pwbuf, *pw;
+
+        if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)
+            continue;
+
+        if (pw->pw_uid == uid) {
+            r = 1;
+            break;
+        }
+    }
+
+finish:
+    pa_xfree(g_buf);
+    pa_xfree(p_buf);
+
+    return r;
+}
+
+/* Get the GID of a gfiven group, return (gid_t) -1 on failure. */
+gid_t pa_get_gid_of_group(const char *name) {
+    gid_t ret = (gid_t) -1;
+    char *g_buf;
+    long g_n;
+    struct group grbuf, *gr;
+
+    g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
+    g_buf = pa_xmalloc(g_n);
+
+    if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
+        goto finish;
+
+    ret = gr->gr_gid;
+
+finish:
+    pa_xfree(g_buf);
+    return ret;
+}
+
+int pa_check_in_group(gid_t g) {
+    gid_t gids[NGROUPS_MAX];
+    int r;
+
+    if ((r = getgroups(NGROUPS_MAX, gids)) < 0)
+        return -1;
+
+    for (; r > 0; r--)
+        if (gids[r-1] == g)
+            return 1;
+
+    return 0;
+}
+
+#else /* HAVE_GRP_H */
+
+int pa_own_uid_in_group(const char *name, gid_t *gid) {
+    return -1;
+
+}
+
+int pa_uid_in_group(uid_t uid, const char *name) {
+    return -1;
+}
+
+gid_t pa_get_gid_of_group(const char *name) {
+    return (gid_t) -1;
+}
+
+int pa_check_in_group(gid_t g) {
+    return -1;
+}
+
+#endif
+
+/* Lock or unlock a file entirely.
+  (advisory on UNIX, mandatory on Windows) */
+int pa_lock_fd(int fd, int b) {
+#ifdef F_SETLKW
+    struct flock flock;
+
+    /* Try a R/W lock first */
+
+    flock.l_type = b ? F_WRLCK : F_UNLCK;
+    flock.l_whence = SEEK_SET;
+    flock.l_start = 0;
+    flock.l_len = 0;
+
+    if (fcntl(fd, F_SETLKW, &flock) >= 0)
+        return 0;
+
+    /* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */
+    if (b && errno == EBADF) {
+        flock.l_type = F_RDLCK;
+        if (fcntl(fd, F_SETLKW, &flock) >= 0)
+            return 0;
+    }
+
+    pa_log("%slock: %s", !b? "un" : "", pa_cstrerror(errno));
+#endif
+
+#ifdef OS_IS_WIN32
+    HANDLE h = (HANDLE)_get_osfhandle(fd);
+
+    if (b && LockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
+        return 0;
+    if (!b && UnlockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
+        return 0;
+
+    pa_log("%slock failed: 0x%08X", !b ? "un" : "", GetLastError());
+#endif
+
+    return -1;
+}
+
+/* Remove trailing newlines from a string */
+char* pa_strip_nl(char *s) {
+    pa_assert(s);
+
+    s[strcspn(s, "\r\n")] = 0;
+    return s;
+}
+
+/* Create a temporary lock file and lock it. */
+int pa_lock_lockfile(const char *fn) {
+    int fd = -1;
+    pa_assert(fn);
+
+    for (;;) {
+        struct stat st;
+
+        if ((fd = open(fn, O_CREAT|O_RDWR
+#ifdef O_NOCTTY
+                       |O_NOCTTY
+#endif
+#ifdef O_NOFOLLOW
+                       |O_NOFOLLOW
+#endif
+                       , S_IRUSR|S_IWUSR)) < 0) {
+            pa_log_warn("Failed to create lock file '%s': %s", fn, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        if (pa_lock_fd(fd, 1) < 0) {
+            pa_log_warn("Failed to lock file '%s'.", fn);
+            goto fail;
+        }
+
+        if (fstat(fd, &st) < 0) {
+            pa_log_warn("Failed to fstat() file '%s': %s", fn, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        /* Check wheter the file has been removed meanwhile. When yes,
+         * restart this loop, otherwise, we're done */
+        if (st.st_nlink >= 1)
+            break;
+
+        if (pa_lock_fd(fd, 0) < 0) {
+            pa_log_warn("Failed to unlock file '%s'.", fn);
+            goto fail;
+        }
+
+        if (pa_close(fd) < 0) {
+            pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno));
+            fd = -1;
+            goto fail;
+        }
+
+        fd = -1;
+    }
+
+    return fd;
+
+fail:
+
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
+
+/* Unlock a temporary lcok file */
+int pa_unlock_lockfile(const char *fn, int fd) {
+    int r = 0;
+    pa_assert(fd >= 0);
+
+    if (fn) {
+        if (unlink(fn) < 0) {
+            pa_log_warn("Unable to remove lock file '%s': %s", fn, pa_cstrerror(errno));
+            r = -1;
+        }
+    }
+
+    if (pa_lock_fd(fd, 0) < 0) {
+        pa_log_warn("Failed to unlock file '%s'.", fn);
+        r = -1;
+    }
+
+    if (pa_close(fd) < 0) {
+        pa_log_warn("Failed to close '%s': %s", fn, pa_cstrerror(errno));
+        r = -1;
+    }
+
+    return r;
+}
+
+static char *get_dir(mode_t m, const char *env_name) {
+    const char *e;
+    char *d;
+
+    if ((e = getenv(env_name)))
+        d = pa_xstrdup(e);
+    else {
+        char h[PATH_MAX];
+        struct stat st;
+
+        if (!pa_get_home_dir(h, sizeof(h))) {
+            pa_log_error("Failed to get home directory.");
+            return NULL;
+        }
+
+        if (stat(h, &st) < 0) {
+            pa_log_error("Failed to stat home directory %s: %s", h, pa_cstrerror(errno));
+            return NULL;
+        }
+
+        if (st.st_uid != getuid()) {
+            pa_log_error("Home directory %s not ours.", d);
+            return NULL;
+        }
+
+        d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
+    }
+
+    if (pa_make_secure_dir(d, m, (pid_t) -1, (pid_t) -1) < 0)  {
+        pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+        return NULL;
+    }
+
+    return d;
+}
+
+char *pa_get_runtime_dir(void) {
+    return get_dir(pa_in_system_mode() ? 0755 : 0700, "PULSE_RUNTIME_PATH");
+}
+
+char *pa_get_state_dir(void) {
+    return get_dir(0700, "PULSE_STATE_PATH");
+}
+
+/* Try to open a configuration file. If "env" is specified, open the
+ * value of the specified environment variable. Otherwise look for a
+ * file "local" in the home directory or a file "global" in global
+ * file system. If "result" is non-NULL, a pointer to a newly
+ * allocated buffer containing the used configuration file is
+ * stored there.*/
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
+    const char *fn;
+#ifdef OS_IS_WIN32
+    char buf[PATH_MAX];
+
+    if (!getenv(PULSE_ROOTENV))
+        pa_set_root(NULL);
+#endif
+
+    if (env && (fn = getenv(env))) {
+        FILE *f;
+
+#ifdef OS_IS_WIN32
+        if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
+            return NULL;
+        fn = buf;
+#endif
+
+        if ((f = fopen(fn, "r"))) {
+            if (result)
+                *result = pa_xstrdup(fn);
+
+            return f;
+        }
+
+        pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+        return NULL;
+    }
+
+    if (local) {
+        const char *e;
+        char *lfn;
+        char h[PATH_MAX];
+        FILE *f;
+
+        if ((e = getenv("PULSE_CONFIG_PATH")))
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+        else if (pa_get_home_dir(h, sizeof(h)))
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
+
+#ifdef OS_IS_WIN32
+        if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+            pa_xfree(lfn);
+            return NULL;
+        }
+        fn = buf;
+#endif
+
+        if ((f = fopen(fn, "r"))) {
+            if (result)
+                *result = pa_xstrdup(fn);
+
+            pa_xfree(lfn);
+            return f;
+        }
+
+        if (errno != ENOENT) {
+            pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+            pa_xfree(lfn);
+            return NULL;
+        }
+
+        pa_xfree(lfn);
+    }
+
+    if (global) {
+        FILE *f;
+
+#ifdef OS_IS_WIN32
+        if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+            return NULL;
+        global = buf;
+#endif
+
+        if ((f = fopen(global, "r"))) {
+
+            if (result)
+                *result = pa_xstrdup(global);
+
+            return f;
+        }
+    } else
+        errno = ENOENT;
+
+    return NULL;
+}
+
+char *pa_find_config_file(const char *global, const char *local, const char *env) {
+    const char *fn;
+#ifdef OS_IS_WIN32
+    char buf[PATH_MAX];
+
+    if (!getenv(PULSE_ROOTENV))
+        pa_set_root(NULL);
+#endif
+
+    if (env && (fn = getenv(env))) {
+#ifdef OS_IS_WIN32
+        if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
+            return NULL;
+        fn = buf;
+#endif
+
+        if (access(fn, R_OK) == 0)
+            return pa_xstrdup(fn);
+
+        pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
+        return NULL;
+    }
+
+    if (local) {
+        const char *e;
+        char *lfn;
+        char h[PATH_MAX];
+
+        if ((e = getenv("PULSE_CONFIG_PATH")))
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+        else if (pa_get_home_dir(h, sizeof(h)))
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
+
+#ifdef OS_IS_WIN32
+        if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+            pa_xfree(lfn);
+            return NULL;
+        }
+        fn = buf;
+#endif
+
+        if (access(fn, R_OK) == 0) {
+            char *r = pa_xstrdup(fn);
+            pa_xfree(lfn);
+            return r;
+        }
+
+        if (errno != ENOENT) {
+            pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
+            pa_xfree(lfn);
+            return NULL;
+        }
+
+        pa_xfree(lfn);
+    }
+
+    if (global) {
+#ifdef OS_IS_WIN32
+        if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+            return NULL;
+        global = buf;
+#endif
+
+        if (access(fn, R_OK) == 0)
+            return pa_xstrdup(global);
+    } else
+        errno = ENOENT;
+
+    return NULL;
+}
+
+/* Format the specified data as a hexademical string */
+char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) {
+    size_t i = 0, j = 0;
+    const char hex[] = "0123456789abcdef";
+
+    pa_assert(d);
+    pa_assert(s);
+    pa_assert(slength > 0);
+
+    while (i < dlength && j+3 <= slength) {
+        s[j++] = hex[*d >> 4];
+        s[j++] = hex[*d & 0xF];
+
+        d++;
+        i++;
+    }
+
+    s[j < slength ? j : slength] = 0;
+    return s;
+}
+
+/* Convert a hexadecimal digit to a number or -1 if invalid */
+static int hexc(char c) {
+    if (c >= '0' && c <= '9')
+        return c - '0';
+
+    if (c >= 'A' && c <= 'F')
+        return c - 'A' + 10;
+
+    if (c >= 'a' && c <= 'f')
+        return c - 'a' + 10;
+
+    return -1;
+}
+
+/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */
+size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) {
+    size_t j = 0;
+
+    pa_assert(p);
+    pa_assert(d);
+
+    while (j < dlength && *p) {
+        int b;
+
+        if ((b = hexc(*(p++))) < 0)
+            return (size_t) -1;
+
+        d[j] = (uint8_t) (b << 4);
+
+        if (!*p)
+            return (size_t) -1;
+
+        if ((b = hexc(*(p++))) < 0)
+            return (size_t) -1;
+
+        d[j] |= (uint8_t) b;
+        j++;
+    }
+
+    return j;
+}
+
+/* Returns nonzero when *s starts with *pfx */
+pa_bool_t pa_startswith(const char *s, const char *pfx) {
+    size_t l;
+
+    pa_assert(s);
+    pa_assert(pfx);
+
+    l = strlen(pfx);
+
+    return strlen(s) >= l && strncmp(s, pfx, l) == 0;
+}
+
+/* Returns nonzero when *s ends with *sfx */
+pa_bool_t pa_endswith(const char *s, const char *sfx) {
+    size_t l1, l2;
+
+    pa_assert(s);
+    pa_assert(sfx);
+
+    l1 = strlen(s);
+    l2 = strlen(sfx);
+
+    return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
+}
+
+pa_bool_t pa_is_path_absolute(const char *fn) {
+    pa_assert(fn);
+
+#ifndef OS_IS_WIN32
+    return *fn == '/';
+#else
+    return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\';
+#endif
+}
+
+char *pa_make_path_absolute(const char *p) {
+    char *r;
+    char *cwd;
+
+    pa_assert(p);
+
+    if (pa_is_path_absolute(p))
+        return pa_xstrdup(p);
+
+    if (!(cwd = pa_getcwd()))
+        return pa_xstrdup(p);
+
+    r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p);
+    pa_xfree(cwd);
+    return r;
+}
+
+/* if fn is null return the PulseAudio run time path in s (~/.pulse)
+ * if fn is non-null and starts with / return fn
+ * otherwise append fn to the run time path and return it */
+static char *get_path(const char *fn, pa_bool_t rt) {
+    char *rtp;
+
+    if (pa_is_path_absolute(fn))
+        return pa_xstrdup(fn);
+
+    rtp = rt ? pa_get_runtime_dir() : pa_get_state_dir();
+
+    if (!rtp)
+        return NULL;
+
+    if (fn) {
+        char *r;
+        r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
+        pa_xfree(rtp);
+        return r;
+    } else
+        return rtp;
+}
+
+char *pa_runtime_path(const char *fn) {
+    return get_path(fn, 1);
+}
+
+char *pa_state_path(const char *fn) {
+    return get_path(fn, 0);
+}
+
+/* Convert the string s to a signed integer in *ret_i */
+int pa_atoi(const char *s, int32_t *ret_i) {
+    char *x = NULL;
+    long l;
+
+    pa_assert(s);
+    pa_assert(ret_i);
+
+    errno = 0;
+    l = strtol(s, &x, 0);
+
+    if (!x || *x || errno != 0)
+        return -1;
+
+    if ((int32_t) l != l)
+        return -1;
+
+    *ret_i = (int32_t) l;
+
+    return 0;
+}
+
+/* Convert the string s to an unsigned integer in *ret_u */
+int pa_atou(const char *s, uint32_t *ret_u) {
+    char *x = NULL;
+    unsigned long l;
+
+    pa_assert(s);
+    pa_assert(ret_u);
+
+    errno = 0;
+    l = strtoul(s, &x, 0);
+
+    if (!x || *x || errno != 0)
+        return -1;
+
+    if ((uint32_t) l != l)
+        return -1;
+
+    *ret_u = (uint32_t) l;
+
+    return 0;
+}
+
+#ifdef HAVE_STRTOF_L
+static locale_t c_locale = NULL;
+
+static void c_locale_destroy(void) {
+    freelocale(c_locale);
+}
+#endif
+
+int pa_atod(const char *s, double *ret_d) {
+    char *x = NULL;
+    double f;
+    int r = 0;
+
+    pa_assert(s);
+    pa_assert(ret_d);
+
+    /* This should be locale independent */
+
+#ifdef HAVE_STRTOF_L
+
+    PA_ONCE_BEGIN {
+
+        if ((c_locale = newlocale(LC_ALL_MASK, "C", NULL)))
+            atexit(c_locale_destroy);
+
+    } PA_ONCE_END;
+
+    if (c_locale) {
+        errno = 0;
+        f = strtod_l(s, &x, c_locale);
+    } else
+#endif
+    {
+        errno = 0;
+        f = strtod(s, &x);
+    }
+
+    if (!x || *x || errno != 0)
+        r =  -1;
+    else
+        *ret_d = f;
+
+    return r;
+}
+
+/* Same as snprintf, but guarantees NUL-termination on every platform */
+int pa_snprintf(char *str, size_t size, const char *format, ...) {
+    int ret;
+    va_list ap;
+
+    pa_assert(str);
+    pa_assert(size > 0);
+    pa_assert(format);
+
+    va_start(ap, format);
+    ret = pa_vsnprintf(str, size, format, ap);
+    va_end(ap);
+
+    return ret;
+}
+
+/* Same as vsnprintf, but guarantees NUL-termination on every platform */
+int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+    int ret;
+
+    pa_assert(str);
+    pa_assert(size > 0);
+    pa_assert(format);
+
+    ret = vsnprintf(str, size, format, ap);
+
+    str[size-1] = 0;
+
+    if (ret < 0)
+        ret = strlen(str);
+
+    return PA_MIN((int) size-1, ret);
+}
+
+/* Truncate the specified string, but guarantee that the string
+ * returned still validates as UTF8 */
+char *pa_truncate_utf8(char *c, size_t l) {
+    pa_assert(c);
+    pa_assert(pa_utf8_valid(c));
+
+    if (strlen(c) <= l)
+        return c;
+
+    c[l] = 0;
+
+    while (l > 0 && !pa_utf8_valid(c))
+        c[--l] = 0;
+
+    return c;
+}
+
+char *pa_getcwd(void) {
+    size_t l = 128;
+
+    for (;;) {
+        char *p = pa_xnew(char, l);
+        if (getcwd(p, l))
+            return p;
+
+        if (errno != ERANGE)
+            return NULL;
+
+        pa_xfree(p);
+        l *= 2;
+    }
+}
+
+void *pa_will_need(const void *p, size_t l) {
+#ifdef RLIMIT_MEMLOCK
+    struct rlimit rlim;
+#endif
+    const void *a;
+    size_t size;
+    int r;
+    size_t bs;
+
+    pa_assert(p);
+    pa_assert(l > 0);
+
+    a = PA_PAGE_ALIGN_PTR(p);
+    size = (const uint8_t*) p + l - (const uint8_t*) a;
+
+#ifdef HAVE_POSIX_MADVISE
+    if ((r = posix_madvise((void*) a, size, POSIX_MADV_WILLNEED)) == 0) {
+        pa_log_debug("posix_madvise() worked fine!");
+        return (void*) p;
+    }
+#endif
+
+    /* Most likely the memory was not mmap()ed from a file and thus
+     * madvise() didn't work, so let's misuse mlock() do page this
+     * stuff back into RAM. Yeah, let's fuck with the MM!  It's so
+     * inviting, the man page of mlock() tells us: "All pages that
+     * contain a part of the specified address range are guaranteed to
+     * be resident in RAM when the call returns successfully." */
+
+#ifdef RLIMIT_MEMLOCK
+    pa_assert_se(getrlimit(RLIMIT_MEMLOCK, &rlim) == 0);
+
+    if (rlim.rlim_cur < PA_PAGE_SIZE) {
+        pa_log_debug("posix_madvise() failed (or doesn't exist), resource limits don't allow mlock(), can't page in data: %s", pa_cstrerror(r));
+        return (void*) p;
+    }
+
+    bs = PA_PAGE_ALIGN(rlim.rlim_cur);
+#else
+    bs = PA_PAGE_SIZE*4;
+#endif
+
+    pa_log_debug("posix_madvise() failed (or doesn't exist), trying mlock(): %s", pa_cstrerror(r));
+
+#ifdef HAVE_MLOCK
+    while (size > 0 && bs > 0) {
+
+        if (bs > size)
+            bs = size;
+
+        if (mlock(a, bs) < 0) {
+            bs = PA_PAGE_ALIGN(bs / 2);
+            continue;
+        }
+
+        pa_assert_se(munlock(a, bs) == 0);
+
+        a = (const uint8_t*) a + bs;
+        size -= bs;
+    }
+#endif
+
+    if (bs <= 0)
+        pa_log_debug("mlock() failed too (or doesn't exist), giving up: %s", pa_cstrerror(errno));
+    else
+        pa_log_debug("mlock() worked fine!");
+
+    return (void*) p;
+}
+
+void pa_close_pipe(int fds[2]) {
+    pa_assert(fds);
+
+    if (fds[0] >= 0)
+        pa_assert_se(pa_close(fds[0]) == 0);
+
+    if (fds[1] >= 0)
+        pa_assert_se(pa_close(fds[1]) == 0);
+
+    fds[0] = fds[1] = -1;
+}
+
+char *pa_readlink(const char *p) {
+    size_t l = 100;
+
+    for (;;) {
+        char *c;
+        ssize_t n;
+
+        c = pa_xnew(char, l);
+
+        if ((n = readlink(p, c, l-1)) < 0) {
+            pa_xfree(c);
+            return NULL;
+        }
+
+        if ((size_t) n < l-1) {
+            c[n] = 0;
+            return c;
+        }
+
+        pa_xfree(c);
+        l *= 2;
+    }
+}
+
+int pa_close_all(int except_fd, ...) {
+    va_list ap;
+    int n = 0, i, r;
+    int *p;
+
+    va_start(ap, except_fd);
+
+    if (except_fd >= 0)
+        for (n = 1; va_arg(ap, int) >= 0; n++)
+            ;
+
+    va_end(ap);
+
+    p = pa_xnew(int, n+1);
+
+    va_start(ap, except_fd);
+
+    i = 0;
+    if (except_fd >= 0) {
+        int fd;
+        p[i++] = except_fd;
+
+        while ((fd = va_arg(ap, int)) >= 0)
+            p[i++] = fd;
+    }
+    p[i] = -1;
+
+    va_end(ap);
+
+    r = pa_close_allv(p);
+    free(p);
+
+    return r;
+}
+
+int pa_close_allv(const int except_fds[]) {
+    struct rlimit rl;
+    int fd;
+    int saved_errno;
+
+#ifdef __linux__
+
+    DIR *d;
+
+    if ((d = opendir("/proc/self/fd"))) {
+
+        struct dirent *de;
+
+        while ((de = readdir(d))) {
+            pa_bool_t found;
+            long l;
+            char *e = NULL;
+            int i;
+
+            if (de->d_name[0] == '.')
+                continue;
+
+            errno = 0;
+            l = strtol(de->d_name, &e, 10);
+            if (errno != 0 || !e || *e) {
+                closedir(d);
+                errno = EINVAL;
+                return -1;
+            }
+
+            fd = (int) l;
+
+            if ((long) fd != l) {
+                closedir(d);
+                errno = EINVAL;
+                return -1;
+            }
+
+            if (fd < 3)
+                continue;
+
+            if (fd == dirfd(d))
+                continue;
+
+            found = FALSE;
+            for (i = 0; except_fds[i] >= 0; i++)
+                if (except_fds[i] == fd) {
+                    found = TRUE;
+                    break;
+                }
+
+            if (found)
+                continue;
+
+            if (pa_close(fd) < 0) {
+                saved_errno = errno;
+                closedir(d);
+                errno = saved_errno;
+
+                return -1;
+            }
+        }
+
+        closedir(d);
+        return 0;
+    }
+
+#endif
+
+    if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
+        return -1;
+
+    for (fd = 0; fd < (int) rl.rlim_max; fd++) {
+        int i;
+
+        if (fd <= 3)
+            continue;
+
+        for (i = 0; except_fds[i] >= 0; i++)
+            if (except_fds[i] == fd)
+                continue;
+
+        if (close(fd) < 0 && errno != EBADF)
+            return -1;
+    }
+
+    return 0;
+}
+
+int pa_unblock_sigs(int except, ...) {
+    va_list ap;
+    int n = 0, i, r;
+    int *p;
+
+    va_start(ap, except);
+
+    if (except >= 1)
+        for (n = 1; va_arg(ap, int) >= 0; n++)
+            ;
+
+    va_end(ap);
+
+    p = pa_xnew(int, n+1);
+
+    va_start(ap, except);
+
+    i = 0;
+    if (except >= 1) {
+        int sig;
+        p[i++] = except;
+
+        while ((sig = va_arg(ap, int)) >= 0)
+            p[i++] = sig;
+    }
+    p[i] = -1;
+
+    va_end(ap);
+
+    r = pa_unblock_sigsv(p);
+    pa_xfree(p);
+
+    return r;
+}
+
+int pa_unblock_sigsv(const int except[]) {
+    int i;
+    sigset_t ss;
+
+    if (sigemptyset(&ss) < 0)
+        return -1;
+
+    for (i = 0; except[i] > 0; i++)
+        if (sigaddset(&ss, except[i]) < 0)
+            return -1;
+
+    return sigprocmask(SIG_SETMASK, &ss, NULL);
+}
+
+int pa_reset_sigs(int except, ...) {
+    va_list ap;
+    int n = 0, i, r;
+    int *p;
+
+    va_start(ap, except);
+
+    if (except >= 1)
+        for (n = 1; va_arg(ap, int) >= 0; n++)
+            ;
+
+    va_end(ap);
+
+    p = pa_xnew(int, n+1);
+
+    va_start(ap, except);
+
+    i = 0;
+    if (except >= 1) {
+        p[i++] = except;
+
+        while ((p[i++] = va_arg(ap, int)) >= 0)
+            ;
+    }
+    p[i] = -1;
+
+    va_end(ap);
+
+    r = pa_reset_sigsv(p);
+    pa_xfree(p);
+
+    return r;
+}
+
+int pa_reset_sigsv(const int except[]) {
+    int sig;
+
+    for (sig = 1; sig < _NSIG; sig++) {
+        pa_bool_t reset = TRUE;
+
+        switch (sig) {
+            case SIGKILL:
+            case SIGSTOP:
+                reset = FALSE;
+                break;
+
+            default: {
+                int i;
+
+                for (i = 0; except[i] > 0; i++) {
+                    if (sig == except[i]) {
+                        reset = FALSE;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (reset) {
+            struct sigaction sa;
+
+            memset(&sa, 0, sizeof(sa));
+            sa.sa_handler = SIG_DFL;
+
+            /* On Linux the first two RT signals are reserved by
+             * glibc, and sigaction() will return EINVAL for them. */
+            if ((sigaction(sig, &sa, NULL) < 0))
+                if (errno != EINVAL)
+                    return -1;
+        }
+    }
+
+    return 0;
+}
+
+void pa_set_env(const char *key, const char *value) {
+    pa_assert(key);
+    pa_assert(value);
+
+    putenv(pa_sprintf_malloc("%s=%s", key, value));
+}
+
+pa_bool_t pa_in_system_mode(void) {
+    const char *e;
+
+    if (!(e = getenv("PULSE_SYSTEM")))
+        return FALSE;
+
+    return !!atoi(e);
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
new file mode 100644 (file)
index 0000000..2ed81fc
--- /dev/null
@@ -0,0 +1,187 @@
+#ifndef foocoreutilhfoo
+#define foocoreutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <pulse/gccmacro.h>
+#include <pulsecore/macro.h>
+
+struct timeval;
+
+/* These resource limits are pretty new on Linux, let's define them
+ * here manually, in case the kernel is newer than the glibc */
+#if !defined(RLIMIT_NICE) && defined(__linux__)
+#define RLIMIT_NICE 13
+#endif
+#if !defined(RLIMIT_RTPRIO) && defined(__linux__)
+#define RLIMIT_RTPRIO 14
+#endif
+#if !defined(RLIMIT_RTTIME) && defined(__linux__)
+#define RLIMIT_RTTIME 15
+#endif
+
+void pa_make_fd_nonblock(int fd);
+void pa_make_fd_cloexec(int fd);
+
+int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid);
+int pa_make_secure_parent_dir(const char *fn, mode_t, uid_t uid, gid_t gid);
+
+ssize_t pa_read(int fd, void *buf, size_t count, int *type);
+ssize_t pa_write(int fd, const void *buf, size_t count, int *type);
+ssize_t pa_loop_read(int fd, void*data, size_t size, int *type);
+ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type);
+
+int pa_close(int fd);
+
+void pa_check_signal_is_blocked(int sig);
+
+char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
+char *pa_vsprintf_malloc(const char *format, va_list ap);
+
+char *pa_strlcpy(char *b, const char *s, size_t l);
+
+char *pa_parent_dir(const char *fn);
+
+int pa_make_realtime(int rtprio);
+int pa_raise_priority(int nice_level);
+void pa_reset_priority(void);
+
+pa_bool_t pa_can_realtime(void);
+pa_bool_t pa_can_high_priority(void);
+
+int pa_parse_boolean(const char *s) PA_GCC_PURE;
+
+static inline const char *pa_yes_no(pa_bool_t b) {
+    return b ? "yes" : "no";
+}
+
+static inline const char *pa_strnull(const char *x) {
+    return x ? x : "(null)";
+}
+
+static inline const char *pa_strempty(const char *x) {
+    return x ? x : "";
+}
+
+char *pa_split(const char *c, const char*delimiters, const char **state);
+char *pa_split_spaces(const char *c, const char **state);
+
+char *pa_strip_nl(char *s);
+
+const char *pa_sig2str(int sig) PA_GCC_PURE;
+
+int pa_own_uid_in_group(const char *name, gid_t *gid);
+int pa_uid_in_group(uid_t uid, const char *name);
+gid_t pa_get_gid_of_group(const char *name);
+int pa_check_in_group(gid_t g);
+
+int pa_lock_fd(int fd, int b);
+
+int pa_lock_lockfile(const char *fn);
+int pa_unlock_lockfile(const char *fn, int fd);
+
+char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
+size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
+
+pa_bool_t pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
+pa_bool_t pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
+
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result);
+char* pa_find_config_file(const char *global, const char *local, const char *env);
+
+char *pa_get_runtime_dir(void);
+char *pa_get_state_dir(void);
+char *pa_runtime_path(const char *fn);
+char *pa_state_path(const char *fn);
+
+int pa_atoi(const char *s, int32_t *ret_i);
+int pa_atou(const char *s, uint32_t *ret_u);
+int pa_atod(const char *s, double *ret_d);
+
+int pa_snprintf(char *str, size_t size, const char *format, ...);
+int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
+
+char *pa_truncate_utf8(char *c, size_t l);
+
+char *pa_getcwd(void);
+char *pa_make_path_absolute(const char *p);
+pa_bool_t pa_is_path_absolute(const char *p);
+
+void *pa_will_need(const void *p, size_t l);
+
+static inline int pa_is_power_of_two(unsigned n) {
+    return !(n & (n - 1));
+}
+
+static inline unsigned pa_make_power_of_two(unsigned n) {
+    unsigned j = n;
+
+    if (pa_is_power_of_two(n))
+        return n;
+
+    while (j) {
+        j = j >> 1;
+        n = n | j;
+    }
+
+    return n + 1;
+}
+
+static inline unsigned pa_ulog2(unsigned n) {
+    unsigned r = 0;
+
+    while (n) {
+        r++;
+        n = n >> 1;
+    }
+
+    return r;
+}
+
+void pa_close_pipe(int fds[2]);
+
+char *pa_readlink(const char *p);
+
+int pa_close_all(int except_fd, ...);
+int pa_close_allv(const int except_fds[]);
+int pa_unblock_sigs(int except, ...);
+int pa_unblock_sigsv(const int except[]);
+int pa_reset_sigs(int except, ...);
+int pa_reset_sigsv(const int except[]);
+
+void pa_set_env(const char *key, const char *value);
+
+pa_bool_t pa_in_system_mode(void);
+
+#define pa_streq(a,b) (!strcmp((a),(b)))
+
+#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
new file mode 100644 (file)
index 0000000..b2638b1
--- /dev/null
@@ -0,0 +1,224 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/module.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/autoload.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/props.h>
+#include <pulsecore/random.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "core.h"
+
+static PA_DEFINE_CHECK_TYPE(pa_core, pa_msgobject);
+
+static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_core *c = PA_CORE(o);
+
+    pa_core_assert_ref(c);
+
+    switch (code) {
+
+        case PA_CORE_MESSAGE_UNLOAD_MODULE:
+            pa_module_unload(c, userdata);
+            return 0;
+
+        default:
+            return -1;
+    }
+}
+
+static void core_free(pa_object *o);
+
+pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
+    pa_core* c;
+    pa_mempool *pool;
+    int j;
+
+    pa_assert(m);
+
+    if (shared) {
+        if (!(pool = pa_mempool_new(shared))) {
+            pa_log_warn("failed to allocate shared memory pool. Falling back to a normal memory pool.");
+            shared = 0;
+        }
+    }
+
+    if (!shared) {
+        if (!(pool = pa_mempool_new(shared))) {
+            pa_log("pa_mempool_new() failed.");
+            return NULL;
+        }
+    }
+
+    c = pa_msgobject_new(pa_core);
+    c->parent.parent.free = core_free;
+    c->parent.process_msg = core_process_msg;
+
+    c->mainloop = m;
+    c->clients = pa_idxset_new(NULL, NULL);
+    c->sinks = pa_idxset_new(NULL, NULL);
+    c->sources = pa_idxset_new(NULL, NULL);
+    c->source_outputs = pa_idxset_new(NULL, NULL);
+    c->sink_inputs = pa_idxset_new(NULL, NULL);
+
+    c->default_source_name = c->default_sink_name = NULL;
+
+    c->modules = NULL;
+    c->namereg = NULL;
+    c->scache = NULL;
+    c->autoload_idxset = NULL;
+    c->autoload_hashmap = NULL;
+    c->running_as_daemon = FALSE;
+
+    c->default_sample_spec.format = PA_SAMPLE_S16NE;
+    c->default_sample_spec.rate = 44100;
+    c->default_sample_spec.channels = 2;
+    c->default_n_fragments = 4;
+    c->default_fragment_size_msec = 25;
+
+    c->module_auto_unload_event = NULL;
+    c->module_defer_unload_event = NULL;
+    c->scache_auto_unload_event = NULL;
+
+    c->subscription_defer_event = NULL;
+    PA_LLIST_HEAD_INIT(pa_subscription, c->subscriptions);
+    PA_LLIST_HEAD_INIT(pa_subscription_event, c->subscription_event_queue);
+    c->subscription_event_last = NULL;
+
+    c->mempool = pool;
+    pa_silence_cache_init(&c->silence_cache);
+
+    c->quit_event = NULL;
+
+    c->exit_idle_time = -1;
+    c->module_idle_time = 20;
+    c->scache_idle_time = 20;
+
+    c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
+
+    c->disallow_module_loading = FALSE;
+    c->realtime_scheduling = FALSE;
+    c->realtime_priority = 5;
+    c->disable_remixing = FALSE;
+
+    for (j = 0; j < PA_CORE_HOOK_MAX; j++)
+        pa_hook_init(&c->hooks[j], c);
+
+    pa_property_init(c);
+
+    pa_random(&c->cookie, sizeof(c->cookie));
+
+#ifdef SIGPIPE
+    pa_check_signal_is_blocked(SIGPIPE);
+#endif
+
+    return c;
+}
+
+static void core_free(pa_object *o) {
+    pa_core *c = PA_CORE(o);
+    int j;
+    pa_assert(c);
+
+    pa_module_unload_all(c);
+    pa_assert(!c->modules);
+
+    pa_assert(pa_idxset_isempty(c->clients));
+    pa_idxset_free(c->clients, NULL, NULL);
+
+    pa_assert(pa_idxset_isempty(c->sinks));
+    pa_idxset_free(c->sinks, NULL, NULL);
+
+    pa_assert(pa_idxset_isempty(c->sources));
+    pa_idxset_free(c->sources, NULL, NULL);
+
+    pa_assert(pa_idxset_isempty(c->source_outputs));
+    pa_idxset_free(c->source_outputs, NULL, NULL);
+
+    pa_assert(pa_idxset_isempty(c->sink_inputs));
+    pa_idxset_free(c->sink_inputs, NULL, NULL);
+
+    pa_scache_free(c);
+    pa_namereg_free(c);
+    pa_autoload_free(c);
+    pa_subscription_free_all(c);
+
+    if (c->quit_event)
+        c->mainloop->time_free(c->quit_event);
+
+    pa_xfree(c->default_source_name);
+    pa_xfree(c->default_sink_name);
+
+    pa_silence_cache_done(&c->silence_cache);
+    pa_mempool_free(c->mempool);
+
+    pa_property_cleanup(c);
+
+    for (j = 0; j < PA_CORE_HOOK_MAX; j++)
+        pa_hook_free(&c->hooks[j]);
+
+    pa_xfree(c);
+}
+
+static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+    pa_core *c = userdata;
+    pa_assert(c->quit_event == e);
+
+    m->quit(m, 0);
+}
+
+void pa_core_check_quit(pa_core *c) {
+    pa_assert(c);
+
+    if (!c->quit_event &&
+        c->exit_idle_time >= 0 &&
+        pa_idxset_size(c->clients) == 0) {
+
+        struct timeval tv;
+        pa_gettimeofday(&tv);
+        tv.tv_sec+= c->exit_idle_time;
+
+        c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c);
+
+    } else if (c->quit_event && pa_idxset_size(c->clients) > 0) {
+        c->mainloop->time_free(c->quit_event);
+        c->quit_event = NULL;
+    }
+}
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
new file mode 100644 (file)
index 0000000..d9ed46f
--- /dev/null
@@ -0,0 +1,145 @@
+#ifndef foocorehfoo
+#define foocorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulse/sample.h>
+
+#include <pulsecore/idxset.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/queue.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/sample-util.h>
+
+typedef struct pa_core pa_core;
+
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/msgobject.h>
+
+typedef enum pa_core_hook {
+    PA_CORE_HOOK_SINK_NEW,
+    PA_CORE_HOOK_SINK_FIXATE,
+    PA_CORE_HOOK_SINK_PUT,
+    PA_CORE_HOOK_SINK_UNLINK,
+    PA_CORE_HOOK_SINK_UNLINK_POST,
+    PA_CORE_HOOK_SINK_STATE_CHANGED,
+    PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SOURCE_NEW,
+    PA_CORE_HOOK_SOURCE_FIXATE,
+    PA_CORE_HOOK_SOURCE_PUT,
+    PA_CORE_HOOK_SOURCE_UNLINK,
+    PA_CORE_HOOK_SOURCE_UNLINK_POST,
+    PA_CORE_HOOK_SOURCE_STATE_CHANGED,
+    PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SINK_INPUT_NEW,
+    PA_CORE_HOOK_SINK_INPUT_FIXATE,
+    PA_CORE_HOOK_SINK_INPUT_PUT,
+    PA_CORE_HOOK_SINK_INPUT_UNLINK,
+    PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
+    PA_CORE_HOOK_SINK_INPUT_MOVE,
+    PA_CORE_HOOK_SINK_INPUT_MOVE_POST,
+    PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
+    PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
+    PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
+    PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
+    PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK,
+    PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
+    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE,
+    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST,
+    PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
+    PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_MAX
+} pa_core_hook_t;
+
+/* The core structure of PulseAudio. Every PulseAudio daemon contains
+ * exactly one of these. It is used for storing kind of global
+ * variables for the daemon. */
+
+struct pa_core {
+    pa_msgobject parent;
+
+    /* A random value which may be used to identify this instance of
+     * PulseAudio. Not cryptographically secure in any way. */
+    uint32_t cookie;
+
+    pa_mainloop_api *mainloop;
+
+    /* idxset of all kinds of entities */
+    pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset;
+
+    /* Some hashmaps for all sorts of entities */
+    pa_hashmap *namereg, *autoload_hashmap, *properties;
+
+    /* The name of the default sink/source */
+    char *default_source_name, *default_sink_name;
+
+    pa_sample_spec default_sample_spec;
+    unsigned default_n_fragments, default_fragment_size_msec;
+
+    pa_time_event *module_auto_unload_event;
+    pa_defer_event *module_defer_unload_event;
+
+    pa_defer_event *subscription_defer_event;
+    PA_LLIST_HEAD(pa_subscription, subscriptions);
+    PA_LLIST_HEAD(pa_subscription_event, subscription_event_queue);
+    pa_subscription_event *subscription_event_last;
+
+    pa_mempool *mempool;
+    pa_silence_cache silence_cache;
+
+    int exit_idle_time, module_idle_time, scache_idle_time;
+
+    pa_time_event *quit_event;
+
+    pa_time_event *scache_auto_unload_event;
+
+    pa_bool_t disallow_module_loading, running_as_daemon;
+    pa_resample_method_t resample_method;
+    pa_bool_t realtime_scheduling;
+    int realtime_priority;
+    pa_bool_t disable_remixing;
+
+    /* hooks */
+    pa_hook hooks[PA_CORE_HOOK_MAX];
+};
+
+PA_DECLARE_CLASS(pa_core);
+#define PA_CORE(o) pa_core_cast(o)
+
+enum {
+    PA_CORE_MESSAGE_UNLOAD_MODULE,
+    PA_CORE_MESSAGE_MAX
+};
+
+pa_core* pa_core_new(pa_mainloop_api *m, int shared);
+
+/* Check whether noone is connected to this core */
+void pa_core_check_quit(pa_core *c);
+
+#endif
diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
new file mode 100644 (file)
index 0000000..c15c469
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef foocredshfoo
+#define foocredshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+typedef struct pa_creds pa_creds;
+
+#if defined(SCM_CREDENTIALS)
+
+#define HAVE_CREDS 1
+
+struct pa_creds {
+    gid_t gid;
+    uid_t uid;
+};
+
+#else
+#undef HAVE_CREDS
+#endif
+
+#endif
diff --git a/src/pulsecore/dllmain.c b/src/pulsecore/dllmain.c
new file mode 100644 (file)
index 0000000..269de60
--- /dev/null
@@ -0,0 +1,55 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef OS_IS_WIN32
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <windows.h>
+
+extern pa_set_root(HANDLE handle);
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
+    WSADATA data;
+
+    switch (fdwReason) {
+
+    case DLL_PROCESS_ATTACH:
+        if (!pa_set_root(hinstDLL))
+            return FALSE;
+        WSAStartup(MAKEWORD(2, 0), &data);
+        break;
+
+    case DLL_PROCESS_DETACH:
+        WSACleanup();
+        break;
+
+    }
+    return TRUE;
+}
+
+#endif /* OS_IS_WIN32 */
similarity index 63%
rename from polyp/dynarray.c
rename to src/pulsecore/dynarray.c
index 0e406ea1b3ea4eb13b1bb22ed03e6396479b74b4..a1fcd8a37a3e2a19fe1ec183d42b8d1a9e485c79 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #endif
 
 #include <string.h>
-#include <assert.h>
 #include <stdlib.h>
 
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
 #include "dynarray.h"
-#include "xmalloc.h"
 
 /* If the array becomes to small, increase its size by 100 entries */
 #define INCREASE_BY 100
@@ -38,18 +39,18 @@ struct pa_dynarray {
     unsigned n_allocated, n_entries;
 };
 
-struct pa_dynarray* pa_dynarray_new(void) {
-    struct pa_dynarray *a;
-    a = pa_xmalloc(sizeof(struct pa_dynarray));
+pa_dynarray* pa_dynarray_new(void) {
+    pa_dynarray *a;
+    a = pa_xnew(pa_dynarray, 1);
     a->data = NULL;
     a->n_entries = 0;
     a->n_allocated = 0;
     return a;
 }
 
-void pa_dynarray_free(struct pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) {
+void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) {
     unsigned i;
-    assert(a);
+    pa_assert(a);
 
     if (func)
         for (i = 0; i < a->n_entries; i++)
@@ -60,8 +61,8 @@ void pa_dynarray_free(struct pa_dynarray* a, void (*func)(void *p, void *userdat
     pa_xfree(a);
 }
 
-void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p) {
-    assert(a);
+void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) {
+    pa_assert(a);
 
     if (i >= a->n_allocated) {
         unsigned n;
@@ -81,22 +82,28 @@ void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p) {
         a->n_entries = i+1;
 }
 
-unsigned pa_dynarray_append(struct pa_dynarray*a, void *p) {
-    unsigned i = a->n_entries;
+unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
+    unsigned i;
+
+    pa_assert(a);
+
+    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)
+void *pa_dynarray_get(pa_dynarray*a, unsigned i) {
+    pa_assert(a);
+
+    if (i >= a->n_entries)
         return NULL;
 
-    assert(a->data);
+    pa_assert(a->data);
     return a->data[i];
 }
 
-unsigned pa_dynarray_ncontents(struct pa_dynarray*a) {
-    assert(a);
+unsigned pa_dynarray_size(pa_dynarray*a) {
+    pa_assert(a);
+
     return a->n_entries;
 }
similarity index 63%
rename from polyp/dynarray.h
rename to src/pulsecore/dynarray.h
index 6733e958769afd2e90361ffeb345df52b5334707..82b42082e82a3c6e57fdab20eabdcf8f799cd6ea 100644 (file)
@@ -1,49 +1,49 @@
 #ifndef foodynarrayhfoo
 #define foodynarrayhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-struct pa_dynarray;
+typedef struct pa_dynarray pa_dynarray;
 
 /* Implementation of a simple dynamically sized array. The array
  * expands if required, but doesn't shrink if possible. Memory
  * management of the array's entries is the user's job. */
 
-struct pa_dynarray* pa_dynarray_new(void);
+pa_dynarray* pa_dynarray_new(void);
 
 /* Free the array calling the specified function for every entry in
  * the array. The function may be NULL. */
-void pa_dynarray_free(struct pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata);
+void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata);
 
 /* Store p at position i in the array */
-void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p);
+void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p);
 
 /* Store p a the first free position in the array. Returns the index
  * of that entry. If entries are removed from the array their position
  * are not filled any more by this function. */
-unsigned pa_dynarray_append(struct pa_dynarray*a, void *p);
+unsigned pa_dynarray_append(pa_dynarray*a, void *p);
 
-void *pa_dynarray_get(struct pa_dynarray*a, unsigned i);
+void *pa_dynarray_get(pa_dynarray*a, unsigned i);
 
-unsigned pa_dynarray_ncontents(struct pa_dynarray*a);
+unsigned pa_dynarray_size(pa_dynarray*a);
 
 #endif
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
new file mode 100644 (file)
index 0000000..2633691
--- /dev/null
@@ -0,0 +1,118 @@
+#ifndef fooendianmacroshfoo
+#define fooendianmacroshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+
+#ifdef HAVE_BYTESWAP_H
+#define PA_INT16_SWAP(x) ((int16_t) bswap_16((uint16_t) x))
+#define PA_UINT16_SWAP(x) ((uint16_t) bswap_16((uint16_t) x))
+#define PA_INT32_SWAP(x) ((int32_t) bswap_32((uint32_t) x))
+#define PA_UINT32_SWAP(x) ((uint32_t) bswap_32((uint32_t) x))
+#else
+#define PA_INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
+#define PA_UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
+#define PA_INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
+#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
+#endif
+
+static inline float PA_FLOAT32_SWAP(float x) {
+    uint32_t i = *(uint32_t*) &x;
+    i = PA_UINT32_SWAP(i);
+    return *(float*) &i;
+}
+
+#define PA_MAYBE_INT16_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x)
+#define PA_MAYBE_UINT16_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : x)
+
+#define PA_MAYBE_INT32_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : x)
+#define PA_MAYBE_UINT32_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : x)
+
+#define PA_MAYBE_FLOAT32_SWAP(c,x) ((c) ? PA_FLOAT32_SWAP(x) : x)
+
+#ifdef WORDS_BIGENDIAN
+ #define PA_INT16_FROM_LE(x) PA_INT16_SWAP(x)
+ #define PA_INT16_FROM_BE(x) ((int16_t)(x))
+
+ #define PA_INT16_TO_LE(x) PA_INT16_SWAP(x)
+ #define PA_INT16_TO_BE(x) ((int16_t)(x))
+
+ #define PA_UINT16_FROM_LE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_FROM_BE(x) ((uint16_t)(x))
+
+ #define PA_UINT16_TO_LE(x) PA_UINT16_SWAP(x)
+ #define PA_UINT16_TO_BE(x) ((uint16_t)(x))
+
+ #define PA_INT32_FROM_LE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_FROM_BE(x) ((int32_t)(x))
+
+ #define PA_INT32_TO_LE(x) PA_INT32_SWAP(x)
+ #define PA_INT32_TO_BE(x) ((int32_t)(x))
+
+ #define PA_UINT32_FROM_LE(x) PA_UINT32_SWAP(x)
+ #define PA_UINT32_FROM_BE(x) ((uint32_t)(x))
+
+ #define PA_UINT32_TO_LE(x) PA_UINT32_SWAP(x)
+ #define PA_UINT32_TO_BE(x) ((uint32_t)(x))
+
+ #define PA_FLOAT32_TO_LE(x) PA_FLOAT32_SWAP(x)
+ #define PA_FLOAT32_TO_BE(x) ((float) (x))
+#else
+ #define PA_INT16_FROM_LE(x) ((int16_t)(x))
+ #define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x)
+
+ #define PA_INT16_TO_LE(x) ((int16_t)(x))
+ #define PA_INT16_TO_BE(x) PA_INT16_SWAP(x)
+
+ #define PA_UINT16_FROM_LE(x) ((uint16_t)(x))
+ #define PA_UINT16_FROM_BE(x) PA_UINT16_SWAP(x)
+
+ #define PA_UINT16_TO_LE(x) ((uint16_t)(x))
+ #define PA_UINT16_TO_BE(x) PA_UINT16_SWAP(x)
+
+ #define PA_INT32_FROM_LE(x) ((int32_t)(x))
+ #define PA_INT32_FROM_BE(x) PA_INT32_SWAP(x)
+
+ #define PA_INT32_TO_LE(x) ((int32_t)(x))
+ #define PA_INT32_TO_BE(x) PA_INT32_SWAP(x)
+
+ #define PA_UINT32_FROM_LE(x) ((uint32_t)(x))
+ #define PA_UINT32_FROM_BE(x) PA_UINT32_SWAP(x)
+
+ #define PA_UINT32_TO_LE(x) ((uint32_t)(x))
+ #define PA_UINT32_TO_BE(x) PA_UINT32_SWAP(x)
+
+ #define PA_FLOAT32_TO_LE(x) ((float) (x))
+ #define PA_FLOAT32_TO_BE(x) PA_FLOAT32_SWAP(x)
+#endif
+
+#endif
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
new file mode 100644 (file)
index 0000000..e269161
--- /dev/null
@@ -0,0 +1,781 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulse/sample.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/g711.h>
+
+#include "envelope.h"
+
+/*
+    Envelope subsystem for applying linear interpolated volume
+    envelopes on audio data. If multiple enevelopes shall be applied
+    at the same time, the "minimum" envelope is determined and
+    applied.
+
+    Envelopes are defined in a statically allocated constant structure
+    pa_envelope_def. It may be activated using pa_envelope_add(). And
+    already active envelope may be replaced with pa_envelope_replace()
+    and removed with pa_envelope_remove().The combined "minimum"
+    envelope can be applied to audio data with pa_envelope_apply().
+
+    _apply() on one hand and _add()/_replace()/_remove() on the other
+    can be executed in seperate threads, in which case no locking is
+    used.
+*/
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+struct pa_envelope_item {
+    PA_LLIST_FIELDS(pa_envelope_item);
+    const pa_envelope_def *def;
+    pa_usec_t start_x;
+    union {
+        int32_t i;
+        float f;
+    } start_y;
+    unsigned j;
+};
+
+enum envelope_state {
+    STATE_VALID0,
+    STATE_VALID1,
+    STATE_READ0,
+    STATE_READ1,
+    STATE_WAIT0,
+    STATE_WAIT1,
+    STATE_WRITE0,
+    STATE_WRITE1
+};
+
+struct pa_envelope {
+    pa_sample_spec sample_spec;
+
+    PA_LLIST_HEAD(pa_envelope_item, items);
+
+    pa_atomic_t state;
+
+    size_t x;
+
+    struct {
+        unsigned n_points, n_allocated, n_current;
+
+        size_t *x;
+        union {
+            int32_t *i;
+            float *f;
+        } y;
+
+        size_t cached_dx;
+        int32_t cached_dy_i;
+        float cached_dy_dx;
+        pa_bool_t cached_valid;
+    } points[2];
+
+    pa_bool_t is_float;
+
+    pa_semaphore *semaphore;
+};
+
+pa_envelope *pa_envelope_new(const pa_sample_spec *ss) {
+    pa_envelope *e;
+    pa_assert(ss);
+
+    e = pa_xnew(pa_envelope, 1);
+
+    e->sample_spec = *ss;
+    PA_LLIST_HEAD_INIT(pa_envelope_item, e->items);
+
+    e->x = 0;
+
+    e->points[0].n_points = e->points[1].n_points = 0;
+    e->points[0].n_allocated = e->points[1].n_allocated = 0;
+    e->points[0].n_current = e->points[1].n_current = 0;
+    e->points[0].x = e->points[1].x = NULL;
+    e->points[0].y.i = e->points[1].y.i = NULL;
+    e->points[0].cached_valid = e->points[1].cached_valid = FALSE;
+
+    pa_atomic_store(&e->state, STATE_VALID0);
+
+    e->is_float =
+        ss->format == PA_SAMPLE_FLOAT32LE ||
+        ss->format == PA_SAMPLE_FLOAT32BE;
+
+    e->semaphore = pa_semaphore_new(0);
+
+    return e;
+}
+
+void pa_envelope_free(pa_envelope *e) {
+    pa_assert(e);
+
+    while (e->items)
+        pa_envelope_remove(e, e->items);
+
+    pa_xfree(e->points[0].x);
+    pa_xfree(e->points[1].x);
+    pa_xfree(e->points[0].y.i);
+    pa_xfree(e->points[1].y.i);
+
+    pa_semaphore_free(e->semaphore);
+
+    pa_xfree(e);
+}
+
+static int32_t linear_interpolate_int(pa_usec_t x1, int32_t _y1, pa_usec_t x2, int32_t y2, pa_usec_t x3) {
+    return (int32_t) (_y1 + (x3 - x1) * (float) (y2 - _y1) / (float) (x2 - x1));
+}
+
+static float linear_interpolate_float(pa_usec_t x1, float _y1, pa_usec_t x2, float y2, pa_usec_t x3) {
+    return _y1 + (x3 - x1) * (y2 - _y1) / (x2 - x1);
+}
+
+static int32_t item_get_int(pa_envelope_item *i, pa_usec_t x) {
+    pa_assert(i);
+
+    if (x <= i->start_x)
+        return i->start_y.i;
+
+    x -= i->start_x;
+
+    if (x <= i->def->points_x[0])
+        return linear_interpolate_int(0, i->start_y.i,
+                                      i->def->points_x[0], i->def->points_y.i[0], x);
+
+    if (x >= i->def->points_x[i->def->n_points-1])
+        return i->def->points_y.i[i->def->n_points-1];
+
+    pa_assert(i->j > 0);
+    pa_assert(i->def->points_x[i->j-1] <= x);
+    pa_assert(x < i->def->points_x[i->j]);
+
+    return linear_interpolate_int(i->def->points_x[i->j-1], i->def->points_y.i[i->j-1],
+                                  i->def->points_x[i->j], i->def->points_y.i[i->j], x);
+}
+
+static float item_get_float(pa_envelope_item *i, pa_usec_t x) {
+    pa_assert(i);
+
+    if (x <= i->start_x)
+        return i->start_y.f;
+
+    x -= i->start_x;
+
+    if (x <= i->def->points_x[0])
+        return linear_interpolate_float(0, i->start_y.f,
+                                        i->def->points_x[0], i->def->points_y.f[0], x);
+
+    if (x >= i->def->points_x[i->def->n_points-1])
+        return i->def->points_y.f[i->def->n_points-1];
+
+    pa_assert(i->j > 0);
+    pa_assert(i->def->points_x[i->j-1] <= x);
+    pa_assert(x < i->def->points_x[i->j]);
+
+    return linear_interpolate_float(i->def->points_x[i->j-1], i->def->points_y.f[i->j-1],
+                                    i->def->points_x[i->j], i->def->points_y.f[i->j], x);
+}
+
+static void envelope_begin_write(pa_envelope *e, int *v) {
+    enum envelope_state new_state, old_state;
+    pa_bool_t wait_sem;
+
+    pa_assert(e);
+    pa_assert(v);
+
+    for (;;) {
+        do {
+            wait_sem = FALSE;
+            old_state = pa_atomic_load(&e->state);
+
+            switch (old_state) {
+                case STATE_VALID0:
+                    *v = 1;
+                    new_state = STATE_WRITE0;
+                    break;
+                case STATE_VALID1:
+                    *v = 0;
+                    new_state = STATE_WRITE1;
+                    break;
+                case STATE_READ0:
+                    new_state = STATE_WAIT0;
+                    wait_sem = TRUE;
+                    break;
+                case STATE_READ1:
+                    new_state = STATE_WAIT1;
+                    wait_sem = TRUE;
+                    break;
+                default:
+                    pa_assert_not_reached();
+            }
+        } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+        if (!wait_sem)
+            break;
+
+        pa_semaphore_wait(e->semaphore);
+    }
+}
+
+static pa_bool_t envelope_commit_write(pa_envelope *e, int v) {
+    enum envelope_state new_state, old_state;
+
+    pa_assert(e);
+
+    do {
+        old_state = pa_atomic_load(&e->state);
+
+        switch (old_state) {
+            case STATE_WRITE0:
+                pa_assert(v == 1);
+                new_state = STATE_VALID1;
+                break;
+            case STATE_WRITE1:
+                pa_assert(v == 0);
+                new_state = STATE_VALID0;
+                break;
+            case STATE_VALID0:
+            case STATE_VALID1:
+            case STATE_READ0:
+            case STATE_READ1:
+                return FALSE;
+            default:
+                pa_assert_not_reached();
+        }
+    } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+    return TRUE;
+}
+
+static void envelope_begin_read(pa_envelope *e, int *v) {
+    enum envelope_state new_state, old_state;
+    pa_assert(e);
+    pa_assert(v);
+
+    do {
+        old_state = pa_atomic_load(&e->state);
+
+        switch (old_state) {
+            case STATE_VALID0:
+            case STATE_WRITE0:
+                *v = 0;
+                new_state = STATE_READ0;
+                break;
+            case STATE_VALID1:
+            case STATE_WRITE1:
+                *v = 1;
+                new_state = STATE_READ1;
+                break;
+            default:
+                pa_assert_not_reached();
+        }
+    } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+}
+
+static void envelope_commit_read(pa_envelope *e, int v) {
+    enum envelope_state new_state, old_state;
+    pa_bool_t post_sem;
+
+    pa_assert(e);
+
+    do {
+        post_sem = FALSE;
+        old_state = pa_atomic_load(&e->state);
+
+        switch (old_state) {
+            case STATE_READ0:
+                pa_assert(v == 0);
+                new_state = STATE_VALID0;
+                break;
+            case STATE_READ1:
+                pa_assert(v == 1);
+                new_state = STATE_VALID1;
+                break;
+            case STATE_WAIT0:
+                pa_assert(v == 0);
+                new_state = STATE_VALID0;
+                post_sem = TRUE;
+                break;
+            case STATE_WAIT1:
+                pa_assert(v == 1);
+                new_state = STATE_VALID1;
+                post_sem = TRUE;
+                break;
+            default:
+                pa_assert_not_reached();
+        }
+    } while (!pa_atomic_cmpxchg(&e->state, old_state, new_state));
+
+    if (post_sem)
+        pa_semaphore_post(e->semaphore);
+}
+
+static void envelope_merge(pa_envelope *e, int v) {
+
+    e->points[v].n_points = 0;
+
+    if (e->items) {
+        pa_envelope_item *i;
+        pa_usec_t x = (pa_usec_t) -1;
+
+        for (i = e->items; i; i = i->next)
+            i->j = 0;
+
+        for (;;) {
+            pa_bool_t min_is_set;
+            pa_envelope_item *s = NULL;
+
+            /* Let's find the next spot on the X axis to analyze */
+            for (i = e->items; i; i = i->next) {
+
+                for (;;) {
+
+                    if (i->j >= i->def->n_points)
+                        break;
+
+                    if ((x != (pa_usec_t) -1) && i->start_x + i->def->points_x[i->j] <= x) {
+                        i->j++;
+                        continue;
+                    }
+
+                    if (!s || (i->start_x + i->def->points_x[i->j] < s->start_x + s->def->points_x[s->j]))
+                        s = i;
+
+                    break;
+                }
+            }
+
+            if (!s)
+                break;
+
+            if (e->points[v].n_points >= e->points[v].n_allocated) {
+                e->points[v].n_allocated = PA_MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX);
+
+                e->points[v].x = pa_xrealloc(e->points[v].x, sizeof(size_t) * e->points[v].n_allocated);
+                e->points[v].y.i = pa_xrealloc(e->points[v].y.i, sizeof(int32_t) * e->points[v].n_allocated);
+            }
+
+            x = s->start_x + s->def->points_x[s->j];
+            e->points[v].x[e->points[v].n_points] = pa_usec_to_bytes(x, &e->sample_spec);
+
+            min_is_set = FALSE;
+
+            /* Now let's find the lowest value */
+            if (e->is_float) {
+                float min_f;
+
+                for (i = e->items; i; i = i->next) {
+                    float f = item_get_float(i, x);
+                    if (!min_is_set || f < min_f) {
+                        min_f = f;
+                        min_is_set = TRUE;
+                    }
+                }
+
+                e->points[v].y.f[e->points[v].n_points] = min_f;
+            } else {
+                int32_t min_k;
+
+                for (i = e->items; i; i = i->next) {
+                    int32_t k = item_get_int(i, x);
+                    if (!min_is_set || k < min_k) {
+                        min_k = k;
+                        min_is_set = TRUE;
+                    }
+                }
+
+                e->points[v].y.i[e->points[v].n_points] = min_k;
+            }
+
+            pa_assert_se(min_is_set);
+            e->points[v].n_points++;
+        }
+    }
+
+    e->points[v].n_current = 0;
+    e->points[v].cached_valid = FALSE;
+}
+
+pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def) {
+    pa_envelope_item *i;
+    int v;
+
+    pa_assert(e);
+    pa_assert(def);
+    pa_assert(def->n_points > 0);
+
+    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+        i = pa_xnew(pa_envelope_item, 1);
+
+    i->def = def;
+
+    if (e->is_float)
+        i->start_y.f = def->points_y.f[0];
+    else
+        i->start_y.i = def->points_y.i[0];
+
+    PA_LLIST_PREPEND(pa_envelope_item, e->items, i);
+
+    envelope_begin_write(e, &v);
+
+    do {
+
+        i->start_x = pa_bytes_to_usec(e->x, &e->sample_spec);
+        envelope_merge(e, v);
+
+    } while (!envelope_commit_write(e, v));
+
+    return i;
+}
+
+pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def) {
+    pa_usec_t x;
+    int v;
+
+    pa_assert(e);
+    pa_assert(i);
+    pa_assert(def->n_points > 0);
+
+    envelope_begin_write(e, &v);
+
+    for (;;) {
+        float saved_f;
+        int32_t saved_i;
+        uint64_t saved_start_x;
+        const pa_envelope_def *saved_def;
+
+        x = pa_bytes_to_usec(e->x, &e->sample_spec);
+
+        if (e->is_float) {
+            saved_f = i->start_y.f;
+            i->start_y.f = item_get_float(i, x);
+        } else {
+            saved_i = i->start_y.i;
+            i->start_y.i = item_get_int(i, x);
+        }
+
+        saved_start_x = i->start_x;
+        saved_def = i->def;
+
+        i->start_x = x;
+        i->def = def;
+
+        envelope_merge(e, v);
+
+        if (envelope_commit_write(e, v))
+            break;
+
+        i->start_x = saved_start_x;
+        i->def = saved_def;
+
+        if (e->is_float)
+            i->start_y.f = saved_f;
+        else
+            i->start_y.i = saved_i;
+    }
+
+    return i;
+}
+
+void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i) {
+    int v;
+
+    pa_assert(e);
+    pa_assert(i);
+
+    PA_LLIST_REMOVE(pa_envelope_item, e->items, i);
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+        pa_xfree(i);
+
+    envelope_begin_write(e, &v);
+    do {
+        envelope_merge(e, v);
+    } while (!envelope_commit_write(e, v));
+}
+
+static int32_t linear_get_int(pa_envelope *e, int v) {
+    pa_assert(e);
+
+    /* The repeated division could be replaced by Bresenham, as an
+     * optimization */
+
+    if (e->x < e->points[v].x[0])
+        return e->points[v].y.i[0];
+
+    for (;;) {
+        if (e->points[v].n_current+1 >= e->points[v].n_points)
+            return e->points[v].y.i[e->points[v].n_points-1];
+
+        if (e->x < e->points[v].x[e->points[v].n_current+1])
+            break;
+
+        e->points[v].n_current++;
+        e->points[v].cached_valid = FALSE;
+    }
+
+    if (!e->points[v].cached_valid) {
+        e->points[v].cached_dx = e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current];
+        e->points[v].cached_dy_i = e->points[v].y.i[e->points[v].n_current+1] - e->points[v].y.i[e->points[v].n_current];
+        e->points[v].cached_valid = TRUE;
+    }
+
+    return e->points[v].y.i[e->points[v].n_current] + (e->points[v].cached_dy_i * (int32_t) (e->x - e->points[v].x[e->points[v].n_current])) / (int32_t) e->points[v].cached_dx;
+}
+
+static float linear_get_float(pa_envelope *e, int v) {
+    pa_assert(e);
+
+    if (e->x < e->points[v].x[0])
+        return e->points[v].y.f[0];
+
+    for (;;) {
+        if (e->points[v].n_current+1 >= e->points[v].n_points)
+            return e->points[v].y.f[e->points[v].n_points-1];
+
+        if (e->x < e->points[v].x[e->points[v].n_current+1])
+            break;
+
+        e->points[v].n_current++;
+        e->points[v].cached_valid = FALSE;
+    }
+
+    if (!e->points[v].cached_valid) {
+        e->points[v].cached_dy_dx =
+            (e->points[v].y.f[e->points[v].n_current+1] - e->points[v].y.f[e->points[v].n_current]) /
+            (e->points[v].x[e->points[v].n_current+1] - e->points[v].x[e->points[v].n_current]);
+        e->points[v].cached_valid = TRUE;
+    }
+
+    return e->points[v].y.f[e->points[v].n_current] + (e->x - e->points[v].x[e->points[v].n_current]) * e->points[v].cached_dy_dx;
+}
+
+void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
+    int v;
+
+    pa_assert(e);
+    pa_assert(chunk);
+
+    envelope_begin_read(e, &v);
+
+    if (e->points[v].n_points > 0) {
+        void *p;
+        size_t fs, n;
+
+        pa_memchunk_make_writable(chunk, 0);
+        p = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index;
+        fs = pa_frame_size(&e->sample_spec);
+        n = chunk->length;
+
+        switch (e->sample_spec.format) {
+
+
+
+            case PA_SAMPLE_U8: {
+                uint8_t *t;
+
+                for (t = p; n > 0; n -= fs) {
+                    int16_t factor = linear_get_int(e, v);
+                    unsigned c;
+                    e->x += fs;
+
+                    for (c = 0; c < e->sample_spec.channels; c++, t++)
+                        *t = (uint8_t) (((factor * ((int16_t) *t - 0x80)) / 0x10000) + 0x80);
+                }
+
+                break;
+            }
+
+            case PA_SAMPLE_ULAW: {
+                uint8_t *t;
+
+                for (t = p; n > 0; n -= fs) {
+                    int16_t factor = linear_get_int(e, v);
+                    unsigned c;
+                    e->x += fs;
+
+                    for (c = 0; c < e->sample_spec.channels; c++, t++) {
+                        int16_t k = st_ulaw2linear16(*t);
+                        *t = (uint8_t) st_14linear2ulaw(((factor * k) / 0x10000) >> 2);
+                    }
+                }
+
+                break;
+            }
+
+            case PA_SAMPLE_ALAW: {
+                uint8_t *t;
+
+                for (t = p; n > 0; n -= fs) {
+                    int16_t factor = linear_get_int(e, v);
+                    unsigned c;
+                    e->x += fs;
+
+                    for (c = 0; c < e->sample_spec.channels; c++, t++) {
+                        int16_t k = st_alaw2linear16(*t);
+                        *t = (uint8_t) st_13linear2alaw(((factor * k) / 0x10000) >> 3);
+                    }
+                }
+
+                break;
+            }
+
+            case PA_SAMPLE_S16NE: {
+                int16_t *t;
+
+                for (t = p; n > 0; n -= fs) {
+                    int32_t factor = linear_get_int(e, v);
+                    unsigned c;
+                    e->x += fs;
+
+                    for (c = 0; c < e->sample_spec.channels; c++, t++)
+                        *t = (factor * *t) / 0x10000;
+                }
+
+                break;
+            }
+
+            case PA_SAMPLE_S16RE: {
+                int16_t *t;
+
+                for (t = p; n > 0; n -= fs) {
+                    int32_t factor = linear_get_int(e, v);
+                    unsigned c;
+                    e->x += fs;
+
+                    for (c = 0; c < e->sample_spec.channels; c++, t++) {
+                        int16_t r = (factor * PA_INT16_SWAP(*t)) / 0x10000;
+                        *t = PA_INT16_SWAP(r);
+                    }
+                }
+
+                break;
+            }
+
+            case PA_SAMPLE_S32NE: {
+                int32_t *t;
+
+                for (t = p; n > 0; n -= fs) {
+                    int32_t factor = linear_get_int(e, v);
+                    unsigned c;
+                    e->x += fs;
+
+                    for (c = 0; c < e->sample_spec.channels; c++, t++)
+                        *t = (int32_t) (((int64_t) factor * (int64_t) *t) / 0x10000);
+                }
+
+                break;
+            }
+
+            case PA_SAMPLE_S32RE: {
+                int32_t *t;
+
+                for (t = p; n > 0; n -= fs) {
+                    int32_t factor = linear_get_int(e, v);
+                    unsigned c;
+                    e->x += fs;
+
+                    for (c = 0; c < e->sample_spec.channels; c++, t++) {
+                        int32_t r = (int32_t) (((int64_t) factor * (int64_t) PA_INT32_SWAP(*t)) / 0x10000);
+                        *t = PA_INT32_SWAP(r);
+                    }
+                }
+
+                break;
+            }
+
+            case PA_SAMPLE_FLOAT32NE: {
+                float *t;
+
+                for (t = p; n > 0; n -= fs) {
+                    float factor = linear_get_float(e, v);
+                    unsigned c;
+                    e->x += fs;
+
+                    for (c = 0; c < e->sample_spec.channels; c++, t++)
+                        *t = *t * factor;
+                }
+
+                break;
+            }
+
+            case PA_SAMPLE_FLOAT32RE: {
+                float *t;
+
+                for (t = p; n > 0; n -= fs) {
+                    float factor = linear_get_float(e, v);
+                    unsigned c;
+                    e->x += fs;
+
+                    for (c = 0; c < e->sample_spec.channels; c++, t++) {
+                        float r = PA_FLOAT32_SWAP(*t) * factor;
+                        *t = PA_FLOAT32_SWAP(r);
+                    }
+                }
+
+                break;
+            }
+
+            case PA_SAMPLE_MAX:
+            case PA_SAMPLE_INVALID:
+                pa_assert_not_reached();
+        }
+
+        pa_memblock_release(chunk->memblock);
+
+        e->x += chunk->length;
+    } else {
+        /* When we have no envelope to apply we reset our origin */
+        e->x = 0;
+    }
+
+    envelope_commit_read(e, v);
+}
+
+void pa_envelope_rewind(pa_envelope *e, size_t n_bytes) {
+    int v;
+
+    pa_assert(e);
+
+    envelope_begin_read(e, &v);
+
+    if (n_bytes < e->x)
+        e->x -= n_bytes;
+    else
+        e->x = 0;
+
+    e->points[v].n_current = 0;
+    e->points[v].cached_valid = FALSE;
+
+    envelope_commit_read(e, v);
+}
diff --git a/src/pulsecore/envelope.h b/src/pulsecore/envelope.h
new file mode 100644 (file)
index 0000000..5296415
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef foopulseenvelopehfoo
+#define foopulseenvelopehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulsecore/memchunk.h>
+
+#include <pulse/sample.h>
+
+#define PA_ENVELOPE_POINTS_MAX 4U
+
+typedef struct pa_envelope pa_envelope;
+typedef struct pa_envelope_item pa_envelope_item;
+
+typedef struct pa_envelope_def {
+    unsigned n_points;
+
+    pa_usec_t points_x[PA_ENVELOPE_POINTS_MAX];
+    struct {
+        int32_t i[PA_ENVELOPE_POINTS_MAX];
+        float f[PA_ENVELOPE_POINTS_MAX];
+    } points_y;
+} pa_envelope_def;
+
+pa_envelope *pa_envelope_new(const pa_sample_spec *ss);
+void pa_envelope_free(pa_envelope *e);
+pa_envelope_item *pa_envelope_add(pa_envelope *e, const pa_envelope_def *def);
+pa_envelope_item *pa_envelope_replace(pa_envelope *e, pa_envelope_item *i, const pa_envelope_def *def);
+void pa_envelope_remove(pa_envelope *e, pa_envelope_item *i);
+void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk);
+void pa_envelope_rewind(pa_envelope *e, size_t n_bytes);
+
+#endif
similarity index 93%
rename from polyp/esound.h
rename to src/pulsecore/esound.h
index 5dc2583b18c52e7322e5a28388763cf84f862448..79322ae46e1ec7bc51fcb835a81a532d252c583d 100644 (file)
@@ -1,23 +1,23 @@
 #ifndef fooesoundhfoo
 #define fooesoundhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
@@ -200,14 +200,10 @@ enum esd_client_state {
 };
 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))
+#define ESD_SWAP_ENDIAN_KEY (PA_UINT32_SWAP(ESD_ENDIAN_KEY))
 
 
 #endif
diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
new file mode 100644 (file)
index 0000000..1531e3d
--- /dev/null
@@ -0,0 +1,324 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/xmalloc.h>
+
+#ifndef HAVE_PIPE
+#include <pulsecore/pipe.h>
+#endif
+
+#ifdef __linux__
+
+#if !defined(__NR_eventfd) && defined(__i386__)
+#define __NR_eventfd 323
+#endif
+
+#if !defined(__NR_eventfd) && defined(__x86_64__)
+#define __NR_eventfd 284
+#endif
+
+#if !defined(__NR_eventfd) && defined(__arm__)
+#define __NR_eventfd (__NR_SYSCALL_BASE+351)
+#endif
+
+#if !defined(SYS_eventfd) && defined(__NR_eventfd)
+#define SYS_eventfd __NR_eventfd
+#endif
+
+#ifdef SYS_eventfd
+#define HAVE_EVENTFD
+
+static inline long eventfd(unsigned count) {
+    return syscall(SYS_eventfd, count);
+}
+
+#endif
+#endif
+
+#include "fdsem.h"
+
+struct pa_fdsem {
+    int fds[2];
+#ifdef HAVE_EVENTFD
+    int efd;
+#endif
+
+    pa_fdsem_data *data;
+};
+
+pa_fdsem *pa_fdsem_new(void) {
+    pa_fdsem *f;
+
+    f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
+
+#ifdef HAVE_EVENTFD
+    if ((f->efd = eventfd(0)) >= 0) {
+        pa_make_fd_cloexec(f->efd);
+        f->fds[0] = f->fds[1] = -1;
+    } else
+#endif
+    {
+        if (pipe(f->fds) < 0) {
+            pa_xfree(f);
+            return NULL;
+        }
+
+        pa_make_fd_cloexec(f->fds[0]);
+        pa_make_fd_cloexec(f->fds[1]);
+    }
+
+    f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem)));
+
+    pa_atomic_store(&f->data->waiting, 0);
+    pa_atomic_store(&f->data->signalled, 0);
+    pa_atomic_store(&f->data->in_pipe, 0);
+
+    return f;
+}
+
+pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) {
+    pa_fdsem *f = NULL;
+
+    pa_assert(data);
+    pa_assert(event_fd >= 0);
+
+#ifdef HAVE_EVENTFD
+    f = pa_xnew(pa_fdsem, 1);
+
+    f->efd = event_fd;
+    pa_make_fd_cloexec(f->efd);
+    f->fds[0] = f->fds[1] = -1;
+    f->data = data;
+#endif
+
+    return f;
+}
+
+pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) {
+    pa_fdsem *f = NULL;
+
+    pa_assert(data);
+    pa_assert(event_fd);
+
+#ifdef HAVE_EVENTFD
+
+    f = pa_xnew(pa_fdsem, 1);
+
+    if ((f->efd = eventfd(0)) < 0) {
+        pa_xfree(f);
+        return NULL;
+    }
+
+    pa_make_fd_cloexec(f->efd);
+    f->fds[0] = f->fds[1] = -1;
+    f->data = data;
+
+    pa_atomic_store(&f->data->waiting, 0);
+    pa_atomic_store(&f->data->signalled, 0);
+    pa_atomic_store(&f->data->in_pipe, 0);
+
+#endif
+
+    return f;
+}
+
+void pa_fdsem_free(pa_fdsem *f) {
+    pa_assert(f);
+
+#ifdef HAVE_EVENTFD
+    if (f->efd >= 0)
+        pa_close(f->efd);
+#endif
+    pa_close_pipe(f->fds);
+
+    pa_xfree(f);
+}
+
+static void flush(pa_fdsem *f) {
+    ssize_t r;
+    pa_assert(f);
+
+    if (pa_atomic_load(&f->data->in_pipe) <= 0)
+        return;
+
+    do {
+        char x[10];
+
+#ifdef HAVE_EVENTFD
+        if (f->efd >= 0) {
+            uint64_t u;
+
+            if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) {
+                pa_assert(r < 0 && errno == EINTR);
+                continue;
+            }
+            r = (ssize_t) u;
+        } else
+#endif
+
+        if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
+            pa_assert(r < 0 && errno == EINTR);
+            continue;
+        }
+
+    } while (pa_atomic_sub(&f->data->in_pipe, r) > r);
+}
+
+void pa_fdsem_post(pa_fdsem *f) {
+    pa_assert(f);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 0, 1)) {
+
+        if (pa_atomic_load(&f->data->waiting)) {
+            ssize_t r;
+            char x = 'x';
+
+            pa_atomic_inc(&f->data->in_pipe);
+
+            for (;;) {
+
+#ifdef HAVE_EVENTFD
+                if (f->efd >= 0) {
+                    uint64_t u = 1;
+
+                    if ((r = write(f->efd, &u, sizeof(u))) != sizeof(u)) {
+                        pa_assert(r < 0 && errno == EINTR);
+                        continue;
+                    }
+                } else
+#endif
+
+                if ((r = write(f->fds[1], &x, 1)) != 1) {
+                    pa_assert(r < 0 && errno == EINTR);
+                    continue;
+                }
+
+                break;
+            }
+        }
+    }
+}
+
+void pa_fdsem_wait(pa_fdsem *f) {
+    pa_assert(f);
+
+    flush(f);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+        return;
+
+    pa_atomic_inc(&f->data->waiting);
+
+    while (!pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
+        char x[10];
+        ssize_t r;
+
+#ifdef HAVE_EVENTFD
+        if (f->efd >= 0) {
+            uint64_t u;
+
+            if ((r = read(f->efd, &u, sizeof(u))) != sizeof(u)) {
+                pa_assert(r < 0 && errno == EINTR);
+                continue;
+            }
+
+            r = (ssize_t) u;
+        } else
+#endif
+
+        if ((r = read(f->fds[0], &x, sizeof(x))) <= 0) {
+            pa_assert(r < 0 && errno == EINTR);
+            continue;
+        }
+
+        pa_atomic_sub(&f->data->in_pipe, r);
+    }
+
+    pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
+}
+
+int pa_fdsem_try(pa_fdsem *f) {
+    pa_assert(f);
+
+    flush(f);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+        return 1;
+
+    return 0;
+}
+
+int pa_fdsem_get(pa_fdsem *f) {
+    pa_assert(f);
+
+#ifdef HAVE_EVENTFD
+    if (f->efd >= 0)
+        return f->efd;
+#endif
+
+    return f->fds[0];
+}
+
+int pa_fdsem_before_poll(pa_fdsem *f) {
+    pa_assert(f);
+
+    flush(f);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+        return -1;
+
+    pa_atomic_inc(&f->data->waiting);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
+        pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
+        return -1;
+    }
+    return 0;
+}
+
+int pa_fdsem_after_poll(pa_fdsem *f) {
+    pa_assert(f);
+
+    pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
+
+    flush(f);
+
+    if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
+        return 1;
+
+    return 0;
+}
diff --git a/src/pulsecore/fdsem.h b/src/pulsecore/fdsem.h
new file mode 100644 (file)
index 0000000..48a77c4
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef foopulsefdsemhfoo
+#define foopulsefdsemhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+#include <pulse/def.h>
+
+/* A simple, asynchronous semaphore which uses fds for sleeping. In
+ * the best case all functions are lock-free unless sleeping is
+ * required.  */
+
+typedef struct pa_fdsem pa_fdsem;
+
+typedef struct pa_fdsem_data {
+    pa_atomic_t waiting;
+    pa_atomic_t signalled;
+    pa_atomic_t in_pipe;
+} pa_fdsem_data;
+
+pa_fdsem *pa_fdsem_new(void);
+pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd);
+pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd);
+void pa_fdsem_free(pa_fdsem *f);
+
+void pa_fdsem_post(pa_fdsem *f);
+void pa_fdsem_wait(pa_fdsem *f);
+int pa_fdsem_try(pa_fdsem *f);
+
+int pa_fdsem_get(pa_fdsem *f);
+
+int pa_fdsem_before_poll(pa_fdsem *f);
+int pa_fdsem_after_poll(pa_fdsem *f);
+
+
+#endif
diff --git a/src/pulsecore/ffmpeg/Makefile b/src/pulsecore/ffmpeg/Makefile
new file mode 100644 (file)
index 0000000..316beb7
--- /dev/null
@@ -0,0 +1,13 @@
+# This is a dirty trick just to ease compilation with emacs
+#
+# This file is not intended to be distributed or anything
+#
+# So: don't touch it, even better ignore it!
+
+all:
+       $(MAKE) -C ../..
+
+clean:
+       $(MAKE) -C ../.. clean
+
+.PHONY: all clean
diff --git a/src/pulsecore/ffmpeg/avcodec.h b/src/pulsecore/ffmpeg/avcodec.h
new file mode 100644 (file)
index 0000000..696fc98
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * copyright (c) 2001 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_H
+#define AVCODEC_H
+
+/* Just a heavily bastardized version of the original file from
+ * ffmpeg, just enough to get resample2.c to compile without
+ * modification -- Lennart */
+
+#if !defined(PACKAGE) && defined(HAVE_CONFIG_H)
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define av_mallocz(l) calloc(1, (l))
+#define av_malloc(l) malloc(l)
+#define av_realloc(p,l) realloc((p),(l))
+#define av_free(p) free(p)
+
+static inline void av_freep(void *k) {
+    void **p = k;
+
+    if (p) {
+        free(*p);
+        *p = NULL;
+    }
+}
+
+static inline int av_clip(int a, int amin, int amax)
+{
+    if (a < amin)      return amin;
+    else if (a > amax) return amax;
+    else               return a;
+}
+
+#define av_log(a,b,c)
+
+#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))
+#define FFSIGN(a) ((a) > 0 ? 1 : -1)
+
+#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
+#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
+
+struct AVResampleContext;
+struct AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_length, int log2_phase_count, int linear, double cutoff);
+int av_resample(struct AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx);
+void av_resample_compensate(struct AVResampleContext *c, int sample_delta, int compensation_distance);
+void av_resample_close(struct AVResampleContext *c);
+void av_build_filter(int16_t *filter, double factor, int tap_count, int phase_count, int scale, int type);
+
+/*
+ * crude lrintf for non-C99 systems.
+ */
+#ifndef HAVE_LRINTF
+#define lrintf(x) ((long int)(x))
+#endif
+
+#endif /* AVCODEC_H */
diff --git a/src/pulsecore/ffmpeg/dsputil.h b/src/pulsecore/ffmpeg/dsputil.h
new file mode 100644 (file)
index 0000000..8da742d
--- /dev/null
@@ -0,0 +1 @@
+/* empty file, just here to allow us to compile an unmodified resampler2.c */
diff --git a/src/pulsecore/ffmpeg/resample2.c b/src/pulsecore/ffmpeg/resample2.c
new file mode 100644 (file)
index 0000000..da1443d
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * audio resampling
+ * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file resample2.c
+ * audio resampling
+ * @author Michael Niedermayer <michaelni@gmx.at>
+ */
+
+#include "avcodec.h"
+#include "dsputil.h"
+
+#ifndef CONFIG_RESAMPLE_HP
+#define FILTER_SHIFT 15
+
+#define FELEM int16_t
+#define FELEM2 int32_t
+#define FELEML int64_t
+#define FELEM_MAX INT16_MAX
+#define FELEM_MIN INT16_MIN
+#define WINDOW_TYPE 9
+#elif !defined(CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE)
+#define FILTER_SHIFT 30
+
+#define FELEM int32_t
+#define FELEM2 int64_t
+#define FELEML int64_t
+#define FELEM_MAX INT32_MAX
+#define FELEM_MIN INT32_MIN
+#define WINDOW_TYPE 12
+#else
+#define FILTER_SHIFT 0
+
+#define FELEM double
+#define FELEM2 double
+#define FELEML double
+#define WINDOW_TYPE 24
+#endif
+
+
+typedef struct AVResampleContext{
+    FELEM *filter_bank;
+    int filter_length;
+    int ideal_dst_incr;
+    int dst_incr;
+    int index;
+    int frac;
+    int src_incr;
+    int compensation_distance;
+    int phase_shift;
+    int phase_mask;
+    int linear;
+}AVResampleContext;
+
+/**
+ * 0th order modified bessel function of the first kind.
+ */
+static double bessel(double x){
+    double v=1;
+    double t=1;
+    int i;
+
+    x= x*x/4;
+    for(i=1; i<50; i++){
+        t *= x/(i*i);
+        v += t;
+    }
+    return v;
+}
+
+/**
+ * builds a polyphase filterbank.
+ * @param factor resampling factor
+ * @param scale wanted sum of coefficients for each filter
+ * @param type 0->cubic, 1->blackman nuttall windowed sinc, 2..16->kaiser windowed sinc beta=2..16
+ */
+void av_build_filter(FELEM *filter, double factor, int tap_count, int phase_count, int scale, int type){
+    int ph, i;
+    double x, y, w, tab[tap_count];
+    const int center= (tap_count-1)/2;
+
+    /* if upsampling, only need to interpolate, no filter */
+    if (factor > 1.0)
+        factor = 1.0;
+
+    for(ph=0;ph<phase_count;ph++) {
+        double norm = 0;
+        for(i=0;i<tap_count;i++) {
+            x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor;
+            if (x == 0) y = 1.0;
+            else        y = sin(x) / x;
+            switch(type){
+            case 0:{
+                const float d= -0.5; //first order derivative = -0.5
+                x = fabs(((double)(i - center) - (double)ph / phase_count) * factor);
+                if(x<1.0) y= 1 - 3*x*x + 2*x*x*x + d*(            -x*x + x*x*x);
+                else      y=                       d*(-4 + 8*x - 5*x*x + x*x*x);
+                break;}
+            case 1:
+                w = 2.0*x / (factor*tap_count) + M_PI;
+                y *= 0.3635819 - 0.4891775 * cos(w) + 0.1365995 * cos(2*w) - 0.0106411 * cos(3*w);
+                break;
+            default:
+                w = 2.0*x / (factor*tap_count*M_PI);
+                y *= bessel(type*sqrt(FFMAX(1-w*w, 0)));
+                break;
+            }
+
+            tab[i] = y;
+            norm += y;
+        }
+
+        /* normalize so that an uniform color remains the same */
+        for(i=0;i<tap_count;i++) {
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+            filter[ph * tap_count + i] = tab[i] / norm;
+#else
+            filter[ph * tap_count + i] = av_clip(lrintf(tab[i] * scale / norm), FELEM_MIN, FELEM_MAX);
+#endif
+        }
+    }
+#if 0
+    {
+#define LEN 1024
+        int j,k;
+        double sine[LEN + tap_count];
+        double filtered[LEN];
+        double maxff=-2, minff=2, maxsf=-2, minsf=2;
+        for(i=0; i<LEN; i++){
+            double ss=0, sf=0, ff=0;
+            for(j=0; j<LEN+tap_count; j++)
+                sine[j]= cos(i*j*M_PI/LEN);
+            for(j=0; j<LEN; j++){
+                double sum=0;
+                ph=0;
+                for(k=0; k<tap_count; k++)
+                    sum += filter[ph * tap_count + k] * sine[k+j];
+                filtered[j]= sum / (1<<FILTER_SHIFT);
+                ss+= sine[j + center] * sine[j + center];
+                ff+= filtered[j] * filtered[j];
+                sf+= sine[j + center] * filtered[j];
+            }
+            ss= sqrt(2*ss/LEN);
+            ff= sqrt(2*ff/LEN);
+            sf= 2*sf/LEN;
+            maxff= FFMAX(maxff, ff);
+            minff= FFMIN(minff, ff);
+            maxsf= FFMAX(maxsf, sf);
+            minsf= FFMIN(minsf, sf);
+            if(i%11==0){
+                av_log(NULL, AV_LOG_ERROR, "i:%4d ss:%f ff:%13.6e-%13.6e sf:%13.6e-%13.6e\n", i, ss, maxff, minff, maxsf, minsf);
+                minff=minsf= 2;
+                maxff=maxsf= -2;
+            }
+        }
+    }
+#endif
+}
+
+/**
+ * Initializes an audio resampler.
+ * Note, if either rate is not an integer then simply scale both rates up so they are.
+ */
+AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff){
+    AVResampleContext *c= av_mallocz(sizeof(AVResampleContext));
+    double factor= FFMIN(out_rate * cutoff / in_rate, 1.0);
+    int phase_count= 1<<phase_shift;
+
+    c->phase_shift= phase_shift;
+    c->phase_mask= phase_count-1;
+    c->linear= linear;
+
+    c->filter_length= FFMAX((int)ceil(filter_size/factor), 1);
+    c->filter_bank= av_mallocz(c->filter_length*(phase_count+1)*sizeof(FELEM));
+    av_build_filter(c->filter_bank, factor, c->filter_length, phase_count, 1<<FILTER_SHIFT, WINDOW_TYPE);
+    memcpy(&c->filter_bank[c->filter_length*phase_count+1], c->filter_bank, (c->filter_length-1)*sizeof(FELEM));
+    c->filter_bank[c->filter_length*phase_count]= c->filter_bank[c->filter_length - 1];
+
+    c->src_incr= out_rate;
+    c->ideal_dst_incr= c->dst_incr= in_rate * phase_count;
+    c->index= -phase_count*((c->filter_length-1)/2);
+
+    return c;
+}
+
+void av_resample_close(AVResampleContext *c){
+    av_freep(&c->filter_bank);
+    av_freep(&c);
+}
+
+/**
+ * Compensates samplerate/timestamp drift. The compensation is done by changing
+ * the resampler parameters, so no audible clicks or similar distortions ocur
+ * @param compensation_distance distance in output samples over which the compensation should be performed
+ * @param sample_delta number of output samples which should be output less
+ *
+ * example: av_resample_compensate(c, 10, 500)
+ * here instead of 510 samples only 500 samples would be output
+ *
+ * note, due to rounding the actual compensation might be slightly different,
+ * especially if the compensation_distance is large and the in_rate used during init is small
+ */
+void av_resample_compensate(AVResampleContext *c, int sample_delta, int compensation_distance){
+//    sample_delta += (c->ideal_dst_incr - c->dst_incr)*(int64_t)c->compensation_distance / c->ideal_dst_incr;
+    c->compensation_distance= compensation_distance;
+    c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * (int64_t)sample_delta / compensation_distance;
+}
+
+/**
+ * resamples.
+ * @param src an array of unconsumed samples
+ * @param consumed the number of samples of src which have been consumed are returned here
+ * @param src_size the number of unconsumed samples available
+ * @param dst_size the amount of space in samples available in dst
+ * @param update_ctx if this is 0 then the context wont be modified, that way several channels can be resampled with the same context
+ * @return the number of samples written in dst or -1 if an error occured
+ */
+int av_resample(AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx){
+    int dst_index, i;
+    int index= c->index;
+    int frac= c->frac;
+    int dst_incr_frac= c->dst_incr % c->src_incr;
+    int dst_incr=      c->dst_incr / c->src_incr;
+    int compensation_distance= c->compensation_distance;
+
+  if(compensation_distance == 0 && c->filter_length == 1 && c->phase_shift==0){
+        int64_t index2= ((int64_t)index)<<32;
+        int64_t incr= (1LL<<32) * c->dst_incr / c->src_incr;
+        dst_size= FFMIN(dst_size, (src_size-1-index) * (int64_t)c->src_incr / c->dst_incr);
+
+        for(dst_index=0; dst_index < dst_size; dst_index++){
+            dst[dst_index] = src[index2>>32];
+            index2 += incr;
+        }
+        frac += dst_index * dst_incr_frac;
+        index += dst_index * dst_incr;
+        index += frac / c->src_incr;
+        frac %= c->src_incr;
+  }else{
+    for(dst_index=0; dst_index < dst_size; dst_index++){
+        FELEM *filter= c->filter_bank + c->filter_length*(index & c->phase_mask);
+        int sample_index= index >> c->phase_shift;
+        FELEM2 val=0;
+
+        if(sample_index < 0){
+            for(i=0; i<c->filter_length; i++)
+                val += src[FFABS(sample_index + i) % src_size] * filter[i];
+        }else if(sample_index + c->filter_length > src_size){
+            break;
+        }else if(c->linear){
+            FELEM2 v2=0;
+            for(i=0; i<c->filter_length; i++){
+                val += src[sample_index + i] * (FELEM2)filter[i];
+                v2  += src[sample_index + i] * (FELEM2)filter[i + c->filter_length];
+            }
+            val+=(v2-val)*(FELEML)frac / c->src_incr;
+        }else{
+            for(i=0; i<c->filter_length; i++){
+                val += src[sample_index + i] * (FELEM2)filter[i];
+            }
+        }
+
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+        dst[dst_index] = av_clip_int16(lrintf(val));
+#else
+        val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;
+        dst[dst_index] = (unsigned)(val + 32768) > 65535 ? (val>>31) ^ 32767 : val;
+#endif
+
+        frac += dst_incr_frac;
+        index += dst_incr;
+        if(frac >= c->src_incr){
+            frac -= c->src_incr;
+            index++;
+        }
+
+        if(dst_index + 1 == compensation_distance){
+            compensation_distance= 0;
+            dst_incr_frac= c->ideal_dst_incr % c->src_incr;
+            dst_incr=      c->ideal_dst_incr / c->src_incr;
+        }
+    }
+  }
+    *consumed= FFMAX(index, 0) >> c->phase_shift;
+    if(index>=0) index &= c->phase_mask;
+
+    if(compensation_distance){
+        compensation_distance -= dst_index;
+        assert(compensation_distance > 0);
+    }
+    if(update_ctx){
+        c->frac= frac;
+        c->index= index;
+        c->dst_incr= dst_incr_frac + c->src_incr*dst_incr;
+        c->compensation_distance= compensation_distance;
+    }
+#if 0
+    if(update_ctx && !c->compensation_distance){
+#undef rand
+        av_resample_compensate(c, rand() % (8000*2) - 8000, 8000*2);
+av_log(NULL, AV_LOG_DEBUG, "%d %d %d\n", c->dst_incr, c->ideal_dst_incr, c->compensation_distance);
+    }
+#endif
+
+    return dst_index;
+}
diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c
new file mode 100644 (file)
index 0000000..f166ee3
--- /dev/null
@@ -0,0 +1,232 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "flist.h"
+
+/* Algorithm is not perfect, in a few corner cases it will fail to pop
+ * from the flist although it isn't empty, and fail to push into the
+ * flist, although it isn't full.
+ *
+ * We keep a fixed size array of entries, each item is either marked
+ * UNUSED, USED or BUSY and contains a user data pointer. When pushing
+ * into the queue we look for an UNUSED cell and mark it BUSY with a
+ * CAS operation. If successful we use it and mark it USED, otherwise
+ * we go on and look for the next UNUSED cell. The algorithm for
+ * popping an item from the queue is practically inverse: look for a
+ * USED cell and and mark it BUSY with a CAS operation, after reading
+ * from it mark it UNUSED again.
+ *
+ * To accelerate finding of used/unused cells we maintain a read and a
+ * write index which is used like a ring buffer. After each push we
+ * increase the write index and after each pop we increase the read
+ * index.
+ *
+ * The indexes are incremented atomically and are never truncated to
+ * the buffer size. Instead we assume that the buffer size is a power
+ * of two and that the truncation can thus be done by applying a
+ * simple AND on read.
+ *
+ * To make sure that we do not look for empty cells indefinitely we
+ * maintain a length value which stores the number of used cells. From
+ * this value the number of unused cells is easily calculated. Please
+ * note that the length value is not updated atomically with the read
+ * and write index and might thus be a few cells off the real
+ * value. To deal with this we always look for N_EXTRA_SCAN extra
+ * cells when pushing/popping entries.
+ *
+ * It might make sense to replace this implementation with a link list
+ * stack or queue, which however requires DCAS to be simple. Patches
+ * welcome.
+ *
+ * Please note that this algorithm is home grown.*/
+
+#define FLIST_SIZE 128
+#define N_EXTRA_SCAN 2
+
+/* For debugging purposes we can define _Y to put and extra thread
+ * yield between each operation. */
+
+#ifdef PROFILE
+#define _Y pa_thread_yield()
+#else
+#define _Y do { } while(0)
+#endif
+
+enum {
+    STATE_UNUSED,
+    STATE_USED,
+    STATE_BUSY
+};
+
+struct cell {
+    pa_atomic_t state;
+    void *data;
+};
+
+struct pa_flist {
+    unsigned size;
+    pa_atomic_t length;
+    pa_atomic_t read_idx;
+    pa_atomic_t write_idx;
+};
+
+#define PA_FLIST_CELLS(x) ((struct cell*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_flist))))
+
+pa_flist *pa_flist_new(unsigned size) {
+    pa_flist *l;
+
+    if (!size)
+        size = FLIST_SIZE;
+
+    pa_assert(pa_is_power_of_two(size));
+
+    l = pa_xmalloc0(PA_ALIGN(sizeof(pa_flist)) + (sizeof(struct cell) * size));
+
+    l->size = size;
+
+    pa_atomic_store(&l->read_idx, 0);
+    pa_atomic_store(&l->write_idx, 0);
+    pa_atomic_store(&l->length, 0);
+
+    return l;
+}
+
+static int reduce(pa_flist *l, int value) {
+    return value & (unsigned) (l->size - 1);
+}
+
+void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb) {
+    pa_assert(l);
+
+    if (free_cb) {
+        struct cell *cells;
+        int len, idx;
+
+        cells = PA_FLIST_CELLS(l);
+
+        idx = reduce(l, pa_atomic_load(&l->read_idx));
+        len = pa_atomic_load(&l->length);
+
+        for (; len > 0; len--) {
+
+            if (pa_atomic_load(&cells[idx].state) == STATE_USED)
+                free_cb(cells[idx].data);
+
+            idx = reduce(l, idx + 1);
+        }
+    }
+
+    pa_xfree(l);
+}
+
+int pa_flist_push(pa_flist*l, void *p) {
+    int idx, len, n;
+    struct cell *cells;
+
+    pa_assert(l);
+    pa_assert(p);
+
+    cells = PA_FLIST_CELLS(l);
+
+    n = len = (int) l->size - pa_atomic_load(&l->length) + N_EXTRA_SCAN;
+    _Y;
+    idx = reduce(l, pa_atomic_load(&l->write_idx));
+
+    for (; n > 0 ; n--) {
+        _Y;
+
+        if (pa_atomic_cmpxchg(&cells[idx].state, STATE_UNUSED, STATE_BUSY)) {
+            _Y;
+            pa_atomic_inc(&l->write_idx);
+            _Y;
+            cells[idx].data = p;
+            _Y;
+            pa_atomic_store(&cells[idx].state, STATE_USED);
+            _Y;
+            pa_atomic_inc(&l->length);
+            return 0;
+        }
+
+        _Y;
+        idx = reduce(l, idx + 1);
+    }
+
+#ifdef PROFILE
+    if (len > N_EXTRA_SCAN)
+        pa_log_warn("Didn't  find free cell after %u iterations.", len);
+#endif
+
+    return -1;
+}
+
+void* pa_flist_pop(pa_flist*l) {
+    int idx, len, n;
+    struct cell *cells;
+
+    pa_assert(l);
+
+    cells = PA_FLIST_CELLS(l);
+
+    n = len = pa_atomic_load(&l->length) + N_EXTRA_SCAN;
+    _Y;
+    idx = reduce(l, pa_atomic_load(&l->read_idx));
+
+    for (; n > 0 ; n--) {
+        _Y;
+
+        if (pa_atomic_cmpxchg(&cells[idx].state, STATE_USED, STATE_BUSY)) {
+            void *p;
+            _Y;
+            pa_atomic_inc(&l->read_idx);
+            _Y;
+            p = cells[idx].data;
+            _Y;
+            pa_atomic_store(&cells[idx].state, STATE_UNUSED);
+            _Y;
+
+            pa_atomic_dec(&l->length);
+            return p;
+        }
+
+        _Y;
+        idx = reduce(l, idx+1);
+    }
+
+#ifdef PROFILE
+    if (len > N_EXTRA_SCAN)
+        pa_log_warn("Didn't find used cell after %u iterations.", len);
+#endif
+
+    return NULL;
+}
diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h
new file mode 100644 (file)
index 0000000..c040667
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef foopulseflisthfoo
+#define foopulseflisthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/def.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/once.h>
+
+/* A multiple-reader multipler-write lock-free free list implementation */
+
+typedef struct pa_flist pa_flist;
+
+/* Size is required to be a power of two, or 0 for the default size */
+pa_flist * pa_flist_new(unsigned size);
+void pa_flist_free(pa_flist *l, pa_free_cb_t free_cb);
+
+/* Please note that this routine might fail! */
+int pa_flist_push(pa_flist*l, void *p);
+void* pa_flist_pop(pa_flist*l);
+
+/* Please not that the destructor stuff is not really necesary, we do
+ * this just to make valgrind output more useful. */
+
+#define PA_STATIC_FLIST_DECLARE(name, size, free_cb)                    \
+    static struct {                                                     \
+        pa_flist *flist;                                                \
+        pa_once once;                                                   \
+    } name##_flist = { NULL, PA_ONCE_INIT };                            \
+    static void name##_flist_init(void) {                               \
+        name##_flist.flist = pa_flist_new(size);                        \
+    }                                                                   \
+    static inline pa_flist* name##_flist_get(void) {                    \
+        pa_run_once(&name##_flist.once, name##_flist_init);             \
+        return name##_flist.flist;                                      \
+    }                                                                   \
+    static void name##_flist_destructor(void) PA_GCC_DESTRUCTOR;        \
+    static void name##_flist_destructor(void) {                         \
+        if (name##_flist.flist)                                         \
+            pa_flist_free(name##_flist.flist, (free_cb));               \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_STATIC_FLIST_GET(name) (name##_flist_get())
+
+#endif
similarity index 94%
rename from polyp/g711.c
rename to src/pulsecore/g711.c
index 55a82396cd7945267453dd3aee95224a03223af6..aa2d703a8e29326f341eb10989e71158b9962272 100644 (file)
-/*\r
- * This source code is a product of Sun Microsystems, Inc. and is provided\r
- * for unrestricted use.  Users may copy or modify this source code without\r
- * charge.\r
- *\r
- * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING\r
- * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR\r
- * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.\r
- *\r
- * Sun source code is provided with no support and without any obligation on\r
- * the part of Sun Microsystems, Inc. to assist in its use, correction,\r
- * modification or enhancement.\r
- *\r
- * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE\r
- * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE\r
- * OR ANY PART THEREOF.\r
- *\r
- * In no event will Sun Microsystems, Inc. be liable for any lost revenue\r
- * or profits or other special, indirect and consequential damages, even if\r
- * Sun has been advised of the possibility of such damages.\r
- *\r
- * Sun Microsystems, Inc.\r
- * 2550 Garcia Avenue\r
- * Mountain View, California  94043\r
- */\r
-\r
-/*\r
- * g711.c\r
- *\r
- * u-law, A-law and linear PCM conversions.\r
- */\r
-\r
-/*\r
- * December 30, 1994:\r
- * Functions linear2alaw, linear2ulaw have been updated to correctly\r
- * convert unquantized 16 bit values.\r
- * Tables for direct u- to A-law and A- to u-law conversions have been\r
- * corrected.\r
- * Borge Lindberg, Center for PersonKommunikation, Aalborg University.\r
- * bli@cpk.auc.dk\r
- *\r
- */\r
-\r
-#include "g711.h"\r
-\r
-#define        SIGN_BIT        (0x80)          /* Sign bit for a A-law byte. */\r
-#define        QUANT_MASK      (0xf)           /* Quantization field mask. */\r
-#define        NSEGS           (8)             /* Number of A-law segments. */\r
-#define        SEG_SHIFT       (4)             /* Left shift for segment number. */\r
-#define        SEG_MASK        (0x70)          /* Segment field mask. */\r
-\r
-#if !defined(FAST_ALAW_CONVERSION) || !defined(FAST_ULAW_CONVERSION)\r
-static int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,\r
-                             0x1FF, 0x3FF, 0x7FF, 0xFFF};\r
-static int16_t seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,\r
-                             0x3FF, 0x7FF, 0xFFF, 0x1FFF};\r
-\r
-static int16_t search(\r
-       int16_t val,\r
-       int16_t *table,\r
-       int size)\r
-{\r
-       int i;\r
-\r
-       for (i = 0; i < size; i++) {\r
-               if (val <= *table++)\r
-                       return (i);\r
-       }\r
-       return (size);\r
-}\r
-#endif /* !FAST_*_CONVERSION */\r
-\r
-#ifndef FAST_ALAW_CONVERSION\r
-/*\r
- * linear2alaw() accepts an 13-bit signed integer and encodes it as A-law data\r
- * stored in a unsigned char.  This function should only be called with\r
- * the data shifted such that it only contains information in the lower\r
- * 13-bits.\r
- *\r
- *             Linear Input Code       Compressed Code\r
- *     ------------------------        ---------------\r
- *     0000000wxyza                    000wxyz\r
- *     0000001wxyza                    001wxyz\r
- *     000001wxyzab                    010wxyz\r
- *     00001wxyzabc                    011wxyz\r
- *     0001wxyzabcd                    100wxyz\r
- *     001wxyzabcde                    101wxyz\r
- *     01wxyzabcdef                    110wxyz\r
- *     1wxyzabcdefg                    111wxyz\r
- *\r
- * For further information see John C. Bellamy's Digital Telephony, 1982,\r
- * John Wiley & Sons, pps 98-111 and 472-476.\r
- */\r
-unsigned char st_13linear2alaw(\r
-       int16_t         pcm_val)        /* 2's complement (13-bit range) */\r
-{\r
-       int16_t         mask;\r
-       short           seg;\r
-       unsigned char   aval;\r
-\r
-       /* Have calling software do it since its already doing a shift\r
-        * from 32-bits down to 16-bits.\r
-        */\r
-       /* pcm_val = pcm_val >> 3; */\r
-\r
-       /* A-law using even bit inversion */\r
-       if (pcm_val >= 0) {\r
-               mask = 0xD5;            /* sign (7th) bit = 1 */\r
-       } else {\r
-               mask = 0x55;            /* sign bit = 0 */\r
-               pcm_val = -pcm_val - 1;\r
-       }\r
-\r
-       /* Convert the scaled magnitude to segment number. */\r
-       seg = search(pcm_val, seg_aend, 8);\r
-\r
-       /* Combine the sign, segment, and quantization bits. */\r
-\r
-       if (seg >= 8)           /* out of range, return maximum value. */\r
-               return (unsigned char) (0x7F ^ mask);\r
-       else {\r
-               aval = (unsigned char) seg << SEG_SHIFT;\r
-               if (seg < 2)\r
-                       aval |= (pcm_val >> 1) & QUANT_MASK;\r
-               else\r
-                       aval |= (pcm_val >> seg) & QUANT_MASK;\r
-               return (aval ^ mask);\r
-       }\r
-}\r
-\r
-/*\r
- * alaw2linear() - Convert an A-law value to 16-bit signed linear PCM\r
- *\r
- */\r
-int16_t st_alaw2linear16(\r
-       unsigned char   a_val)\r
-{\r
-       int16_t t;\r
-       int16_t seg;\r
-\r
-       a_val ^= 0x55;\r
-\r
-       t = (a_val & QUANT_MASK) << 4;\r
-       seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;\r
-       switch (seg) {\r
-       case 0:\r
-               t += 8;\r
-               break;\r
-       case 1:\r
-               t += 0x108;\r
-               break;\r
-       default:\r
-               t += 0x108;\r
-               t <<= seg - 1;\r
-       }\r
-       return ((a_val & SIGN_BIT) ? t : -t);\r
-}\r
-#endif /* !FAST_ALAW_CONVERSION */\r
-\r
-#define        BIAS            (0x84)          /* Bias for linear code. */\r
-#define CLIP            8159\r
-\r
-#ifndef FAST_ULAW_CONVERSION\r
-/*\r
- * linear2ulaw() accepts a 14-bit signed integer and encodes it as u-law data\r
- * stored in a unsigned char.  This function should only be called with\r
- * the data shifted such that it only contains information in the lower\r
- * 14-bits.\r
- *\r
- * In order to simplify the encoding process, the original linear magnitude\r
- * is biased by adding 33 which shifts the encoding range from (0 - 8158) to\r
- * (33 - 8191). The result can be seen in the following encoding table:\r
- *\r
- *     Biased Linear Input Code        Compressed Code\r
- *     ------------------------        ---------------\r
- *     00000001wxyza                   000wxyz\r
- *     0000001wxyzab                   001wxyz\r
- *     000001wxyzabc                   010wxyz\r
- *     00001wxyzabcd                   011wxyz\r
- *     0001wxyzabcde                   100wxyz\r
- *     001wxyzabcdef                   101wxyz\r
- *     01wxyzabcdefg                   110wxyz\r
- *     1wxyzabcdefgh                   111wxyz\r
- *\r
- * Each biased linear code has a leading 1 which identifies the segment\r
- * number. The value of the segment number is equal to 7 minus the number\r
- * of leading 0's. The quantization interval is directly available as the\r
- * four bits wxyz.  * The trailing bits (a - h) are ignored.\r
- *\r
- * Ordinarily the complement of the resulting code word is used for\r
- * transmission, and so the code word is complemented before it is returned.\r
- *\r
- * For further information see John C. Bellamy's Digital Telephony, 1982,\r
- * John Wiley & Sons, pps 98-111 and 472-476.\r
- */\r
-unsigned char st_14linear2ulaw(\r
-       int16_t         pcm_val)        /* 2's complement (14-bit range) */\r
-{\r
-       int16_t         mask;\r
-       int16_t         seg;\r
-       unsigned char   uval;\r
-\r
-       /* Have calling software do it since its already doing a shift\r
-        * from 32-bits down to 16-bits.\r
-        */\r
-       /* pcm_val = pcm_val >> 2; */\r
-\r
-       /* u-law inverts all bits */\r
-       /* Get the sign and the magnitude of the value. */\r
-       if (pcm_val < 0) {\r
-               pcm_val = -pcm_val;\r
-               mask = 0x7F;\r
-       } else {\r
-               mask = 0xFF;\r
-       }\r
-        if ( pcm_val > CLIP ) pcm_val = CLIP;          /* clip the magnitude */\r
-       pcm_val += (BIAS >> 2);\r
-\r
-       /* Convert the scaled magnitude to segment number. */\r
-       seg = search(pcm_val, seg_uend, 8);\r
-\r
-       /*\r
-        * Combine the sign, segment, quantization bits;\r
-        * and complement the code word.\r
-        */\r
-       if (seg >= 8)           /* out of range, return maximum value. */\r
-               return (unsigned char) (0x7F ^ mask);\r
-       else {\r
-               uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);\r
-               return (uval ^ mask);\r
-       }\r
-\r
-}\r
-\r
-/*\r
- * ulaw2linear() - Convert a u-law value to 16-bit linear PCM\r
- *\r
- * First, a biased linear code is derived from the code word. An unbiased\r
- * output can then be obtained by subtracting 33 from the biased code.\r
- *\r
- * Note that this function expects to be passed the complement of the\r
- * original code word. This is in keeping with ISDN conventions.\r
- */\r
-int16_t st_ulaw2linear16(\r
-       unsigned char   u_val)\r
-{\r
-       int16_t         t;\r
-\r
-       /* Complement to obtain normal u-law value. */\r
-       u_val = ~u_val;\r
-\r
-       /*\r
-        * Extract and bias the quantization bits. Then\r
-        * shift up by the segment number and subtract out the bias.\r
-        */\r
-       t = ((u_val & QUANT_MASK) << 3) + BIAS;\r
-       t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;\r
-\r
-       return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));\r
-}\r
-#endif /* !FAST_ULAW_CONVERSION */\r
-\r
-#ifdef FAST_ALAW_CONVERSION\r
-\r
-int16_t _st_alaw2linear16[256] = {\r
-     -5504,   -5248,   -6016,   -5760,   -4480,   -4224,   -4992,\r
-     -4736,   -7552,   -7296,   -8064,   -7808,   -6528,   -6272,\r
-     -7040,   -6784,   -2752,   -2624,   -3008,   -2880,   -2240,\r
-     -2112,   -2496,   -2368,   -3776,   -3648,   -4032,   -3904,\r
-     -3264,   -3136,   -3520,   -3392,  -22016,  -20992,  -24064,\r
-    -23040,  -17920,  -16896,  -19968,  -18944,  -30208,  -29184,\r
-    -32256,  -31232,  -26112,  -25088,  -28160,  -27136,  -11008,\r
-    -10496,  -12032,  -11520,   -8960,   -8448,   -9984,   -9472,\r
-    -15104,  -14592,  -16128,  -15616,  -13056,  -12544,  -14080,\r
-    -13568,    -344,    -328,    -376,    -360,    -280,    -264,\r
-      -312,    -296,    -472,    -456,    -504,    -488,    -408,\r
-      -392,    -440,    -424,     -88,     -72,    -120,    -104,\r
-       -24,      -8,     -56,     -40,    -216,    -200,    -248,\r
-      -232,    -152,    -136,    -184,    -168,   -1376,   -1312,\r
-     -1504,   -1440,   -1120,   -1056,   -1248,   -1184,   -1888,\r
-     -1824,   -2016,   -1952,   -1632,   -1568,   -1760,   -1696,\r
-      -688,    -656,    -752,    -720,    -560,    -528,    -624,\r
-      -592,    -944,    -912,   -1008,    -976,    -816,    -784,\r
-      -880,    -848,    5504,    5248,    6016,    5760,    4480,\r
-      4224,    4992,    4736,    7552,    7296,    8064,    7808,\r
-      6528,    6272,    7040,    6784,    2752,    2624,    3008,\r
-      2880,    2240,    2112,    2496,    2368,    3776,    3648,\r
-      4032,    3904,    3264,    3136,    3520,    3392,   22016,\r
-     20992,   24064,   23040,   17920,   16896,   19968,   18944,\r
-     30208,   29184,   32256,   31232,   26112,   25088,   28160,\r
-     27136,   11008,   10496,   12032,   11520,    8960,    8448,\r
-      9984,    9472,   15104,   14592,   16128,   15616,   13056,\r
-     12544,   14080,   13568,     344,     328,     376,     360,\r
-       280,     264,     312,     296,     472,     456,     504,\r
-       488,     408,     392,     440,     424,      88,      72,\r
-       120,     104,      24,       8,      56,      40,     216,\r
-       200,     248,     232,     152,     136,     184,     168,\r
-      1376,    1312,    1504,    1440,    1120,    1056,    1248,\r
-      1184,    1888,    1824,    2016,    1952,    1632,    1568,\r
-      1760,    1696,     688,     656,     752,     720,     560,\r
-       528,     624,     592,     944,     912,    1008,     976,\r
-       816,     784,     880,     848\r
-};\r
-\r
-uint8_t _st_13linear2alaw[0x2000] = {\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x3a, 0x3a, 0x3a, 0x3a,\r
-   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,\r
-   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,\r
-   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,\r
-   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,\r
-   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,\r
-   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,\r
-   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,\r
-   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,\r
-   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,\r
-   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,\r
-   0x3b, 0x3b, 0x3b, 0x3b, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,\r
-   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,\r
-   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,\r
-   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,\r
-   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,\r
-   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39,\r
-   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,\r
-   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,\r
-   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,\r
-   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,\r
-   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,\r
-   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,\r
-   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,\r
-   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,\r
-   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,\r
-   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,\r
-   0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,\r
-   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,\r
-   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,\r
-   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,\r
-   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,\r
-   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3c, 0x3c, 0x3c, 0x3c,\r
-   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,\r
-   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,\r
-   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,\r
-   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,\r
-   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,\r
-   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,\r
-   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,\r
-   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,\r
-   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,\r
-   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,\r
-   0x3d, 0x3d, 0x3d, 0x3d, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,\r
-   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,\r
-   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,\r
-   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,\r
-   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,\r
-   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33,\r
-   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,\r
-   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,\r
-   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,\r
-   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,\r
-   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,\r
-   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,\r
-   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,\r
-   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,\r
-   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,\r
-   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,\r
-   0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,\r
-   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,\r
-   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,\r
-   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,\r
-   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,\r
-   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x36, 0x36, 0x36, 0x36,\r
-   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,\r
-   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,\r
-   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,\r
-   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,\r
-   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,\r
-   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,\r
-   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,\r
-   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,\r
-   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,\r
-   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,\r
-   0x37, 0x37, 0x37, 0x37, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,\r
-   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,\r
-   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,\r
-   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,\r
-   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,\r
-   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35,\r
-   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,\r
-   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,\r
-   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,\r
-   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,\r
-   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b,\r
-   0x6b, 0x6b, 0x6b, 0x6b, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,\r
-   0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6e, 0x6e, 0x6e, 0x6e,\r
-   0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,\r
-   0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d,\r
-   0x6d, 0x6d, 0x6d, 0x6d, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,\r
-   0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x60, 0x60, 0x60, 0x60,\r
-   0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,\r
-   0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67,\r
-   0x67, 0x67, 0x67, 0x67, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,\r
-   0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x7a, 0x7a, 0x7a, 0x7a,\r
-   0x7b, 0x7b, 0x7b, 0x7b, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,\r
-   0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7c, 0x7c, 0x7c, 0x7c,\r
-   0x7d, 0x7d, 0x7d, 0x7d, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73,\r
-   0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, 0x76, 0x76, 0x76, 0x76,\r
-   0x77, 0x77, 0x77, 0x77, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75,\r
-   0x4a, 0x4a, 0x4b, 0x4b, 0x48, 0x48, 0x49, 0x49, 0x4e, 0x4e, 0x4f, 0x4f,\r
-   0x4c, 0x4c, 0x4d, 0x4d, 0x42, 0x42, 0x43, 0x43, 0x40, 0x40, 0x41, 0x41,\r
-   0x46, 0x46, 0x47, 0x47, 0x44, 0x44, 0x45, 0x45, 0x5a, 0x5a, 0x5b, 0x5b,\r
-   0x58, 0x58, 0x59, 0x59, 0x5e, 0x5e, 0x5f, 0x5f, 0x5c, 0x5c, 0x5d, 0x5d,\r
-   0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, 0x56, 0x56, 0x57, 0x57,\r
-   0x54, 0x54, 0x55, 0x55, 0xd5, 0xd5, 0xd4, 0xd4, 0xd7, 0xd7, 0xd6, 0xd6,\r
-   0xd1, 0xd1, 0xd0, 0xd0, 0xd3, 0xd3, 0xd2, 0xd2, 0xdd, 0xdd, 0xdc, 0xdc,\r
-   0xdf, 0xdf, 0xde, 0xde, 0xd9, 0xd9, 0xd8, 0xd8, 0xdb, 0xdb, 0xda, 0xda,\r
-   0xc5, 0xc5, 0xc4, 0xc4, 0xc7, 0xc7, 0xc6, 0xc6, 0xc1, 0xc1, 0xc0, 0xc0,\r
-   0xc3, 0xc3, 0xc2, 0xc2, 0xcd, 0xcd, 0xcc, 0xcc, 0xcf, 0xcf, 0xce, 0xce,\r
-   0xc9, 0xc9, 0xc8, 0xc8, 0xcb, 0xcb, 0xca, 0xca, 0xf5, 0xf5, 0xf5, 0xf5,\r
-   0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6,\r
-   0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf3, 0xf3, 0xf3, 0xf3,\r
-   0xf2, 0xf2, 0xf2, 0xf2, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc,\r
-   0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xf9, 0xf9, 0xf9, 0xf9,\r
-   0xf8, 0xf8, 0xf8, 0xf8, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa,\r
-   0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4,\r
-   0xe4, 0xe4, 0xe4, 0xe4, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,\r
-   0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1,\r
-   0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,\r
-   0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2,\r
-   0xe2, 0xe2, 0xe2, 0xe2, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,\r
-   0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef,\r
-   0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,\r
-   0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8,\r
-   0xe8, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,\r
-   0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5,\r
-   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\r
-   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\r
-   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\r
-   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\r
-   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\r
-   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\r
-   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\r
-   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\r
-   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\r
-   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\r
-   0xb4, 0xb4, 0xb4, 0xb4, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\r
-   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\r
-   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\r
-   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\r
-   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\r
-   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6,\r
-   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\r
-   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\r
-   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\r
-   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\r
-   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\r
-   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\r
-   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\r
-   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\r
-   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\r
-   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\r
-   0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\r
-   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\r
-   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\r
-   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\r
-   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\r
-   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3,\r
-   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\r
-   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\r
-   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\r
-   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\r
-   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\r
-   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\r
-   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\r
-   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\r
-   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\r
-   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\r
-   0xb2, 0xb2, 0xb2, 0xb2, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\r
-   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\r
-   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\r
-   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\r
-   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\r
-   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc,\r
-   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\r
-   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\r
-   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\r
-   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\r
-   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\r
-   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\r
-   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\r
-   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\r
-   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\r
-   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\r
-   0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\r
-   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\r
-   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\r
-   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\r
-   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\r
-   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9,\r
-   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\r
-   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\r
-   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\r
-   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\r
-   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\r
-   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\r
-   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\r
-   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\r
-   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\r
-   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\r
-   0xb8, 0xb8, 0xb8, 0xb8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\r
-   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\r
-   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\r
-   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\r
-   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\r
-   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba,\r
-   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\r
-   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\r
-   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\r
-   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\r
-   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa\r
-};\r
-\r
-#endif /* FAST_ALAW_CONVERSION */\r
-\r
-#ifdef FAST_ULAW_CONVERSION\r
-\r
-int16_t _st_ulaw2linear16[256] = {\r
-    -32124,  -31100,  -30076,  -29052,  -28028,  -27004,  -25980,\r
-    -24956,  -23932,  -22908,  -21884,  -20860,  -19836,  -18812,\r
-    -17788,  -16764,  -15996,  -15484,  -14972,  -14460,  -13948,\r
-    -13436,  -12924,  -12412,  -11900,  -11388,  -10876,  -10364,\r
-     -9852,   -9340,   -8828,   -8316,   -7932,   -7676,   -7420,\r
-     -7164,   -6908,   -6652,   -6396,   -6140,   -5884,   -5628,\r
-     -5372,   -5116,   -4860,   -4604,   -4348,   -4092,   -3900,\r
-     -3772,   -3644,   -3516,   -3388,   -3260,   -3132,   -3004,\r
-     -2876,   -2748,   -2620,   -2492,   -2364,   -2236,   -2108,\r
-     -1980,   -1884,   -1820,   -1756,   -1692,   -1628,   -1564,\r
-     -1500,   -1436,   -1372,   -1308,   -1244,   -1180,   -1116,\r
-     -1052,    -988,    -924,    -876,    -844,    -812,    -780,\r
-      -748,    -716,    -684,    -652,    -620,    -588,    -556,\r
-      -524,    -492,    -460,    -428,    -396,    -372,    -356,\r
-      -340,    -324,    -308,    -292,    -276,    -260,    -244,\r
-      -228,    -212,    -196,    -180,    -164,    -148,    -132,\r
-      -120,    -112,    -104,     -96,     -88,     -80,     -72,\r
-       -64,     -56,     -48,     -40,     -32,     -24,     -16,\r
-        -8,       0,   32124,   31100,   30076,   29052,   28028,\r
-     27004,   25980,   24956,   23932,   22908,   21884,   20860,\r
-     19836,   18812,   17788,   16764,   15996,   15484,   14972,\r
-     14460,   13948,   13436,   12924,   12412,   11900,   11388,\r
-     10876,   10364,    9852,    9340,    8828,    8316,    7932,\r
-      7676,    7420,    7164,    6908,    6652,    6396,    6140,\r
-      5884,    5628,    5372,    5116,    4860,    4604,    4348,\r
-      4092,    3900,    3772,    3644,    3516,    3388,    3260,\r
-      3132,    3004,    2876,    2748,    2620,    2492,    2364,\r
-      2236,    2108,    1980,    1884,    1820,    1756,    1692,\r
-      1628,    1564,    1500,    1436,    1372,    1308,    1244,\r
-      1180,    1116,    1052,     988,     924,     876,     844,\r
-       812,     780,     748,     716,     684,     652,     620,\r
-       588,     556,     524,     492,     460,     428,     396,\r
-       372,     356,     340,     324,     308,     292,     276,\r
-       260,     244,     228,     212,     196,     180,     164,\r
-       148,     132,     120,     112,     104,      96,      88,\r
-        80,      72,      64,      56,      48,      40,      32,\r
-        24,      16,       8,       0\r
-};\r
-\r
-uint8_t _st_14linear2ulaw[0x4000] = {\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
-   0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
-   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
-   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
-   0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
-   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
-   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
-   0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
-   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
-   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
-   0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,\r
-   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,\r
-   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,\r
-   0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,\r
-   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,\r
-   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,\r
-   0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,\r
-   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,\r
-   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,\r
-   0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,\r
-   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,\r
-   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,\r
-   0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,\r
-   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,\r
-   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,\r
-   0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,\r
-   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,\r
-   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,\r
-   0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,\r
-   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,\r
-   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,\r
-   0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,\r
-   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\r
-   0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,\r
-   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\r
-   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,\r
-   0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,\r
-   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,\r
-   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\r
-   0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,\r
-   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,\r
-   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,\r
-   0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,\r
-   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,\r
-   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\r
-   0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,\r
-   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,\r
-   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,\r
-   0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,\r
-   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,\r
-   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31,\r
-   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,\r
-   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,\r
-   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,\r
-   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,\r
-   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,\r
-   0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,\r
-   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,\r
-   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34,\r
-   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,\r
-   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,\r
-   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,\r
-   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,\r
-   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,\r
-   0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,\r
-   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,\r
-   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37,\r
-   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,\r
-   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,\r
-   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,\r
-   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,\r
-   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,\r
-   0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,\r
-   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,\r
-   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a,\r
-   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,\r
-   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,\r
-   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,\r
-   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,\r
-   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,\r
-   0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,\r
-   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,\r
-   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d,\r
-   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,\r
-   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,\r
-   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,\r
-   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,\r
-   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,\r
-   0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,\r
-   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,\r
-   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40,\r
-   0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,\r
-   0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\r
-   0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\r
-   0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43,\r
-   0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,\r
-   0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\r
-   0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,\r
-   0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46,\r
-   0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,\r
-   0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,\r
-   0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,\r
-   0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49,\r
-   0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,\r
-   0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,\r
-   0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b,\r
-   0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,\r
-   0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\r
-   0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d,\r
-   0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e,\r
-   0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f,\r
-   0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,\r
-   0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51,\r
-   0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52,\r
-   0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54,\r
-   0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,\r
-   0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57,\r
-   0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,\r
-   0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a,\r
-   0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b,\r
-   0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,\r
-   0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,\r
-   0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60,\r
-   0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63,\r
-   0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66,\r
-   0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69,\r
-   0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c,\r
-   0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,\r
-   0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,\r
-   0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a,\r
-   0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0xff, 0xfe, 0xfe, 0xfd,\r
-   0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7,\r
-   0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1,\r
-   0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed,\r
-   0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea,\r
-   0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7,\r
-   0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4,\r
-   0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1,\r
-   0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,\r
-   0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd,\r
-   0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,\r
-   0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xda,\r
-   0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,\r
-   0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7,\r
-   0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,\r
-   0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4,\r
-   0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,\r
-   0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1,\r
-   0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,\r
-   0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,\r
-   0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce,\r
-   0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd,\r
-   0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,\r
-   0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,\r
-   0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,\r
-   0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca,\r
-   0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca,\r
-   0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9,\r
-   0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,\r
-   0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7,\r
-   0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,\r
-   0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,\r
-   0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,\r
-   0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4,\r
-   0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,\r
-   0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,\r
-   0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,\r
-   0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1,\r
-   0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,\r
-   0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,\r
-   0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\r
-   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\r
-   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\r
-   0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\r
-   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\r
-   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd,\r
-   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\r
-   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\r
-   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\r
-   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\r
-   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\r
-   0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\r
-   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\r
-   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba,\r
-   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\r
-   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\r
-   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\r
-   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\r
-   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\r
-   0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\r
-   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\r
-   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7,\r
-   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\r
-   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\r
-   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\r
-   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\r
-   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\r
-   0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\r
-   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\r
-   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4,\r
-   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\r
-   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\r
-   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\r
-   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\r
-   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\r
-   0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\r
-   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\r
-   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1,\r
-   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\r
-   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\r
-   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\r
-   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\r
-   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\r
-   0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\r
-   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\r
-   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\r
-   0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\r
-   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\r
-   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\r
-   0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\r
-   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\r
-   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\r
-   0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\r
-   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\r
-   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\r
-   0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\r
-   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\r
-   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\r
-   0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\r
-   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\r
-   0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\r
-   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\r
-   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\r
-   0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\r
-   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\r
-   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\r
-   0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\r
-   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\r
-   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\r
-   0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\r
-   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\r
-   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\r
-   0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\r
-   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\r
-   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\r
-   0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\r
-   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\r
-   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\r
-   0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\r
-   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\r
-   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\r
-   0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\r
-   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\r
-   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\r
-   0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\r
-   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\r
-   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\r
-   0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\r
-   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\r
-   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\r
-   0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\r
-   0x80, 0x80, 0x80, 0x80\r
-};\r
-\r
-#endif /* FAST_ULAW_CONVERSION */\r
-\r
-/* The following code was used to generate the lookup tables */\r
-#if 0\r
-int main()\r
-{\r
-    int x, y, find2a = 0;\r
-\r
-    y = 0;\r
-    printf("int16_t _st_alaw2linear16[256] = {\n  ");\r
-    for (x = 0; x < 256; x++)\r
-    {\r
-       printf("%8d,", st_alaw2linear16(x));\r
-       y++;\r
-       if (y == 7)\r
-       {\r
-           y = 0;\r
-           printf("\n  ");\r
-       }\r
-    }\r
-\r
-    printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n  ");\r
-    y = 0;\r
-    for (x = 0; x < 0x2000; x++)\r
-    {\r
-       printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x));\r
-       y++;\r
-       if (y == 12)\r
-       {\r
-           y = 0;\r
-           printf("\n  ");\r
-       }\r
-    }\r
-\r
-    printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n  ");\r
-    y = 0;\r
-    for (x = 0; x < 256; x++)\r
-    {\r
-       printf("%8d,", st_ulaw2linear16(x));\r
-       y++;\r
-       if (y == 7)\r
-       {\r
-           y = 0;\r
-           printf("\n  ");\r
-       }\r
-    }\r
-\r
-    printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n  ");\r
-    y = 0;\r
-    for (x = 0; x < 0x4000; x++)\r
-    {\r
-       printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x));\r
-       y++;\r
-       if (y == 12)\r
-       {\r
-           y = 0;\r
-           printf("\n  ");\r
-       }\r
-    }\r
-    printf("\n};\n");\r
-\r
-}\r
-#endif\r
-\r
-/* The following is not used by SoX but kept for reference */\r
-#if 0\r
-/* copy from CCITT G.711 specifications */\r
-unsigned char _u2a[128] = {                    /* u- to A-law conversions */\r
-       1,      1,      2,      2,      3,      3,      4,      4,\r
-       5,      5,      6,      6,      7,      7,      8,      8,\r
-       9,      10,     11,     12,     13,     14,     15,     16,\r
-       17,     18,     19,     20,     21,     22,     23,     24,\r
-       25,     27,     29,     31,     33,     34,     35,     36,\r
-       37,     38,     39,     40,     41,     42,     43,     44,\r
-       46,     48,     49,     50,     51,     52,     53,     54,\r
-       55,     56,     57,     58,     59,     60,     61,     62,\r
-       64,     65,     66,     67,     68,     69,     70,     71,\r
-       72,     73,     74,     75,     76,     77,     78,     79,\r
-/* corrected:\r
-       81,     82,     83,     84,     85,     86,     87,     88, \r
-   should be: */\r
-       80,     82,     83,     84,     85,     86,     87,     88,\r
-       89,     90,     91,     92,     93,     94,     95,     96,\r
-       97,     98,     99,     100,    101,    102,    103,    104,\r
-       105,    106,    107,    108,    109,    110,    111,    112,\r
-       113,    114,    115,    116,    117,    118,    119,    120,\r
-       121,    122,    123,    124,    125,    126,    127,    128};\r
-\r
-unsigned char _a2u[128] = {                    /* A- to u-law conversions */\r
-       1,      3,      5,      7,      9,      11,     13,     15,\r
-       16,     17,     18,     19,     20,     21,     22,     23,\r
-       24,     25,     26,     27,     28,     29,     30,     31,\r
-       32,     32,     33,     33,     34,     34,     35,     35,\r
-       36,     37,     38,     39,     40,     41,     42,     43,\r
-       44,     45,     46,     47,     48,     48,     49,     49,\r
-       50,     51,     52,     53,     54,     55,     56,     57,\r
-       58,     59,     60,     61,     62,     63,     64,     64,\r
-       65,     66,     67,     68,     69,     70,     71,     72,\r
-/* corrected:\r
-       73,     74,     75,     76,     77,     78,     79,     79,\r
-   should be: */\r
-       73,     74,     75,     76,     77,     78,     79,     80,\r
-\r
-       80,     81,     82,     83,     84,     85,     86,     87,\r
-       88,     89,     90,     91,     92,     93,     94,     95,\r
-       96,     97,     98,     99,     100,    101,    102,    103,\r
-       104,    105,    106,    107,    108,    109,    110,    111,\r
-       112,    113,    114,    115,    116,    117,    118,    119,\r
-       120,    121,    122,    123,    124,    125,    126,    127};\r
-\r
-/* A-law to u-law conversion */\r
-unsigned char st_alaw2ulaw(\r
-       unsigned char   aval)\r
-{\r
-       aval &= 0xff;\r
-       return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :\r
-           (0x7F ^ _a2u[aval ^ 0x55]));\r
-}\r
-\r
-/* u-law to A-law conversion */\r
-unsigned char st_ulaw2alaw(\r
-       unsigned char   uval)\r
-{\r
-       uval &= 0xff;\r
-       return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :\r
-           (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));\r
-}\r
-#endif\r
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use.  Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+
+/*
+ * December 30, 1994:
+ * Functions linear2alaw, linear2ulaw have been updated to correctly
+ * convert unquantized 16 bit values.
+ * Tables for direct u- to A-law and A- to u-law conversions have been
+ * corrected.
+ * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
+ * bli@cpk.auc.dk
+ *
+ */
+
+#include "g711.h"
+
+#define        SIGN_BIT        (0x80)                /* Sign bit for a A-law byte. */
+#define        QUANT_MASK        (0xf)                /* Quantization field mask. */
+#define        NSEGS                (8)                /* Number of A-law segments. */
+#define        SEG_SHIFT        (4)                /* Left shift for segment number. */
+#define        SEG_MASK        (0x70)                /* Segment field mask. */
+
+#if !defined(FAST_ALAW_CONVERSION) || !defined(FAST_ULAW_CONVERSION)
+static int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
+                              0x1FF, 0x3FF, 0x7FF, 0xFFF};
+static int16_t seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
+                              0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+
+static int16_t search(
+        int16_t        val,
+        int16_t *table,
+        int size)
+{
+        int i;
+
+        for (i = 0; i < size; i++) {
+                if (val <= *table++)
+                        return (i);
+        }
+        return (size);
+}
+#endif /* !FAST_*_CONVERSION */
+
+#ifndef FAST_ALAW_CONVERSION
+/*
+ * linear2alaw() accepts an 13-bit signed integer and encodes it as A-law data
+ * stored in a unsigned char.  This function should only be called with
+ * the data shifted such that it only contains information in the lower
+ * 13-bits.
+ *
+ *                Linear Input Code        Compressed Code
+ *        ------------------------        ---------------
+ *        0000000wxyza                        000wxyz
+ *        0000001wxyza                        001wxyz
+ *        000001wxyzab                        010wxyz
+ *        00001wxyzabc                        011wxyz
+ *        0001wxyzabcd                        100wxyz
+ *        001wxyzabcde                        101wxyz
+ *        01wxyzabcdef                        110wxyz
+ *        1wxyzabcdefg                        111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char st_13linear2alaw(
+        int16_t                pcm_val)        /* 2's complement (13-bit range) */
+{
+        int16_t                mask;
+        short                seg;
+        unsigned char        aval;
+
+        /* Have calling software do it since its already doing a shift
+         * from 32-bits down to 16-bits.
+         */
+        /* pcm_val = pcm_val >> 3; */
+
+        /* A-law using even bit inversion */
+        if (pcm_val >= 0) {
+                mask = 0xD5;                /* sign (7th) bit = 1 */
+        } else {
+                mask = 0x55;                /* sign bit = 0 */
+                pcm_val = -pcm_val - 1;
+        }
+
+        /* Convert the scaled magnitude to segment number. */
+        seg = search(pcm_val, seg_aend, 8);
+
+        /* Combine the sign, segment, and quantization bits. */
+
+        if (seg >= 8)                /* out of range, return maximum value. */
+                return (unsigned char) (0x7F ^ mask);
+        else {
+                aval = (unsigned char) seg << SEG_SHIFT;
+                if (seg < 2)
+                        aval |= (pcm_val >> 1) & QUANT_MASK;
+                else
+                        aval |= (pcm_val >> seg) & QUANT_MASK;
+                return (aval ^ mask);
+        }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit signed linear PCM
+ *
+ */
+int16_t st_alaw2linear16(
+        unsigned char        a_val)
+{
+        int16_t t;
+        int16_t seg;
+
+        a_val ^= 0x55;
+
+        t = (a_val & QUANT_MASK) << 4;
+        seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+        switch (seg) {
+        case 0:
+                t += 8;
+                break;
+        case 1:
+                t += 0x108;
+                break;
+        default:
+                t += 0x108;
+                t <<= seg - 1;
+        }
+        return ((a_val & SIGN_BIT) ? t : -t);
+}
+#endif /* !FAST_ALAW_CONVERSION */
+
+#define        BIAS                (0x84)                /* Bias for linear code. */
+#define CLIP            8159
+
+#ifndef FAST_ULAW_CONVERSION
+/*
+ * linear2ulaw() accepts a 14-bit signed integer and encodes it as u-law data
+ * stored in a unsigned char.  This function should only be called with
+ * the data shifted such that it only contains information in the lower
+ * 14-bits.
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ *        Biased Linear Input Code        Compressed Code
+ *        ------------------------        ---------------
+ *        00000001wxyza                        000wxyz
+ *        0000001wxyzab                        001wxyz
+ *        000001wxyzabc                        010wxyz
+ *        00001wxyzabcd                        011wxyz
+ *        0001wxyzabcde                        100wxyz
+ *        001wxyzabcdef                        101wxyz
+ *        01wxyzabcdefg                        110wxyz
+ *        1wxyzabcdefgh                        111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz.  * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char st_14linear2ulaw(
+        int16_t                pcm_val)        /* 2's complement (14-bit range) */
+{
+        int16_t                mask;
+        int16_t                seg;
+        unsigned char        uval;
+
+        /* Have calling software do it since its already doing a shift
+         * from 32-bits down to 16-bits.
+         */
+        /* pcm_val = pcm_val >> 2; */
+
+        /* u-law inverts all bits */
+        /* Get the sign and the magnitude of the value. */
+        if (pcm_val < 0) {
+                pcm_val = -pcm_val;
+                mask = 0x7F;
+        } else {
+                mask = 0xFF;
+        }
+        if ( pcm_val > CLIP ) pcm_val = CLIP;                /* clip the magnitude */
+        pcm_val += (BIAS >> 2);
+
+        /* Convert the scaled magnitude to segment number. */
+        seg = search(pcm_val, seg_uend, 8);
+
+        /*
+         * Combine the sign, segment, quantization bits;
+         * and complement the code word.
+         */
+        if (seg >= 8)                /* out of range, return maximum value. */
+                return (unsigned char) (0x7F ^ mask);
+        else {
+                uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+                return (uval ^ mask);
+        }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+int16_t st_ulaw2linear16(
+        unsigned char        u_val)
+{
+        int16_t                t;
+
+        /* Complement to obtain normal u-law value. */
+        u_val = ~u_val;
+
+        /*
+         * Extract and bias the quantization bits. Then
+         * shift up by the segment number and subtract out the bias.
+         */
+        t = ((u_val & QUANT_MASK) << 3) + BIAS;
+        t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+        return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+#endif /* !FAST_ULAW_CONVERSION */
+
+#ifdef FAST_ALAW_CONVERSION
+
+int16_t _st_alaw2linear16[256] = {
+     -5504,   -5248,   -6016,   -5760,   -4480,   -4224,   -4992,
+     -4736,   -7552,   -7296,   -8064,   -7808,   -6528,   -6272,
+     -7040,   -6784,   -2752,   -2624,   -3008,   -2880,   -2240,
+     -2112,   -2496,   -2368,   -3776,   -3648,   -4032,   -3904,
+     -3264,   -3136,   -3520,   -3392,  -22016,  -20992,  -24064,
+    -23040,  -17920,  -16896,  -19968,  -18944,  -30208,  -29184,
+    -32256,  -31232,  -26112,  -25088,  -28160,  -27136,  -11008,
+    -10496,  -12032,  -11520,   -8960,   -8448,   -9984,   -9472,
+    -15104,  -14592,  -16128,  -15616,  -13056,  -12544,  -14080,
+    -13568,    -344,    -328,    -376,    -360,    -280,    -264,
+      -312,    -296,    -472,    -456,    -504,    -488,    -408,
+      -392,    -440,    -424,     -88,     -72,    -120,    -104,
+       -24,      -8,     -56,     -40,    -216,    -200,    -248,
+      -232,    -152,    -136,    -184,    -168,   -1376,   -1312,
+     -1504,   -1440,   -1120,   -1056,   -1248,   -1184,   -1888,
+     -1824,   -2016,   -1952,   -1632,   -1568,   -1760,   -1696,
+      -688,    -656,    -752,    -720,    -560,    -528,    -624,
+      -592,    -944,    -912,   -1008,    -976,    -816,    -784,
+      -880,    -848,    5504,    5248,    6016,    5760,    4480,
+      4224,    4992,    4736,    7552,    7296,    8064,    7808,
+      6528,    6272,    7040,    6784,    2752,    2624,    3008,
+      2880,    2240,    2112,    2496,    2368,    3776,    3648,
+      4032,    3904,    3264,    3136,    3520,    3392,   22016,
+     20992,   24064,   23040,   17920,   16896,   19968,   18944,
+     30208,   29184,   32256,   31232,   26112,   25088,   28160,
+     27136,   11008,   10496,   12032,   11520,    8960,    8448,
+      9984,    9472,   15104,   14592,   16128,   15616,   13056,
+     12544,   14080,   13568,     344,     328,     376,     360,
+       280,     264,     312,     296,     472,     456,     504,
+       488,     408,     392,     440,     424,      88,      72,
+       120,     104,      24,       8,      56,      40,     216,
+       200,     248,     232,     152,     136,     184,     168,
+      1376,    1312,    1504,    1440,    1120,    1056,    1248,
+      1184,    1888,    1824,    2016,    1952,    1632,    1568,
+      1760,    1696,     688,     656,     752,     720,     560,
+       528,     624,     592,     944,     912,    1008,     976,
+       816,     784,     880,     848
+};
+
+uint8_t _st_13linear2alaw[0x2000] = {
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b,
+   0x6b, 0x6b, 0x6b, 0x6b, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
+   0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6e, 0x6e, 0x6e, 0x6e,
+   0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
+   0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d,
+   0x6d, 0x6d, 0x6d, 0x6d, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,
+   0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x60, 0x60, 0x60, 0x60,
+   0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+   0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67,
+   0x67, 0x67, 0x67, 0x67, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
+   0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x7a, 0x7a, 0x7a, 0x7a,
+   0x7b, 0x7b, 0x7b, 0x7b, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
+   0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7c, 0x7c, 0x7c, 0x7c,
+   0x7d, 0x7d, 0x7d, 0x7d, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73,
+   0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, 0x76, 0x76, 0x76, 0x76,
+   0x77, 0x77, 0x77, 0x77, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75,
+   0x4a, 0x4a, 0x4b, 0x4b, 0x48, 0x48, 0x49, 0x49, 0x4e, 0x4e, 0x4f, 0x4f,
+   0x4c, 0x4c, 0x4d, 0x4d, 0x42, 0x42, 0x43, 0x43, 0x40, 0x40, 0x41, 0x41,
+   0x46, 0x46, 0x47, 0x47, 0x44, 0x44, 0x45, 0x45, 0x5a, 0x5a, 0x5b, 0x5b,
+   0x58, 0x58, 0x59, 0x59, 0x5e, 0x5e, 0x5f, 0x5f, 0x5c, 0x5c, 0x5d, 0x5d,
+   0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, 0x56, 0x56, 0x57, 0x57,
+   0x54, 0x54, 0x55, 0x55, 0xd5, 0xd5, 0xd4, 0xd4, 0xd7, 0xd7, 0xd6, 0xd6,
+   0xd1, 0xd1, 0xd0, 0xd0, 0xd3, 0xd3, 0xd2, 0xd2, 0xdd, 0xdd, 0xdc, 0xdc,
+   0xdf, 0xdf, 0xde, 0xde, 0xd9, 0xd9, 0xd8, 0xd8, 0xdb, 0xdb, 0xda, 0xda,
+   0xc5, 0xc5, 0xc4, 0xc4, 0xc7, 0xc7, 0xc6, 0xc6, 0xc1, 0xc1, 0xc0, 0xc0,
+   0xc3, 0xc3, 0xc2, 0xc2, 0xcd, 0xcd, 0xcc, 0xcc, 0xcf, 0xcf, 0xce, 0xce,
+   0xc9, 0xc9, 0xc8, 0xc8, 0xcb, 0xcb, 0xca, 0xca, 0xf5, 0xf5, 0xf5, 0xf5,
+   0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6,
+   0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf3, 0xf3, 0xf3, 0xf3,
+   0xf2, 0xf2, 0xf2, 0xf2, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc,
+   0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xf9, 0xf9, 0xf9, 0xf9,
+   0xf8, 0xf8, 0xf8, 0xf8, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa,
+   0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4,
+   0xe4, 0xe4, 0xe4, 0xe4, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+   0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1,
+   0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+   0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2,
+   0xe2, 0xe2, 0xe2, 0xe2, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+   0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef,
+   0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+   0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8,
+   0xe8, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+   0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+#endif /* FAST_ALAW_CONVERSION */
+
+#ifdef FAST_ULAW_CONVERSION
+
+int16_t _st_ulaw2linear16[256] = {
+    -32124,  -31100,  -30076,  -29052,  -28028,  -27004,  -25980,
+    -24956,  -23932,  -22908,  -21884,  -20860,  -19836,  -18812,
+    -17788,  -16764,  -15996,  -15484,  -14972,  -14460,  -13948,
+    -13436,  -12924,  -12412,  -11900,  -11388,  -10876,  -10364,
+     -9852,   -9340,   -8828,   -8316,   -7932,   -7676,   -7420,
+     -7164,   -6908,   -6652,   -6396,   -6140,   -5884,   -5628,
+     -5372,   -5116,   -4860,   -4604,   -4348,   -4092,   -3900,
+     -3772,   -3644,   -3516,   -3388,   -3260,   -3132,   -3004,
+     -2876,   -2748,   -2620,   -2492,   -2364,   -2236,   -2108,
+     -1980,   -1884,   -1820,   -1756,   -1692,   -1628,   -1564,
+     -1500,   -1436,   -1372,   -1308,   -1244,   -1180,   -1116,
+     -1052,    -988,    -924,    -876,    -844,    -812,    -780,
+      -748,    -716,    -684,    -652,    -620,    -588,    -556,
+      -524,    -492,    -460,    -428,    -396,    -372,    -356,
+      -340,    -324,    -308,    -292,    -276,    -260,    -244,
+      -228,    -212,    -196,    -180,    -164,    -148,    -132,
+      -120,    -112,    -104,     -96,     -88,     -80,     -72,
+       -64,     -56,     -48,     -40,     -32,     -24,     -16,
+        -8,       0,   32124,   31100,   30076,   29052,   28028,
+     27004,   25980,   24956,   23932,   22908,   21884,   20860,
+     19836,   18812,   17788,   16764,   15996,   15484,   14972,
+     14460,   13948,   13436,   12924,   12412,   11900,   11388,
+     10876,   10364,    9852,    9340,    8828,    8316,    7932,
+      7676,    7420,    7164,    6908,    6652,    6396,    6140,
+      5884,    5628,    5372,    5116,    4860,    4604,    4348,
+      4092,    3900,    3772,    3644,    3516,    3388,    3260,
+      3132,    3004,    2876,    2748,    2620,    2492,    2364,
+      2236,    2108,    1980,    1884,    1820,    1756,    1692,
+      1628,    1564,    1500,    1436,    1372,    1308,    1244,
+      1180,    1116,    1052,     988,     924,     876,     844,
+       812,     780,     748,     716,     684,     652,     620,
+       588,     556,     524,     492,     460,     428,     396,
+       372,     356,     340,     324,     308,     292,     276,
+       260,     244,     228,     212,     196,     180,     164,
+       148,     132,     120,     112,     104,      96,      88,
+        80,      72,      64,      56,      48,      40,      32,
+        24,      16,       8,       0
+};
+
+uint8_t _st_14linear2ulaw[0x4000] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+   0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+   0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+   0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+   0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+   0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+   0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+   0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+   0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+   0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+   0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+   0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+   0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+   0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+   0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+   0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
+   0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+   0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
+   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+   0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
+   0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+   0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+   0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+   0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+   0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+   0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+   0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+   0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+   0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+   0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+   0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+   0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+   0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
+   0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+   0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+   0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+   0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+   0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+   0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
+   0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+   0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+   0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
+   0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
+   0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+   0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+   0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
+   0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+   0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
+   0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+   0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
+   0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+   0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40,
+   0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+   0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+   0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+   0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43,
+   0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
+   0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+   0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
+   0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46,
+   0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
+   0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
+   0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
+   0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49,
+   0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+   0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+   0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b,
+   0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+   0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+   0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d,
+   0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e,
+   0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f,
+   0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
+   0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51,
+   0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52,
+   0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54,
+   0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57,
+   0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+   0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a,
+   0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b,
+   0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d,
+   0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
+   0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
+   0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63,
+   0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66,
+   0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69,
+   0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c,
+   0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
+   0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
+   0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+   0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0xff, 0xfe, 0xfe, 0xfd,
+   0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7,
+   0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1,
+   0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed,
+   0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea,
+   0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7,
+   0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4,
+   0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1,
+   0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+   0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd,
+   0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+   0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xda,
+   0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+   0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7,
+   0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+   0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4,
+   0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
+   0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1,
+   0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+   0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+   0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce,
+   0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd,
+   0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+   0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+   0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+   0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca,
+   0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca,
+   0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9,
+   0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8,
+   0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7,
+   0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+   0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+   0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+   0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4,
+   0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+   0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+   0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
+   0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1,
+   0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+   0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+   0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+   0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+   0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+   0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+   0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+   0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
+   0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,
+   0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+   0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+   0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,
+   0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+   0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
+   0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+   0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+   0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
+   0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
+   0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+   0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
+   0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+   0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
+   0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+   0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
+   0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+   0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
+   0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
+   0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+   0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+   0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
+   0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,
+   0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+   0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
+   0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+   0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+   0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
+   0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+   0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+   0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+   0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+   0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+   0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+   0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+   0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+   0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+   0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+   0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+   0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+   0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+   0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
+   0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+   0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
+   0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+   0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
+   0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
+   0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+   0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+   0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+   0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+   0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+   0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+   0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+   0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+   0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80
+};
+
+#endif /* FAST_ULAW_CONVERSION */
+
+/* The following code was used to generate the lookup tables */
+#if 0
+int main()
+{
+    int x, y, find2a = 0;
+
+    y = 0;
+    printf("int16_t _st_alaw2linear16[256] = {\n  ");
+    for (x = 0; x < 256; x++)
+    {
+        printf("%8d,", st_alaw2linear16(x));
+        y++;
+        if (y == 7)
+        {
+            y = 0;
+            printf("\n  ");
+        }
+    }
+
+    printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n  ");
+    y = 0;
+    for (x = 0; x < 0x2000; x++)
+    {
+        printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x));
+        y++;
+        if (y == 12)
+        {
+            y = 0;
+            printf("\n  ");
+        }
+    }
+
+    printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n  ");
+    y = 0;
+    for (x = 0; x < 256; x++)
+    {
+        printf("%8d,", st_ulaw2linear16(x));
+        y++;
+        if (y == 7)
+        {
+            y = 0;
+            printf("\n  ");
+        }
+    }
+
+    printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n  ");
+    y = 0;
+    for (x = 0; x < 0x4000; x++)
+    {
+        printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x));
+        y++;
+        if (y == 12)
+        {
+            y = 0;
+            printf("\n  ");
+        }
+    }
+    printf("\n};\n");
+
+}
+#endif
+
+/* The following is not used by SoX but kept for reference */
+#if 0
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = {                        /* u- to A-law conversions */
+        1,        1,        2,        2,        3,        3,        4,        4,
+        5,        5,        6,        6,        7,        7,        8,        8,
+        9,        10,        11,        12,        13,        14,        15,        16,
+        17,        18,        19,        20,        21,        22,        23,        24,
+        25,        27,        29,        31,        33,        34,        35,        36,
+        37,        38,        39,        40,        41,        42,        43,        44,
+        46,        48,        49,        50,        51,        52,        53,        54,
+        55,        56,        57,        58,        59,        60,        61,        62,
+        64,        65,        66,        67,        68,        69,        70,        71,
+        72,        73,        74,        75,        76,        77,        78,        79,
+/* corrected:
+        81,        82,        83,        84,        85,        86,        87,        88,
+   should be: */
+        80,        82,        83,        84,        85,        86,        87,        88,
+        89,        90,        91,        92,        93,        94,        95,        96,
+        97,        98,        99,        100,        101,        102,        103,        104,
+        105,        106,        107,        108,        109,        110,        111,        112,
+        113,        114,        115,        116,        117,        118,        119,        120,
+        121,        122,        123,        124,        125,        126,        127,        128};
+
+unsigned char _a2u[128] = {                        /* A- to u-law conversions */
+        1,        3,        5,        7,        9,        11,        13,        15,
+        16,        17,        18,        19,        20,        21,        22,        23,
+        24,        25,        26,        27,        28,        29,        30,        31,
+        32,        32,        33,        33,        34,        34,        35,        35,
+        36,        37,        38,        39,        40,        41,        42,        43,
+        44,        45,        46,        47,        48,        48,        49,        49,
+        50,        51,        52,        53,        54,        55,        56,        57,
+        58,        59,        60,        61,        62,        63,        64,        64,
+        65,        66,        67,        68,        69,        70,        71,        72,
+/* corrected:
+        73,        74,        75,        76,        77,        78,        79,        79,
+   should be: */
+        73,        74,        75,        76,        77,        78,        79,        80,
+
+        80,        81,        82,        83,        84,        85,        86,        87,
+        88,        89,        90,        91,        92,        93,        94,        95,
+        96,        97,        98,        99,        100,        101,        102,        103,
+        104,        105,        106,        107,        108,        109,        110,        111,
+        112,        113,        114,        115,        116,        117,        118,        119,
+        120,        121,        122,        123,        124,        125,        126,        127};
+
+/* A-law to u-law conversion */
+unsigned char st_alaw2ulaw(
+        unsigned char        aval)
+{
+        aval &= 0xff;
+        return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+            (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char st_ulaw2alaw(
+        unsigned char        uval)
+{
+        uval &= 0xff;
+        return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+            (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+#endif
similarity index 92%
rename from polyp/g711.h
rename to src/pulsecore/g711.h
index 97cedf814b8800dcedc106cc32d199109d58db5a..37ebcf72a8f2676f39b9b35b02ae66173b523438 100644 (file)
@@ -13,7 +13,7 @@
 ** implied warranty.
 */
 
-/** Copied from sox -- Lennart Poettring*/
+/** Copied from sox -- Lennart Poettering */
 
 #include <inttypes.h>
 
@@ -33,7 +33,7 @@ extern int16_t _st_ulaw2linear16[256];
 #define st_14linear2ulaw(sw) (_st_14linear2ulaw[(sw + 0x2000)])
 #define st_ulaw2linear16(uc) (_st_ulaw2linear16[uc])
 #else
-unsigned char st_14linear2ulaw(int16_t pcm_val); 
+unsigned char st_14linear2ulaw(int16_t pcm_val);
 int16_t st_ulaw2linear16(unsigned char);
 #endif
 
similarity index 53%
rename from polyp/hashmap.c
rename to src/pulsecore/hashmap.c
index 3b1265c95341c95fb4cfa8e245ea48c79e402d40..b7f4109bc2f871c895c947cb648c3b34f960fc9b 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #endif
 
 #include <stdlib.h>
-#include <assert.h>
 #include <string.h>
 
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/idxset.h>
+#include <pulsecore/log.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/macro.h>
+
 #include "hashmap.h"
-#include "idxset.h"
-#include "xmalloc.h"
-#include "log.h"
 
-#define BUCKETS 1023
+#define BUCKETS 127
 
 struct hashmap_entry {
     struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous;
@@ -45,25 +48,30 @@ struct pa_hashmap {
     unsigned size;
     struct hashmap_entry **data;
     struct hashmap_entry *first_entry;
-    
+
     unsigned n_entries;
-    unsigned (*hash_func) (const void *p);
-    int (*compare_func) (const void*a, const void*b);
+    pa_hash_func_t hash_func;
+    pa_compare_func_t compare_func;
 };
 
-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 = pa_xmalloc(sizeof(struct pa_hashmap));
-    h->data = pa_xmalloc0(sizeof(struct hashmap_entry*)*(h->size = BUCKETS));
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
+
+pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
+    pa_hashmap *h;
+
+    h = pa_xnew(pa_hashmap, 1);
+    h->data = pa_xnew0(struct hashmap_entry*, h->size = BUCKETS);
     h->first_entry = NULL;
     h->n_entries = 0;
     h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
     h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
+
     return h;
 }
 
-static void remove(struct pa_hashmap *h, struct hashmap_entry *e) {
-    assert(e);
+static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) {
+    pa_assert(h);
+    pa_assert(e);
 
     if (e->next)
         e->next->previous = e->previous;
@@ -77,30 +85,33 @@ static void remove(struct pa_hashmap *h, struct hashmap_entry *e) {
     if (e->bucket_previous)
         e->bucket_previous->bucket_next = e->bucket_next;
     else {
-        assert(e->hash < h->size);
+        pa_assert(e->hash < h->size);
         h->data[e->hash] = e->bucket_next;
     }
 
-    pa_xfree(e);
+    if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+        pa_xfree(e);
+
     h->n_entries--;
 }
 
-void pa_hashmap_free(struct pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) {
-    assert(h);
+void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) {
+    pa_assert(h);
 
     while (h->first_entry) {
         if (free_func)
             free_func(h->first_entry->value, userdata);
-        remove(h, h->first_entry);
+        remove_entry(h, h->first_entry);
     }
-    
+
     pa_xfree(h->data);
     pa_xfree(h);
 }
 
-static struct hashmap_entry *get(struct pa_hashmap *h, unsigned hash, const void *key) {
+static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) {
     struct hashmap_entry *e;
-    assert(h && hash < h->size);
+    pa_assert(h);
+    pa_assert(hash < h->size);
 
     for (e = h->data[hash]; e; e = e->bucket_next)
         if (h->compare_func(e->key, key) == 0)
@@ -109,41 +120,44 @@ static struct hashmap_entry *get(struct pa_hashmap *h, unsigned hash, const void
     return NULL;
 }
 
-int pa_hashmap_put(struct pa_hashmap *h, const void *key, void *value) {
+int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) {
     struct hashmap_entry *e;
     unsigned hash;
-    assert(h);
+    pa_assert(h);
 
     hash = h->hash_func(key) % h->size;
 
     if ((e = get(h, hash, key)))
         return -1;
-    
-    e = pa_xmalloc(sizeof(struct hashmap_entry));
+
+    if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
+        e = pa_xnew(struct hashmap_entry, 1);
+
     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) {
+void* pa_hashmap_get(pa_hashmap *h, const void *key) {
     unsigned hash;
     struct hashmap_entry *e;
-    assert(h && key);
+
+    pa_assert(h);
 
     hash = h->hash_func(key) % h->size;
 
@@ -153,11 +167,12 @@ void* pa_hashmap_get(struct pa_hashmap *h, const void *key) {
     return e->value;
 }
 
-void* pa_hashmap_remove(struct pa_hashmap *h, const void *key) {
+void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
     struct hashmap_entry *e;
     unsigned hash;
     void *data;
-    assert(h && key);
+
+    pa_assert(h);
 
     hash = h->hash_func(key) % h->size;
 
@@ -165,30 +180,65 @@ void* pa_hashmap_remove(struct pa_hashmap *h, const void *key) {
         return NULL;
 
     data = e->value;
-    remove(h, e);
+    remove_entry(h, e);
     return data;
 }
 
-unsigned pa_hashmap_ncontents(struct pa_hashmap *h) {
+unsigned pa_hashmap_size(pa_hashmap *h) {
     return h->n_entries;
 }
 
-void *pa_hashmap_iterate(struct pa_hashmap *h, void **state, const void **key) {
-    assert(h && state);
+void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
+    struct hashmap_entry *e;
+
+    pa_assert(h);
+    pa_assert(state);
+
+    if (*state == (void*) -1)
+        goto at_end;
+
+    if ((!*state && !h->first_entry))
+        goto at_end;
 
-    if (!*state) 
-        *state = h->first_entry;
+    e = *state ? *state : h->first_entry;
+
+    if (e->next)
+        *state = e->next;
     else
-        *state = ((struct hashmap_entry*) *state)->next;
+        *state = (void*) -1;
 
-    if (!*state) {
-        if (key)
-            *key = NULL;
-        return NULL;
-    }
+    if (key)
+        *key = e->key;
+
+    return e->value;
+
+at_end:
+    *state = (void *) -1;
 
     if (key)
-        *key = ((struct hashmap_entry*) *state)->key;
-    
-    return ((struct hashmap_entry*) *state)->value;
+        *key = NULL;
+
+    return NULL;
+}
+
+void* pa_hashmap_steal_first(pa_hashmap *h) {
+    void *data;
+
+    pa_assert(h);
+
+    if (!h->first_entry)
+        return NULL;
+
+    data = h->first_entry->value;
+    remove_entry(h, h->first_entry);
+    return data;
+}
+
+void *pa_hashmap_get_first(pa_hashmap *h) {
+    pa_assert(h);
+
+    if (!h->first_entry)
+        return NULL;
+
+    return h->first_entry->value;
 }
similarity index 51%
rename from polyp/hashmap.h
rename to src/pulsecore/hashmap.h
index d55834c1ae8b3561530bf67823579d794b68d1a8..a4505c4c8b2c5f354e2426e5a3d337956e6c25e6 100644 (file)
@@ -1,53 +1,62 @@
 #ifndef foohashmaphfoo
 #define foohashmaphfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
+#include <pulsecore/idxset.h>
+
 /* Simple Implementation of a hash table. Memory management is the
  * user's job. It's a good idea to have the key pointer point to a
  * string in the value data. */
 
-struct pa_hashmap;
+typedef struct pa_hashmap pa_hashmap;
+
+typedef void (*pa_free2_cb_t)(void *p, void *userdata);
 
 /* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */
-struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
+pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func);
 
 /* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */
-void pa_hashmap_free(struct pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata);
+void pa_hashmap_free(pa_hashmap*, pa_free2_cb_t free_cb, void *userdata);
 
 /* Returns non-zero when the entry already exists */
-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_put(pa_hashmap *h, const void *key, void *value);
+void* pa_hashmap_get(pa_hashmap *h, const void *key);
 
 /* Returns the data of the entry while removing */
-void* pa_hashmap_remove(struct pa_hashmap *h, const void *key);
+void* pa_hashmap_remove(pa_hashmap *h, const void *key);
 
-unsigned pa_hashmap_ncontents(struct pa_hashmap *h);
+unsigned pa_hashmap_size(pa_hashmap *h);
 
 /* May be used to iterate through the hashmap. Initially the opaque
    pointer *state has to be set to NULL. The hashmap may not be
-   modified during iteration. The key of the entry is returned in
-   *key, if key is non-NULL. After the last entry in the hashmap NULL
-   is returned. */
-void *pa_hashmap_iterate(struct pa_hashmap *h, void **state, const void**key);
+   modified during iteration -- except for deleting the current entry
+   via pa_hashmap_remove(). The key of the entry is returned in *key,
+   if key is non-NULL. After the last entry in the hashmap NULL is
+   returned. */
+void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
+
+void *pa_hashmap_steal_first(pa_hashmap *h);
+
+void *pa_hashmap_get_first(pa_hashmap *h);
 
 #endif
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
new file mode 100644 (file)
index 0000000..0aac475
--- /dev/null
@@ -0,0 +1,123 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulsecore/macro.h>
+
+#include "hook-list.h"
+
+void pa_hook_init(pa_hook *hook, void *data) {
+    pa_assert(hook);
+
+    PA_LLIST_HEAD_INIT(pa_hook_slot, hook->slots);
+    hook->n_dead = hook->n_firing = 0;
+    hook->data = data;
+}
+
+static void slot_free(pa_hook *hook, pa_hook_slot *slot) {
+    pa_assert(hook);
+    pa_assert(slot);
+
+    PA_LLIST_REMOVE(pa_hook_slot, hook->slots, slot);
+
+    pa_xfree(slot);
+}
+
+void pa_hook_free(pa_hook *hook) {
+    pa_assert(hook);
+    pa_assert(hook->n_firing == 0);
+
+    while (hook->slots)
+        slot_free(hook, hook->slots);
+
+    pa_hook_init(hook, NULL);
+}
+
+pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) {
+    pa_hook_slot *slot, *where, *prev;
+
+    pa_assert(cb);
+
+    slot = pa_xnew(pa_hook_slot, 1);
+    slot->hook = hook;
+    slot->dead = FALSE;
+    slot->callback = cb;
+    slot->data = data;
+    slot->priority = prio;
+
+    prev = NULL;
+    for (where = hook->slots; where; where = where->next) {
+        if (prio < where->priority)
+            break;
+        prev = where;
+    }
+
+    PA_LLIST_INSERT_AFTER(pa_hook_slot, hook->slots, prev, slot);
+
+    return slot;
+}
+
+void pa_hook_slot_free(pa_hook_slot *slot) {
+    pa_assert(slot);
+    pa_assert(!slot->dead);
+
+    if (slot->hook->n_firing > 0) {
+        slot->dead = TRUE;
+        slot->hook->n_dead++;
+    } else
+        slot_free(slot->hook, slot);
+}
+
+pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
+    pa_hook_slot *slot, *next;
+    pa_hook_result_t result = PA_HOOK_OK;
+
+    pa_assert(hook);
+
+    hook->n_firing ++;
+
+    for (slot = hook->slots; slot; slot = slot->next) {
+        if (slot->dead)
+            continue;
+
+        if ((result = slot->callback(hook->data, data, slot->data)) != PA_HOOK_OK)
+            break;
+    }
+
+    hook->n_firing --;
+    pa_assert(hook->n_firing >= 0);
+
+    for (slot = hook->slots; hook->n_dead > 0 && slot; slot = next) {
+        next = slot->next;
+
+        if (slot->dead) {
+            slot_free(hook, slot);
+            hook->n_dead--;
+        }
+    }
+
+    pa_assert(hook->n_dead == 0);
+
+    return result;
+}
diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h
new file mode 100644 (file)
index 0000000..cf85aca
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef foohooklistfoo
+#define foohooklistfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/llist.h>
+
+typedef struct pa_hook_slot pa_hook_slot;
+typedef struct pa_hook pa_hook;
+
+typedef enum pa_hook_result {
+    PA_HOOK_OK = 0,
+    PA_HOOK_STOP = 1,
+    PA_HOOK_CANCEL = -1
+} pa_hook_result_t;
+
+typedef enum pa_hook_priority {
+    PA_HOOK_EARLY = -100,
+    PA_HOOK_NORMAL = 0,
+    PA_HOOK_LATE = 100
+} pa_hook_priority_t;
+
+typedef pa_hook_result_t (*pa_hook_cb_t)(
+        void *hook_data,
+        void *call_data,
+        void *slot_data);
+
+struct pa_hook_slot {
+    pa_bool_t dead;
+    pa_hook *hook;
+    pa_hook_priority_t priority;
+    pa_hook_cb_t callback;
+    void *data;
+    PA_LLIST_FIELDS(pa_hook_slot);
+};
+
+struct pa_hook {
+    PA_LLIST_HEAD(pa_hook_slot, slots);
+    int n_firing, n_dead;
+
+    void *data;
+};
+
+void pa_hook_init(pa_hook *hook, void *data);
+void pa_hook_free(pa_hook *hook);
+
+pa_hook_slot* pa_hook_connect(pa_hook *hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data);
+void pa_hook_slot_free(pa_hook_slot *slot);
+
+pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data);
+
+#endif
similarity index 59%
rename from polyp/idxset.c
rename to src/pulsecore/idxset.c
index dcc38423e3c6676bb1f86854970cc0903da8d1ce..7c9520a4be28066bb783198bdef0a4ff7bb0ec66 100644 (file)
@@ -1,20 +1,21 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #endif
 
 #include <stdio.h>
-#include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+
 #include "idxset.h"
-#include "xmalloc.h"
 
 struct idxset_entry {
     void *data;
@@ -41,18 +44,20 @@ struct idxset_entry {
 };
 
 struct pa_idxset {
-    unsigned (*hash_func) (const void *p);
-    int (*compare_func)(const void *a, const void *b);
-    
+    pa_hash_func_t hash_func;
+    pa_compare_func_t compare_func;
+
     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;
 };
 
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
+
 unsigned pa_idxset_string_hash_func(const void *p) {
     unsigned hash = 0;
     const char *c;
-    
+
     for (c = p; *c; c++)
         hash = 31 * hash + *c;
 
@@ -64,21 +69,21 @@ int pa_idxset_string_compare_func(const void *a, const void *b) {
 }
 
 unsigned pa_idxset_trivial_hash_func(const void *p) {
-    return (unsigned) p;
+    return PA_PTR_TO_UINT(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;
+pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
+    pa_idxset *s;
 
-    s = pa_xmalloc(sizeof(struct pa_idxset));
+    s = pa_xnew(pa_idxset, 1);
     s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
     s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
-    s->hash_table_size = 1023;
-    s->hash_table = pa_xmalloc0(sizeof(struct idxset_entry*)*s->hash_table_size);
+    s->hash_table_size = 127;
+    s->hash_table = pa_xnew0(struct idxset_entry*, s->hash_table_size);
     s->array = NULL;
     s->array_size = 0;
     s->index = 0;
@@ -90,16 +95,18 @@ struct pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*com
     return s;
 }
 
-void pa_idxset_free(struct pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) {
-    assert(s);
+void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) {
+    pa_assert(s);
 
     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);
-        pa_xfree(e);
+
+        if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+            pa_xfree(e);
     }
 
     pa_xfree(s->hash_table);
@@ -107,10 +114,10 @@ void pa_idxset_free(struct pa_idxset *s, void (*free_func) (void *p, void *userd
     pa_xfree(s);
 }
 
-static struct idxset_entry* hash_scan(struct pa_idxset *s, struct idxset_entry* e, const void *p) {
-    assert(p);
+static struct idxset_entry* hash_scan(pa_idxset *s, struct idxset_entry* e, const void *p) {
+    pa_assert(p);
 
-    assert(s->compare_func);
+    pa_assert(s->compare_func);
     for (; e; e = e->hash_next)
         if (s->compare_func(e->data, p) == 0)
             return e;
@@ -118,58 +125,65 @@ static struct idxset_entry* hash_scan(struct pa_idxset *s, struct idxset_entry*
     return NULL;
 }
 
-static void extend_array(struct pa_idxset *s, uint32_t index) {
+static void extend_array(pa_idxset *s, uint32_t idx) {
     uint32_t i, j, l;
     struct idxset_entry** n;
-    assert(index >= s->start_index);
 
-    if (index < s->start_index + s->array_size)
+    pa_assert(s);
+    pa_assert(idx >= s->start_index);
+
+    if (idx < s->start_index + s->array_size)
         return;
 
     for (i = 0; i < s->array_size; i++)
         if (s->array[i])
             break;
 
-    l = index - s->start_index - i + 100;
-    n = pa_xmalloc0(sizeof(struct hash_table_entry*)*l);
-    
+    l = idx - s->start_index - i + 100;
+    n = pa_xnew0(struct idxset_entry*, l);
+
     for (j = 0; j < s->array_size-i; j++)
         n[j] = s->array[i+j];
 
     pa_xfree(s->array);
-    
+
     s->array = n;
     s->array_size = l;
     s->start_index += i;
 }
 
-static struct idxset_entry** array_index(struct pa_idxset*s, uint32_t index) {
-    if (index >= s->start_index + s->array_size)
+static struct idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
+    pa_assert(s);
+
+    if (idx >= s->start_index + s->array_size)
         return NULL;
-    
-    if (index < s->start_index)
+
+    if (idx < s->start_index)
         return NULL;
-    
-    return s->array + (index - s->start_index);
+
+    return s->array + idx - s->start_index;
 }
 
-int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index) {
+int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
     unsigned h;
     struct idxset_entry *e, **a;
-    assert(s && p);
 
-    assert(s->hash_func);
+    pa_assert(s);
+    pa_assert(p);
+
+    pa_assert(s->hash_func);
     h = s->hash_func(p) % s->hash_table_size;
 
-    assert(s->hash_table);
+    pa_assert(s->hash_table);
     if ((e = hash_scan(s, s->hash_table[h], p))) {
-        if (index)
-            *index = e->index;
-        
+        if (idx)
+            *idx = e->index;
+
         return -1;
     }
 
-    e = pa_xmalloc(sizeof(struct idxset_entry));
+    if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
+        e = pa_xnew(struct idxset_entry, 1);
     e->data = p;
     e->index = s->index++;
     e->hash_value = h;
@@ -184,35 +198,35 @@ int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index) {
     /* Insert into array */
     extend_array(s, e->index);
     a = array_index(s, e->index);
-    assert(a && !*a);
+    pa_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);
+        pa_assert(s->iterate_list_head);
         s->iterate_list_tail->iterate_next = e;
     } else {
-        assert(!s->iterate_list_head);
+        pa_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;
+    pa_assert(s->n_entries >= 1);
+
+    if (idx)
+        *idx = e->index;
 
     return 0;
 }
 
-void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index) {
+void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
     struct idxset_entry **a;
-    assert(s);
-    
-    if (!(a = array_index(s, index)))
+    pa_assert(s);
+
+    if (!(a = array_index(s, idx)))
         return NULL;
 
     if (!*a)
@@ -221,39 +235,43 @@ void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index) {
     return (*a)->data;
 }
 
-void* pa_idxset_get_by_data(struct pa_idxset*s, const void *p, uint32_t *index) {
+void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
     unsigned h;
     struct idxset_entry *e;
-    assert(s && p);
-    
-    assert(s->hash_func);
+
+    pa_assert(s);
+    pa_assert(p);
+
+    pa_assert(s->hash_func);
     h = s->hash_func(p) % s->hash_table_size;
 
-    assert(s->hash_table);
+    pa_assert(s->hash_table);
     if (!(e = hash_scan(s, s->hash_table[h], p)))
         return NULL;
 
-    if (index)
-        *index = e->index;
+    if (idx)
+        *idx = e->index;
 
     return e->data;
 }
 
-static void remove_entry(struct pa_idxset *s, struct idxset_entry *e) {
+static void remove_entry(pa_idxset *s, struct idxset_entry *e) {
     struct idxset_entry **a;
-    assert(s && e);
+
+    pa_assert(s);
+    pa_assert(e);
 
     /* Remove from array */
     a = array_index(s, e->index);
-    assert(a && *a && *a == e);
+    pa_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
@@ -268,53 +286,61 @@ static void remove_entry(struct pa_idxset *s, struct idxset_entry *e) {
     else
         s->hash_table[e->hash_value] = e->hash_next;
 
-    pa_xfree(e);
+    if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+        pa_xfree(e);
 
-    assert(s->n_entries >= 1);
+    pa_assert(s->n_entries >= 1);
     s->n_entries--;
 }
 
-void* pa_idxset_remove_by_index(struct pa_idxset*s, uint32_t index) {
+void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
     struct idxset_entry **a;
     void *data;
-    
-    assert(s);
 
-    if (!(a = array_index(s, index)))
+    pa_assert(s);
+
+    if (!(a = array_index(s, idx)))
+        return NULL;
+
+    if (!*a)
         return NULL;
 
     data = (*a)->data;
     remove_entry(s, *a);
-    
-    return data; 
+
+    return data;
 }
 
-void* pa_idxset_remove_by_data(struct pa_idxset*s, const void *data, uint32_t *index) {
+void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
     struct idxset_entry *e;
     unsigned h;
     void *r;
-    
-    assert(s->hash_func);
+
+    pa_assert(s);
+
+    pa_assert(s->hash_func);
     h = s->hash_func(data) % s->hash_table_size;
 
-    assert(s->hash_table);
+    pa_assert(s->hash_table);
     if (!(e = hash_scan(s, s->hash_table[h], data)))
         return NULL;
 
     r = e->data;
-    if (index)
-        *index = e->index;
+    if (idx)
+        *idx = e->index;
 
     remove_entry(s, e);
 
     return r;
 }
 
-void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index) {
+void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
     struct idxset_entry **a, *e = NULL;
-    assert(s && index);
 
-    if ((a = array_index(s, *index)) && *a)
+    pa_assert(s);
+    pa_assert(idx);
+
+    if ((a = array_index(s, *idx)) && *a)
         e = (*a)->iterate_next;
 
     if (!e)
@@ -322,42 +348,45 @@ void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index) {
 
     if (!e)
         return NULL;
-    
-    *index = e->index;
+
+    *idx = e->index;
     return e->data;
 }
 
-void* pa_idxset_first(struct pa_idxset *s, uint32_t *index) {
-    assert(s);
+void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
+    pa_assert(s);
 
     if (!s->iterate_list_head)
         return NULL;
 
-    if (index)
-        *index = s->iterate_list_head->index;
+    if (idx)
+        *idx = s->iterate_list_head->index;
     return s->iterate_list_head->data;
 }
 
-void *pa_idxset_next(struct pa_idxset *s, uint32_t *index) {
+void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
     struct idxset_entry **a, *e = NULL;
-    assert(s && index);
 
-    if ((a = array_index(s, *index)) && *a)
+    pa_assert(s);
+    pa_assert(idx);
+
+    if ((a = array_index(s, *idx)) && *a)
         e = (*a)->iterate_next;
-    
+
     if (e) {
-        *index = e->index;
+        *idx = e->index;
         return e->data;
     } else {
-        *index = PA_IDXSET_INVALID;
+        *idx = 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) {
+int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) {
     struct idxset_entry *e;
-    assert(s && func);
+
+    pa_assert(s);
+    pa_assert(func);
 
     e = s->iterate_list_head;
     while (e) {
@@ -374,17 +403,19 @@ int pa_idxset_foreach(struct pa_idxset*s, int (*func)(void *p, uint32_t index, i
 
         e = n;
     }
-    
+
     return 0;
 }
 
-unsigned pa_idxset_ncontents(struct pa_idxset*s) {
-    assert(s);
+unsigned pa_idxset_size(pa_idxset*s) {
+    pa_assert(s);
+
     return s->n_entries;
 }
 
-int pa_idxset_isempty(struct pa_idxset *s) {
-    assert(s);
+int pa_idxset_isempty(pa_idxset *s) {
+    pa_assert(s);
+
     return s->n_entries == 0;
 }
 
similarity index 62%
rename from polyp/idxset.h
rename to src/pulsecore/idxset.h
index 4c89eea19c548000c807cb3e65f85de33701d1f9..0a4e528e599cec7acd444b1f50a9435815079e3f 100644 (file)
@@ -1,23 +1,24 @@
 #ifndef fooidxsethfoo
 #define fooidxsethfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
@@ -41,53 +42,57 @@ 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;
+typedef unsigned (*pa_hash_func_t)(const void *p);
+typedef int (*pa_compare_func_t)(const void *a, const void *b);
+
+typedef struct pa_idxset pa_idxset;
 
 /* Instantiate a new idxset with the specified hash and comparison functions */
-struct pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
+pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func);
 
 /* Free the idxset. When the idxset is not empty the specified function is called for every entry contained */
-void pa_idxset_free(struct pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata);
+void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata);
 
-/* Store a new item in the idxset. The index of the item is returned in *index */
-int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index);
+/* Store a new item in the idxset. The index of the item is returned in *idx */
+int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx);
 
-/* Get the entry by its index */
-void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index);
+/* Get the entry by its idx */
+void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx);
 
-/* Get the entry by its data. The index is returned in *index */
-void* pa_idxset_get_by_data(struct pa_idxset*s, const void *p, uint32_t *index);
+/* Get the entry by its data. The idx is returned in *index */
+void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx);
 
 /* Similar to pa_idxset_get_by_index(), but removes the entry from the idxset. */
-void* pa_idxset_remove_by_index(struct pa_idxset*s, uint32_t index);
+void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx);
 
 /* Similar to pa_idxset_get_by_data(), but removes the entry from the idxset */
-void* pa_idxset_remove_by_data(struct pa_idxset*s, const void *p, uint32_t *index);
+void* pa_idxset_remove_by_data(pa_idxset*s, const void *p, uint32_t *idx);
 
 /* This may be used to iterate through all entries. When called with
    an invalid index value it returns the first entry, otherwise the
-   next following. The function is best called with *index =
+   next following. The function is best called with *idx =
    PA_IDXSET_VALID first. It is safe to manipulate the idxset between
    the calls. It is not guaranteed that all entries have already been
    returned before the an entry is returned the second time.*/
-void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index);
+void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx);
 
-/* Return the oldest entry in the idxset. Fill in its index in *index. */
-void* pa_idxset_first(struct pa_idxset *s, uint32_t *index);
+/* Return the oldest entry in the idxset. Fill in its index in *idx. */
+void* pa_idxset_first(pa_idxset *s, uint32_t *idx);
 
-/* Return the entry following the entry indexed by *index.  After the
+/* Return the entry following the entry indexed by *idx.  After the
  * call *index contains the index of the returned
  * object. pa_idxset_first() and pa_idxset_next() may be used to
  * iterate through the set.*/
-void *pa_idxset_next(struct pa_idxset *s, uint32_t *index);
+void *pa_idxset_next(pa_idxset *s, uint32_t *idx);
 
 /* Call a function for every item in the set. If the callback function
    returns -1, the loop is terminated. If *del is set to non-zero that
    specific item is removed. It is not safe to call any other
    functions on the idxset while pa_idxset_foreach is executed. */
-int pa_idxset_foreach(struct pa_idxset*s, int (*func)(void *p, uint32_t index, int *del, void*userdata), void *userdata);
+int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata);
+
+unsigned pa_idxset_size(pa_idxset*s);
 
-unsigned pa_idxset_ncontents(struct pa_idxset*s);
-int pa_idxset_isempty(struct pa_idxset *s);
+int pa_idxset_isempty(pa_idxset *s);
 
 #endif
diff --git a/src/pulsecore/inet_ntop.c b/src/pulsecore/inet_ntop.c
new file mode 100644 (file)
index 0000000..8755123
--- /dev/null
@@ -0,0 +1,79 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifndef HAVE_INET_NTOP
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "winsock.h"
+
+#include "inet_ntop.h"
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
+    struct in_addr *in = (struct in_addr*)src;
+    struct in6_addr *in6 = (struct in6_addr*)src;
+
+    assert(src && dst);
+
+    switch (af) {
+    case AF_INET:
+        pa_snprintf(dst, cnt, "%d.%d.%d.%d",
+#ifdef WORDS_BIGENDIAN
+            (int)(in->s_addr >> 24) & 0xff,
+            (int)(in->s_addr >> 16) & 0xff,
+            (int)(in->s_addr >>  8) & 0xff,
+            (int)(in->s_addr >>  0) & 0xff);
+#else
+            (int)(in->s_addr >>  0) & 0xff,
+            (int)(in->s_addr >>  8) & 0xff,
+            (int)(in->s_addr >> 16) & 0xff,
+            (int)(in->s_addr >> 24) & 0xff);
+#endif
+        break;
+    case AF_INET6:
+        pa_snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
+            in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1],
+            in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3],
+            in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5],
+            in6->s6_addr[ 6] << 8 | in6->s6_addr[ 7],
+            in6->s6_addr[ 8] << 8 | in6->s6_addr[ 9],
+            in6->s6_addr[10] << 8 | in6->s6_addr[11],
+            in6->s6_addr[12] << 8 | in6->s6_addr[13],
+            in6->s6_addr[14] << 8 | in6->s6_addr[15]);
+        break;
+    default:
+        errno = EAFNOSUPPORT;
+        return NULL;
+    }
+
+    return dst;
+}
+
+#endif /* INET_NTOP */
diff --git a/src/pulsecore/inet_ntop.h b/src/pulsecore/inet_ntop.h
new file mode 100644 (file)
index 0000000..7fb67b4
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef fooinet_ntophfoo
+#define fooinet_ntophfoo
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "winsock.h"
+
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
+
+#endif
diff --git a/src/pulsecore/inet_pton.c b/src/pulsecore/inet_pton.c
new file mode 100644 (file)
index 0000000..d191e55
--- /dev/null
@@ -0,0 +1,61 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifndef HAVE_INET_PTON
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "winsock.h"
+
+#include "inet_pton.h"
+
+int inet_pton(int af, const char *src, void *dst) {
+    struct in_addr *in = (struct in_addr*)dst;
+    struct in6_addr *in6 = (struct in6_addr*)dst;
+
+    assert(src && dst);
+
+    switch (af) {
+    case AF_INET:
+        in->s_addr = inet_addr(src);
+        if (in->s_addr == INADDR_NONE)
+            return 0;
+        break;
+    case AF_INET6:
+        /* FIXME */
+    default:
+        errno = EAFNOSUPPORT;
+        return -1;
+    }
+
+    return 1;
+}
+
+#endif /* INET_PTON */
diff --git a/src/pulsecore/inet_pton.h b/src/pulsecore/inet_pton.h
new file mode 100644 (file)
index 0000000..111b4a0
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef fooinet_ptonhfoo
+#define fooinet_ptonhfoo
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "winsock.h"
+
+int inet_pton(int af, const char *src, void *dst);
+
+#endif
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
new file mode 100644 (file)
index 0000000..b40c981
--- /dev/null
@@ -0,0 +1,437 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#include "winsock.h"
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "iochannel.h"
+
+struct pa_iochannel {
+    int ifd, ofd;
+    int ifd_type, ofd_type;
+    pa_mainloop_api* mainloop;
+
+    pa_iochannel_cb_t callback;
+    void*userdata;
+
+    pa_bool_t readable;
+    pa_bool_t writable;
+    pa_bool_t hungup;
+
+    pa_bool_t no_close;
+
+    pa_io_event* input_event, *output_event;
+};
+
+static void enable_mainloop_sources(pa_iochannel *io) {
+    pa_assert(io);
+
+    if (io->input_event == io->output_event && io->input_event) {
+        pa_io_event_flags_t f = PA_IO_EVENT_NULL;
+        pa_assert(io->input_event);
+
+        if (!io->readable)
+            f |= PA_IO_EVENT_INPUT;
+        if (!io->writable)
+            f |= PA_IO_EVENT_OUTPUT;
+
+        io->mainloop->io_enable(io->input_event, f);
+    } else {
+        if (io->input_event)
+            io->mainloop->io_enable(io->input_event, io->readable ? PA_IO_EVENT_NULL : PA_IO_EVENT_INPUT);
+        if (io->output_event)
+            io->mainloop->io_enable(io->output_event, io->writable ? PA_IO_EVENT_NULL : PA_IO_EVENT_OUTPUT);
+    }
+}
+
+static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    pa_iochannel *io = userdata;
+    pa_bool_t changed = FALSE;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(fd >= 0);
+    pa_assert(userdata);
+
+    if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
+        io->hungup = TRUE;
+        changed = TRUE;
+    }
+
+    if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
+        io->readable = TRUE;
+        changed = TRUE;
+        pa_assert(e == io->input_event);
+    }
+
+    if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
+        io->writable = TRUE;
+        changed = TRUE;
+        pa_assert(e == io->output_event);
+    }
+
+    if (changed) {
+        enable_mainloop_sources(io);
+
+        if (io->callback)
+            io->callback(io, io->userdata);
+    }
+}
+
+pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
+    pa_iochannel *io;
+
+    pa_assert(m);
+    pa_assert(ifd >= 0 || ofd >= 0);
+
+    io = pa_xnew(pa_iochannel, 1);
+    io->ifd = ifd;
+    io->ofd = ofd;
+    io->ifd_type = io->ofd_type = 0;
+    io->mainloop = m;
+
+    io->userdata = NULL;
+    io->callback = NULL;
+    io->readable = FALSE;
+    io->writable = FALSE;
+    io->hungup = FALSE;
+    io->no_close = FALSE;
+
+    io->input_event = io->output_event = NULL;
+
+    if (ifd == ofd) {
+        pa_assert(ifd >= 0);
+        pa_make_fd_nonblock(io->ifd);
+        io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io);
+    } else {
+
+        if (ifd >= 0) {
+            pa_make_fd_nonblock(io->ifd);
+            io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io);
+        }
+
+        if (ofd >= 0) {
+            pa_make_fd_nonblock(io->ofd);
+            io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io);
+        }
+    }
+
+    return io;
+}
+
+void pa_iochannel_free(pa_iochannel*io) {
+    pa_assert(io);
+
+    if (io->input_event)
+        io->mainloop->io_free(io->input_event);
+
+    if (io->output_event && (io->output_event != io->input_event))
+        io->mainloop->io_free(io->output_event);
+
+    if (!io->no_close) {
+        if (io->ifd >= 0)
+            pa_close(io->ifd);
+        if (io->ofd >= 0 && io->ofd != io->ifd)
+            pa_close(io->ofd);
+    }
+
+    pa_xfree(io);
+}
+
+pa_bool_t pa_iochannel_is_readable(pa_iochannel*io) {
+    pa_assert(io);
+
+    return io->readable || io->hungup;
+}
+
+pa_bool_t pa_iochannel_is_writable(pa_iochannel*io) {
+    pa_assert(io);
+
+    return io->writable && !io->hungup;
+}
+
+pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io) {
+    pa_assert(io);
+
+    return io->hungup;
+}
+
+ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
+    ssize_t r;
+
+    pa_assert(io);
+    pa_assert(data);
+    pa_assert(l);
+    pa_assert(io->ofd >= 0);
+
+    if ((r = pa_write(io->ofd, data, l, &io->ofd_type)) >= 0) {
+        io->writable = FALSE;
+        enable_mainloop_sources(io);
+    }
+
+    return r;
+}
+
+ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
+    ssize_t r;
+
+    pa_assert(io);
+    pa_assert(data);
+    pa_assert(io->ifd >= 0);
+
+    if ((r = pa_read(io->ifd, data, l, &io->ifd_type)) >= 0) {
+        io->readable = FALSE;
+        enable_mainloop_sources(io);
+    }
+
+    return r;
+}
+
+#ifdef HAVE_CREDS
+
+pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io) {
+    struct sockaddr_un sa;
+    socklen_t l;
+
+    pa_assert(io);
+    pa_assert(io->ifd >= 0);
+    pa_assert(io->ofd == io->ifd);
+
+    l = sizeof(sa);
+
+    if (getsockname(io->ifd, (struct sockaddr*) &sa, &l) < 0)
+        return 0;
+
+    return sa.sun_family == AF_UNIX;
+}
+
+int pa_iochannel_creds_enable(pa_iochannel *io) {
+    int t = 1;
+
+    pa_assert(io);
+    pa_assert(io->ifd >= 0);
+
+    if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {
+        pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred) {
+    ssize_t r;
+    struct msghdr mh;
+    struct iovec iov;
+    union {
+        struct cmsghdr hdr;
+        uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
+    } cmsg;
+    struct ucred *u;
+
+    pa_assert(io);
+    pa_assert(data);
+    pa_assert(l);
+    pa_assert(io->ofd >= 0);
+
+    memset(&iov, 0, sizeof(iov));
+    iov.iov_base = (void*) data;
+    iov.iov_len = l;
+
+    memset(&cmsg, 0, sizeof(cmsg));
+    cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
+    cmsg.hdr.cmsg_level = SOL_SOCKET;
+    cmsg.hdr.cmsg_type = SCM_CREDENTIALS;
+
+    u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
+
+    u->pid = getpid();
+    if (ucred) {
+        u->uid = ucred->uid;
+        u->gid = ucred->gid;
+    } else {
+        u->uid = getuid();
+        u->gid = getgid();
+    }
+
+    memset(&mh, 0, sizeof(mh));
+    mh.msg_name = NULL;
+    mh.msg_namelen = 0;
+    mh.msg_iov = &iov;
+    mh.msg_iovlen = 1;
+    mh.msg_control = &cmsg;
+    mh.msg_controllen = sizeof(cmsg);
+    mh.msg_flags = 0;
+
+    if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
+        io->writable = FALSE;
+        enable_mainloop_sources(io);
+    }
+
+    return r;
+}
+
+ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, pa_bool_t *creds_valid) {
+    ssize_t r;
+    struct msghdr mh;
+    struct iovec iov;
+    union {
+        struct cmsghdr hdr;
+        uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
+    } cmsg;
+
+    pa_assert(io);
+    pa_assert(data);
+    pa_assert(l);
+    pa_assert(io->ifd >= 0);
+    pa_assert(creds);
+    pa_assert(creds_valid);
+
+    memset(&iov, 0, sizeof(iov));
+    iov.iov_base = data;
+    iov.iov_len = l;
+
+    memset(&cmsg, 0, sizeof(cmsg));
+
+    memset(&mh, 0, sizeof(mh));
+    mh.msg_name = NULL;
+    mh.msg_namelen = 0;
+    mh.msg_iov = &iov;
+    mh.msg_iovlen = 1;
+    mh.msg_control = &cmsg;
+    mh.msg_controllen = sizeof(cmsg);
+    mh.msg_flags = 0;
+
+    if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) {
+        struct cmsghdr *cmh;
+
+        *creds_valid = 0;
+
+        for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) {
+
+            if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_CREDENTIALS) {
+                struct ucred u;
+                pa_assert(cmh->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
+                memcpy(&u, CMSG_DATA(cmh), sizeof(struct ucred));
+
+                creds->gid = u.gid;
+                creds->uid = u.uid;
+                *creds_valid = TRUE;
+                break;
+            }
+        }
+
+        io->readable = FALSE;
+        enable_mainloop_sources(io);
+    }
+
+    return r;
+}
+
+#endif /* HAVE_CREDS */
+
+void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) {
+    pa_assert(io);
+
+    io->callback = _callback;
+    io->userdata = userdata;
+}
+
+void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b) {
+    pa_assert(io);
+
+    io->no_close = !!b;
+}
+
+void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) {
+    pa_assert(io);
+    pa_assert(s);
+    pa_assert(l);
+
+    pa_socket_peer_to_string(io->ifd, s, l);
+}
+
+int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
+    pa_assert(io);
+
+    return pa_socket_set_rcvbuf(io->ifd, l);
+}
+
+int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
+    pa_assert(io);
+
+    return pa_socket_set_sndbuf(io->ofd, l);
+}
+
+pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
+    pa_assert(io);
+
+    return io->mainloop;
+}
+
+int pa_iochannel_get_recv_fd(pa_iochannel *io) {
+    pa_assert(io);
+
+    return io->ifd;
+}
+
+int pa_iochannel_get_send_fd(pa_iochannel *io) {
+    pa_assert(io);
+
+    return io->ofd;
+}
+
+pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io) {
+    pa_assert(io);
+
+    if (pa_socket_is_local(io->ifd))
+        return TRUE;
+
+    if (io->ifd != io->ofd)
+        if (pa_socket_is_local(io->ofd))
+            return TRUE;
+
+    return FALSE;
+}
diff --git a/src/pulsecore/iochannel.h b/src/pulsecore/iochannel.h
new file mode 100644 (file)
index 0000000..9050df9
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef fooiochannelhfoo
+#define fooiochannelhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#include <sys/types.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+
+/* A wrapper around UNIX file descriptors for attaching them to the a
+   main event loop. Everytime new data may be read or be written to
+   the channel a callback function is called. It is safe to destroy
+   the calling iochannel object from the callback */
+
+/* When pa_iochannel_is_readable() returns non-zero, the user has to
+ * call this function in a loop until it is no longer set or EOF
+ * reached. Otherwise strange things may happen when an EOF is
+ * reached. */
+
+typedef struct pa_iochannel pa_iochannel;
+
+/* Create a new IO channel for the specified file descriptors for
+input resp. output. It is safe to pass the same file descriptor for
+both parameters (in case of full-duplex channels). For a simplex
+channel specify -1 for the other direction. */
+
+pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd);
+void pa_iochannel_free(pa_iochannel*io);
+
+ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l);
+ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l);
+
+#ifdef HAVE_CREDS
+pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io);
+int pa_iochannel_creds_enable(pa_iochannel *io);
+
+ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred);
+ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *ucred, pa_bool_t *creds_valid);
+#endif
+
+pa_bool_t pa_iochannel_is_readable(pa_iochannel*io);
+pa_bool_t pa_iochannel_is_writable(pa_iochannel*io);
+pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io);
+
+/* Don't close the file descirptors when the io channel is freed. By
+ * default the file descriptors are closed. */
+void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b);
+
+/* Set the callback function that is called whenever data becomes available for read or write */
+typedef void (*pa_iochannel_cb_t)(pa_iochannel*io, void *userdata);
+void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t callback, void *userdata);
+
+/* In case the file descriptor is a socket, return a pretty-printed string in *s which describes the peer connected */
+void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l);
+
+/* Use setsockopt() to tune the recieve and send buffers of TCP sockets */
+int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l);
+int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l);
+
+pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io);
+
+pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io);
+
+int pa_iochannel_get_recv_fd(pa_iochannel *io);
+int pa_iochannel_get_send_fd(pa_iochannel *io);
+
+#endif
similarity index 59%
rename from polyp/ioline.c
rename to src/pulsecore/ioline.c
index 6f7886da5b428df963563bdfb24b7172f94e31bd..90afaafd603aed24bf83aa5abd9a1c5f92d39a03 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include <errno.h>
 #include <stdio.h>
-#include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/winsock.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+
 #include "ioline.h"
-#include "xmalloc.h"
-#include "log.h"
 
 #define BUFFER_LIMIT (64*1024)
 #define READ_SIZE (1024)
 
 struct pa_ioline {
-    struct pa_iochannel *io;
-    struct pa_defer_event *defer_event;
-    struct pa_mainloop_api *mainloop;
-    int ref;
-    int dead;
+    PA_REFCNT_DECLARE;
+
+    pa_iochannel *io;
+    pa_defer_event *defer_event;
+    pa_mainloop_api *mainloop;
 
     char *wbuf;
     size_t wbuf_length, wbuf_index, wbuf_valid_length;
@@ -49,22 +54,23 @@ struct pa_ioline {
     char *rbuf;
     size_t rbuf_length, rbuf_index, rbuf_valid_length;
 
-    void (*callback)(struct pa_ioline*io, const char *s, void *userdata);
+    pa_ioline_cb_t callback;
     void *userdata;
 
-    int defer_close;
+    pa_bool_t dead:1;
+    pa_bool_t defer_close:1;
 };
 
-static void io_callback(struct pa_iochannel*io, void *userdata);
-static void defer_callback(struct pa_mainloop_api*m, struct pa_defer_event*e, void *userdata);
+static void io_callback(pa_iochannel*io, void *userdata);
+static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata);
+
+pa_ioline* pa_ioline_new(pa_iochannel *io) {
+    pa_ioline *l;
+    pa_assert(io);
 
-struct pa_ioline* pa_ioline_new(struct pa_iochannel *io) {
-    struct pa_ioline *l;
-    assert(io);
-    
-    l = pa_xmalloc(sizeof(struct pa_ioline));
+    l = pa_xnew(pa_ioline, 1);
+    PA_REFCNT_INIT(l);
     l->io = io;
-    l->dead = 0;
 
     l->wbuf = NULL;
     l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
@@ -74,22 +80,22 @@ struct pa_ioline* pa_ioline_new(struct pa_iochannel *io) {
 
     l->callback = NULL;
     l->userdata = NULL;
-    l->ref = 1;
 
     l->mainloop = pa_iochannel_get_mainloop_api(io);
 
     l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
     l->mainloop->defer_enable(l->defer_event, 0);
 
-    l->defer_close = 0;
-    
+    l->dead = FALSE;
+    l->defer_close = FALSE;
+
     pa_iochannel_set_callback(io, io_callback, l);
-    
+
     return l;
 }
 
-static void ioline_free(struct pa_ioline *l) {
-    assert(l);
+static void ioline_free(pa_ioline *l) {
+    pa_assert(l);
 
     if (l->io)
         pa_iochannel_free(l->io);
@@ -102,24 +108,28 @@ static void ioline_free(struct pa_ioline *l) {
     pa_xfree(l);
 }
 
-void pa_ioline_unref(struct pa_ioline *l) {
-    assert(l && l->ref >= 1);
+void pa_ioline_unref(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
 
-    if ((--l->ref) <= 0)
+    if (PA_REFCNT_DEC(l) <= 0)
         ioline_free(l);
 }
 
-struct pa_ioline* pa_ioline_ref(struct pa_ioline *l) {
-    assert(l && l->ref >= 1);
+pa_ioline* pa_ioline_ref(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
 
-    l->ref++;
+    PA_REFCNT_INC(l);
     return l;
 }
 
-void pa_ioline_close(struct pa_ioline *l) {
-    assert(l && l->ref >= 1);
+void pa_ioline_close(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    l->dead = TRUE;
 
-    l->dead = 1;
     if (l->io) {
         pa_iochannel_free(l->io);
         l->io = NULL;
@@ -129,80 +139,106 @@ void pa_ioline_close(struct pa_ioline *l) {
         l->mainloop->defer_free(l->defer_event);
         l->defer_event = NULL;
     }
+
+    if (l->callback)
+        l->callback = NULL;
 }
 
-void pa_ioline_puts(struct pa_ioline *l, const char *c) {
+void pa_ioline_puts(pa_ioline *l, const char *c) {
     size_t len;
-    assert(l && c && l->ref >= 1 && !l->dead);
 
-    pa_ioline_ref(l);
-    
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+    pa_assert(c);
+
+    if (l->dead)
+        return;
+
     len = strlen(c);
     if (len > BUFFER_LIMIT - l->wbuf_valid_length)
         len = BUFFER_LIMIT - l->wbuf_valid_length;
 
     if (len) {
-        assert(l->wbuf_length >= l->wbuf_valid_length);
-        
+        pa_assert(l->wbuf_length >= l->wbuf_valid_length);
+
         /* In case the allocated buffer is too small, enlarge it. */
         if (l->wbuf_valid_length + len > l->wbuf_length) {
             size_t n = l->wbuf_valid_length+len;
-            char *new = pa_xmalloc(n);
+            char *new = pa_xnew(char, n);
+
             if (l->wbuf) {
                 memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
                 pa_xfree(l->wbuf);
             }
+
             l->wbuf = new;
             l->wbuf_length = n;
             l->wbuf_index = 0;
         } else if (l->wbuf_index + l->wbuf_valid_length + len > l->wbuf_length) {
-            
+
             /* In case the allocated buffer fits, but the current index is too far from the start, move it to the front. */
             memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
             l->wbuf_index = 0;
         }
-        
-        assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
-        
+
+        pa_assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
+
         /* Append the new string */
         memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len);
         l->wbuf_valid_length += len;
 
         l->mainloop->defer_enable(l->defer_event, 1);
     }
-
-    pa_ioline_unref(l);
 }
 
-void pa_ioline_set_callback(struct pa_ioline*l, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata) {
-    assert(l && l->ref >= 1);
+void pa_ioline_set_callback(pa_ioline*l, pa_ioline_cb_t callback, void *userdata) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
+    if (l->dead)
+        return;
+
     l->callback = callback;
     l->userdata = userdata;
 }
 
-static void failure(struct pa_ioline *l) {
-    assert(l && l->ref >= 1 && !l->dead);
+static void failure(pa_ioline *l, pa_bool_t process_leftover) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+    pa_assert(!l->dead);
 
-    pa_ioline_close(l);
+    if (process_leftover && l->rbuf_valid_length > 0) {
+        /* Pass the last missing bit to the client */
+
+        if (l->callback) {
+            char *p = pa_xstrndup(l->rbuf+l->rbuf_index, l->rbuf_valid_length);
+            l->callback(l, p, l->userdata);
+            pa_xfree(p);
+        }
+    }
 
     if (l->callback) {
         l->callback(l, NULL, l->userdata);
         l->callback = NULL;
     }
+
+    pa_ioline_close(l);
 }
 
-static void scan_for_lines(struct pa_ioline *l, size_t skip) {
-    assert(l && l->ref >= 1 && skip < l->rbuf_valid_length);
+static void scan_for_lines(pa_ioline *l, size_t skip) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+    pa_assert(skip < l->rbuf_valid_length);
 
     while (!l->dead && l->rbuf_valid_length > skip) {
         char *e, *p;
         size_t m;
-        
+
         if (!(e = memchr(l->rbuf + l->rbuf_index + skip, '\n', l->rbuf_valid_length - skip)))
             break;
 
         *e = 0;
-    
+
         p = l->rbuf + l->rbuf_index;
         m = strlen(p);
 
@@ -214,7 +250,7 @@ static void scan_for_lines(struct pa_ioline *l, size_t skip) {
             l->rbuf_index = 0;
 
         if (l->callback)
-            l->callback(l, p, l->userdata);
+            l->callback(l, pa_strip_nl(p), l->userdata);
 
         skip = 0;
     }
@@ -224,135 +260,166 @@ static void scan_for_lines(struct pa_ioline *l, size_t skip) {
         l->rbuf_index = l->rbuf_valid_length = 0;
 }
 
-static int do_read(struct pa_ioline *l) {
-    assert(l && l->ref >= 1);
+static int do_write(pa_ioline *l);
+
+static int do_read(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
 
     while (!l->dead && pa_iochannel_is_readable(l->io)) {
         ssize_t r;
         size_t len;
 
         len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
-        
+
         /* Check if we have to enlarge the read buffer */
         if (len < READ_SIZE) {
             size_t n = l->rbuf_valid_length+READ_SIZE;
-            
+
             if (n >= BUFFER_LIMIT)
                 n = BUFFER_LIMIT;
-            
+
             if (l->rbuf_length >= n) {
                 /* The current buffer is large enough, let's just move the data to the front */
                 if (l->rbuf_valid_length)
                     memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
             } else {
                 /* Enlarge the buffer */
-                char *new = pa_xmalloc(n);
+                char *new = pa_xnew(char, n);
                 if (l->rbuf_valid_length)
                     memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
                 pa_xfree(l->rbuf);
                 l->rbuf = new;
                 l->rbuf_length = n;
             }
-            
+
             l->rbuf_index = 0;
         }
-        
+
         len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
-        
-        assert(len >= READ_SIZE);
-        
+
+        pa_assert(len >= READ_SIZE);
+
         /* Read some data */
         if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) {
-            pa_log(__FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
-            failure(l);
+
+            if (r < 0 && errno == EAGAIN)
+                return 0;
+
+            if (r < 0 && errno != ECONNRESET) {
+                pa_log("read(): %s", pa_cstrerror(errno));
+                failure(l, FALSE);
+            } else
+                failure(l, TRUE);
+
             return -1;
         }
-        
+
         l->rbuf_valid_length += r;
-        
+
         /* Look if a line has been terminated in the newly read data */
         scan_for_lines(l, l->rbuf_valid_length - r);
     }
-        
+
     return 0;
 }
 
 /* Try to flush the buffer */
-static int do_write(struct pa_ioline *l) {
+static int do_write(pa_ioline *l) {
     ssize_t r;
-    assert(l && l->ref >= 1);
+
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
 
     while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) {
-        
-        if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) {
-            pa_log(__FILE__": write() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
-            failure(l);
+
+        if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) <= 0) {
+
+            if (r < 0 && errno == EAGAIN)
+                return 0;
+
+            if (r < 0 && errno != EPIPE)
+                pa_log("write(): %s", pa_cstrerror(errno));
+
+            failure(l, FALSE);
+
             return -1;
         }
-        
+
         l->wbuf_index += r;
         l->wbuf_valid_length -= r;
-        
+
         /* A shortcut for the next time */
         if (l->wbuf_valid_length == 0)
             l->wbuf_index = 0;
     }
-        
+
     return 0;
 }
 
 /* Try to flush read/write data */
-static void do_work(struct pa_ioline *l) {
-    assert(l && l->ref >= 1);
+static void do_work(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
 
     pa_ioline_ref(l);
 
     l->mainloop->defer_enable(l->defer_event, 0);
-    
-    if (!l->dead)
-        do_write(l);
 
     if (!l->dead)
         do_read(l);
 
+    if (!l->dead)
+        do_write(l);
+
     if (l->defer_close && !l->wbuf_valid_length)
-        failure(l);
+        failure(l, TRUE);
 
     pa_ioline_unref(l);
 }
 
-static void io_callback(struct pa_iochannel*io, void *userdata) {
-    struct pa_ioline *l = userdata;
-    assert(io && l && l->ref >= 1);
+static void io_callback(pa_iochannel*io, void *userdata) {
+    pa_ioline *l = userdata;
+
+    pa_assert(io);
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
 
     do_work(l);
 }
 
-static void defer_callback(struct pa_mainloop_api*m, struct pa_defer_event*e, void *userdata) {
-    struct pa_ioline *l = userdata;
-    assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e);
+static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) {
+    pa_ioline *l = userdata;
+
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+    pa_assert(l->mainloop == m);
+    pa_assert(l->defer_event == e);
 
     do_work(l);
 }
 
-void pa_ioline_defer_close(struct pa_ioline *l) {
-    assert(l);
+void pa_ioline_defer_close(pa_ioline *l) {
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
 
-    l->defer_close = 1;
+    l->defer_close = TRUE;
 
     if (!l->wbuf_valid_length)
         l->mainloop->defer_enable(l->defer_event, 1);
 }
 
-void pa_ioline_printf(struct pa_ioline *s, const char *format, ...) {
+void pa_ioline_printf(pa_ioline *l, const char *format, ...) {
     char *t;
     va_list ap;
 
-    
+    pa_assert(l);
+    pa_assert(PA_REFCNT_VALUE(l) >= 1);
+
     va_start(ap, format);
     t = pa_vsprintf_malloc(format, ap);
     va_end(ap);
 
-    pa_ioline_puts(s, t);
+    pa_ioline_puts(l, t);
     pa_xfree(t);
 }
similarity index 52%
rename from polyp/ioline.h
rename to src/pulsecore/ioline.h
index 6e9c76d0884ece566d57f87b5b83aa2caeeb7b2a..b9a3d9f4749252301958f2616477b2f429f1461e 100644 (file)
@@ -1,51 +1,53 @@
 #ifndef fooiolinehfoo
 #define fooiolinehfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "iochannel.h"
-#include "util.h"
+#include <pulsecore/iochannel.h>
+#include <pulsecore/core-util.h>
 
 /* An ioline wraps an iochannel for line based communication. A
  * callback function is called whenever a new line has been recieved
  * from the client */
 
-struct pa_ioline;
+typedef struct pa_ioline pa_ioline;
+
+typedef void (*pa_ioline_cb_t)(pa_ioline*io, const char *s, void *userdata);
 
-struct pa_ioline* pa_ioline_new(struct pa_iochannel *io);
-void pa_ioline_unref(struct pa_ioline *l);
-struct pa_ioline* pa_ioline_ref(struct pa_ioline *l);
-void pa_ioline_close(struct pa_ioline *l);
+pa_ioline* pa_ioline_new(pa_iochannel *io);
+void pa_ioline_unref(pa_ioline *l);
+pa_ioline* pa_ioline_ref(pa_ioline *l);
+void pa_ioline_close(pa_ioline *l);
 
 /* Write a string to the channel */
-void pa_ioline_puts(struct pa_ioline *s, const char *c);
+void pa_ioline_puts(pa_ioline *s, const char *c);
 
 /* Write a string to the channel */
-void pa_ioline_printf(struct pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
+void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
 
 /* Set the callback function that is called for every recieved line */
-void pa_ioline_set_callback(struct pa_ioline*io, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata);
+void pa_ioline_set_callback(pa_ioline*io, pa_ioline_cb_t callback, void *userdata);
 
 /* Make sure to close the ioline object as soon as the send buffer is emptied */
-void pa_ioline_defer_close(struct pa_ioline *io);
+void pa_ioline_defer_close(pa_ioline *io);
 
 #endif
diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c
new file mode 100644 (file)
index 0000000..7b5f865
--- /dev/null
@@ -0,0 +1,237 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <sys/types.h>
+#include <string.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/winsock.h>
+
+#ifndef HAVE_INET_PTON
+#include "inet_pton.h"
+#endif
+
+#include "ipacl.h"
+
+struct acl_entry {
+    PA_LLIST_FIELDS(struct acl_entry);
+    int family;
+    struct in_addr address_ipv4;
+    struct in6_addr address_ipv6;
+    int bits;
+};
+
+struct pa_ip_acl {
+    PA_LLIST_HEAD(struct acl_entry, entries);
+};
+
+pa_ip_acl* pa_ip_acl_new(const char *s) {
+    const char *state = NULL;
+    char *a;
+    pa_ip_acl *acl;
+
+    pa_assert(s);
+
+    acl = pa_xnew(pa_ip_acl, 1);
+    PA_LLIST_HEAD_INIT(struct acl_entry, acl->entries);
+
+    while ((a = pa_split(s, ";", &state))) {
+        char *slash;
+        struct acl_entry e, *n;
+        uint32_t bits;
+
+        if ((slash = strchr(a, '/'))) {
+            *slash = 0;
+            slash++;
+            if (pa_atou(slash, &bits) < 0) {
+                pa_log_warn("Failed to parse number of bits: %s", slash);
+                goto fail;
+            }
+        } else
+            bits = (uint32_t) -1;
+
+        if (inet_pton(AF_INET, a, &e.address_ipv4) > 0) {
+
+            e.bits = bits == (uint32_t) -1 ? 32 : (int) bits;
+
+            if (e.bits > 32) {
+                pa_log_warn("Number of bits out of range: %i", e.bits);
+                goto fail;
+            }
+
+            e.family = AF_INET;
+
+            if (e.bits < 32 && (uint32_t) (ntohl(e.address_ipv4.s_addr) << e.bits) != 0)
+                pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
+
+        } else if (inet_pton(AF_INET6, a, &e.address_ipv6) > 0) {
+
+            e.bits = bits == (uint32_t) -1 ? 128 : (int) bits;
+
+            if (e.bits > 128) {
+                pa_log_warn("Number of bits out of range: %i", e.bits);
+                goto fail;
+            }
+            e.family = AF_INET6;
+
+            if (e.bits < 128) {
+                int t = 0, i;
+
+                for (i = 0, bits = e.bits; i < 16; i++) {
+
+                    if (bits >= 8)
+                        bits -= 8;
+                    else {
+                        if ((uint8_t) ((e.address_ipv6.s6_addr[i]) << bits) != 0) {
+                            t = 1;
+                            break;
+                        }
+                        bits = 0;
+                    }
+                }
+
+                if (t)
+                    pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
+            }
+
+        } else {
+            pa_log_warn("Failed to parse address: %s", a);
+            goto fail;
+        }
+
+        n = pa_xmemdup(&e, sizeof(struct acl_entry));
+        PA_LLIST_PREPEND(struct acl_entry, acl->entries, n);
+
+        pa_xfree(a);
+    }
+
+    return acl;
+
+fail:
+    pa_xfree(a);
+    pa_ip_acl_free(acl);
+
+    return NULL;
+}
+
+void pa_ip_acl_free(pa_ip_acl *acl) {
+    pa_assert(acl);
+
+    while (acl->entries) {
+        struct acl_entry *e = acl->entries;
+        PA_LLIST_REMOVE(struct acl_entry, acl->entries, e);
+        pa_xfree(e);
+    }
+
+    pa_xfree(acl);
+}
+
+int pa_ip_acl_check(pa_ip_acl *acl, int fd) {
+    struct sockaddr_storage sa;
+    struct acl_entry *e;
+    socklen_t  salen;
+
+    pa_assert(acl);
+    pa_assert(fd >= 0);
+
+    salen = sizeof(sa);
+    if (getpeername(fd, (struct sockaddr*) &sa, &salen) < 0)
+        return -1;
+
+    if (sa.ss_family != AF_INET && sa.ss_family != AF_INET6)
+        return -1;
+
+    if (sa.ss_family == AF_INET && salen != sizeof(struct sockaddr_in))
+        return -1;
+
+    if (sa.ss_family == AF_INET6 && salen != sizeof(struct sockaddr_in6))
+        return -1;
+
+    for (e = acl->entries; e; e = e->next) {
+
+        if (e->family != sa.ss_family)
+            continue;
+
+        if (e->family == AF_INET) {
+            struct sockaddr_in *sai = (struct sockaddr_in*) &sa;
+
+            if (e->bits == 0 || /* this needs special handling because >> takes the right-hand side modulo 32 */
+                (ntohl(sai->sin_addr.s_addr ^ e->address_ipv4.s_addr) >> (32 - e->bits)) == 0)
+                return 1;
+        } else if (e->family == AF_INET6) {
+            int i, bits ;
+            struct sockaddr_in6 *sai = (struct sockaddr_in6*) &sa;
+
+            if (e->bits == 128)
+                return memcmp(&sai->sin6_addr, &e->address_ipv6, 16) == 0;
+
+            if (e->bits == 0)
+                return 1;
+
+            for (i = 0, bits = e->bits; i < 16; i++) {
+
+                if (bits >= 8) {
+                    if (sai->sin6_addr.s6_addr[i] != e->address_ipv6.s6_addr[i])
+                        break;
+
+                    bits -= 8;
+                } else {
+                    if ((sai->sin6_addr.s6_addr[i] ^ e->address_ipv6.s6_addr[i]) >> (8 - bits) != 0)
+                        break;
+
+                    bits = 0;
+                }
+
+                if (bits == 0)
+                    return 1;
+            }
+        }
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/ipacl.h b/src/pulsecore/ipacl.h
new file mode 100644 (file)
index 0000000..7b7ffa6
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef fooparseaddrhfoo
+#define fooparseaddrhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+typedef struct pa_ip_acl pa_ip_acl;
+
+pa_ip_acl* pa_ip_acl_new(const char *s);
+void pa_ip_acl_free(pa_ip_acl *acl);
+int pa_ip_acl_check(pa_ip_acl *acl, int fd);
+
+#endif
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
new file mode 100644 (file)
index 0000000..46b54eb
--- /dev/null
@@ -0,0 +1,107 @@
+#ifndef foollistfoo
+#define foollistfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/macro.h>
+
+/* Some macros for maintaining doubly linked lists */
+
+/* The head of the linked list. Use this in the structure that shall
+ * contain the head of the linked list */
+#define PA_LLIST_HEAD(t,name)                                           \
+    t *name
+
+/* The pointers in the linked list's items. Use this in the item structure */
+#define PA_LLIST_FIELDS(t)                                              \
+    t *next, *prev
+
+/* Initialize the list's head */
+#define PA_LLIST_HEAD_INIT(t,item)                                      \
+    do {                                                                \
+        (item) = (t*) NULL; }                                           \
+    while(0)
+
+/* Initialize a list item */
+#define PA_LLIST_INIT(t,item)                                           \
+    do {                                                                \
+        t *_item = (item);                                              \
+        pa_assert(_item);                                               \
+        _item->prev = _item->next = NULL;                               \
+    } while(0)
+
+/* Prepend an item to the list */
+#define PA_LLIST_PREPEND(t,head,item)                                   \
+    do {                                                                \
+        t **_head = &(head), *_item = (item);                           \
+        pa_assert(_item);                                               \
+        if ((_item->next = *_head))                                     \
+            _item->next->prev = _item;                                  \
+        _item->prev = NULL;                                             \
+        *_head = _item;                                                 \
+    } while (0)
+
+/* Remove an item from the list */
+#define PA_LLIST_REMOVE(t,head,item)                                    \
+    do {                                                                \
+        t **_head = &(head), *_item = (item);                           \
+        pa_assert(_item);                                               \
+        if (_item->next)                                                \
+            _item->next->prev = _item->prev;                            \
+        if (_item->prev)                                                \
+            _item->prev->next = _item->next;                            \
+        else {                                                          \
+            pa_assert(*_head == _item);                                 \
+            *_head = _item->next;                                       \
+        }                                                               \
+        _item->next = _item->prev = NULL;                               \
+    } while(0)
+
+/* Find the head of the list */
+#define PA_LLIST_FIND_HEAD(t,item,head)                                 \
+    do {                                                                \
+        t **_head = (head), *_item = (item);                            \
+        *_head = _item;                                                 \
+        pa_assert(_head);                                               \
+        while ((*_head)->prev)                                          \
+            *_head = (*_head)->prev;                                    \
+    } while (0)
+
+/* Insert an item after another one (a = where, b = what) */
+#define PA_LLIST_INSERT_AFTER(t,head,a,b)                               \
+    do {                                                                \
+        t **_head = &(head), *_a = (a), *_b = (b);                      \
+        pa_assert(_b);                                                  \
+        if (!_a) {                                                      \
+            if ((_b->next = *_head))                                    \
+                _b->next->prev = _b;                                    \
+            _b->prev = NULL;                                            \
+            *_head = _b;                                                \
+        } else {                                                        \
+            if ((_b->next = _a->next))                                  \
+                _b->next->prev = _b;                                    \
+            _b->prev = _a;                                              \
+            _a->next = _b;                                              \
+        }                                                               \
+    } while (0)
+
+#endif
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
new file mode 100644 (file)
index 0000000..5eda4f6
--- /dev/null
@@ -0,0 +1,240 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "log.h"
+
+#define ENV_LOGLEVEL "PULSE_LOG"
+#define ENV_LOGMETA "PULSE_LOG_META"
+
+static char *log_ident = NULL, *log_ident_local = NULL;
+static pa_log_target_t log_target = PA_LOG_STDERR;
+static void (*user_log_func)(pa_log_level_t l, const char *s) = NULL;
+static pa_log_level_t maximal_level = PA_LOG_NOTICE;
+
+#ifdef HAVE_SYSLOG_H
+static const int level_to_syslog[] = {
+    [PA_LOG_ERROR] = LOG_ERR,
+    [PA_LOG_WARN] = LOG_WARNING,
+    [PA_LOG_NOTICE] = LOG_NOTICE,
+    [PA_LOG_INFO] = LOG_INFO,
+    [PA_LOG_DEBUG] = LOG_DEBUG
+};
+#endif
+
+static const char level_to_char[] = {
+    [PA_LOG_ERROR] = 'E',
+    [PA_LOG_WARN] = 'W',
+    [PA_LOG_NOTICE] = 'N',
+    [PA_LOG_INFO] = 'I',
+    [PA_LOG_DEBUG] = 'D'
+};
+
+void pa_log_set_ident(const char *p) {
+    pa_xfree(log_ident);
+    pa_xfree(log_ident_local);
+
+    log_ident = pa_xstrdup(p);
+    if (!(log_ident_local = pa_utf8_to_locale(log_ident)))
+        log_ident_local = pa_xstrdup(log_ident);
+}
+
+/* To make valgrind shut up. */
+static void ident_destructor(void) PA_GCC_DESTRUCTOR;
+static void ident_destructor(void) {
+    pa_xfree(log_ident);
+    pa_xfree(log_ident_local);
+}
+
+void pa_log_set_maximal_level(pa_log_level_t l) {
+    pa_assert(l < PA_LOG_LEVEL_MAX);
+
+    maximal_level = l;
+}
+
+void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) {
+    pa_assert(t == PA_LOG_USER || !func);
+
+    log_target = t;
+    user_log_func = func;
+}
+
+void pa_log_levelv_meta(
+        pa_log_level_t level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *format,
+        va_list ap) {
+
+    const char *e;
+    char *t, *n;
+    int saved_errno = errno;
+
+    /* We don't use dynamic memory allocation here to minimize the hit
+     * in RT threads */
+    char text[1024], location[128];
+
+    pa_assert(level < PA_LOG_LEVEL_MAX);
+    pa_assert(format);
+
+    if ((e = getenv(ENV_LOGLEVEL)))
+        maximal_level = atoi(e);
+
+    if (level > maximal_level) {
+        errno = saved_errno;
+        return;
+    }
+
+    pa_vsnprintf(text, sizeof(text), format, ap);
+
+    if (getenv(ENV_LOGMETA) && file && line > 0 && func)
+        pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);
+    else if (file)
+        pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
+    else
+        location[0] = 0;
+
+    if (!pa_utf8_valid(text))
+        pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
+
+    for (t = text; t; t = n) {
+        if ((n = strchr(t, '\n'))) {
+            *n = 0;
+            n++;
+        }
+
+        if (!*t)
+            continue;
+
+        switch (log_target) {
+            case PA_LOG_STDERR: {
+                const char *prefix = "", *suffix = "";
+                char *local_t;
+
+#ifndef OS_IS_WIN32
+                /* Yes indeed. Useless, but fun! */
+                if (isatty(STDERR_FILENO)) {
+                    if (level <= PA_LOG_ERROR) {
+                        prefix = "\x1B[1;31m";
+                        suffix = "\x1B[0m";
+                    } else if (level <= PA_LOG_WARN) {
+                        prefix = "\x1B[1m";
+                        suffix = "\x1B[0m";
+                    }
+                }
+#endif
+
+                /* We shouldn't be using dynamic allocation here to
+                 * minimize the hit in RT threads */
+                local_t = pa_utf8_to_locale(t);
+                if (!local_t)
+                    fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, t, suffix);
+                else {
+                    fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, local_t, suffix);
+                    pa_xfree(local_t);
+                }
+
+                break;
+            }
+
+#ifdef HAVE_SYSLOG_H
+            case PA_LOG_SYSLOG: {
+                char *local_t;
+
+                openlog(log_ident_local ? log_ident_local : "???", LOG_PID, LOG_USER);
+
+                local_t = pa_utf8_to_locale(t);
+                if (!local_t)
+                    syslog(level_to_syslog[level], "%s%s", location, t);
+                else {
+                    syslog(level_to_syslog[level], "%s%s", location, local_t);
+                    pa_xfree(local_t);
+                }
+
+                closelog();
+                break;
+            }
+#endif
+
+            case PA_LOG_USER: {
+                char x[1024];
+
+                pa_snprintf(x, sizeof(x), "%s%s", location, t);
+                user_log_func(level, x);
+
+                break;
+            }
+
+            case PA_LOG_NULL:
+            default:
+                break;
+        }
+    }
+
+    errno = saved_errno;
+}
+
+void pa_log_level_meta(
+        pa_log_level_t level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *format, ...) {
+
+    va_list ap;
+    va_start(ap, format);
+    pa_log_levelv_meta(level, file, line, func, format, ap);
+    va_end(ap);
+}
+
+void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
+    pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
+}
+
+void pa_log_level(pa_log_level_t level, const char *format, ...) {
+    va_list ap;
+
+    va_start(ap, format);
+    pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
+    va_end(ap);
+}
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
new file mode 100644 (file)
index 0000000..2047696
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef foologhfoo
+#define foologhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <pulse/gccmacro.h>
+
+/* A simple logging subsystem */
+
+/* Where to log to */
+typedef enum pa_log_target {
+    PA_LOG_STDERR,  /* default */
+    PA_LOG_SYSLOG,
+    PA_LOG_USER,    /* to user specified function */
+    PA_LOG_NULL     /* to /dev/null */
+} pa_log_target_t;
+
+typedef enum pa_log_level {
+    PA_LOG_ERROR  = 0,    /* Error messages */
+    PA_LOG_WARN   = 1,    /* Warning messages */
+    PA_LOG_NOTICE = 2,    /* Notice messages */
+    PA_LOG_INFO   = 3,    /* Info messages */
+    PA_LOG_DEBUG  = 4,    /* debug message */
+    PA_LOG_LEVEL_MAX
+} pa_log_level_t;
+
+/* Set an identification for the current daemon. Used when logging to syslog. */
+void pa_log_set_ident(const char *p);
+
+/* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */
+void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t t, const char*s));
+
+/* Minimal log level */
+void pa_log_set_maximal_level(pa_log_level_t l);
+
+void pa_log_level_meta(
+        pa_log_level_t level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *format, ...) PA_GCC_PRINTF_ATTR(5,6);
+void pa_log_levelv_meta(
+        pa_log_level_t level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *format,
+        va_list ap);
+
+void pa_log_level(pa_log_level_t level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
+void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap);
+
+#if __STDC_VERSION__ >= 199901L
+
+/* ISO varargs available */
+
+#define pa_log_debug(...)  pa_log_level_meta(PA_LOG_DEBUG,  __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_log_info(...)   pa_log_level_meta(PA_LOG_INFO,   __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_log_notice(...) pa_log_level_meta(PA_LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_log_warn(...)   pa_log_level_meta(PA_LOG_WARN,   __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_log_error(...)  pa_log_level_meta(PA_LOG_ERROR,  __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+#else
+
+#define LOG_FUNC(suffix, level) \
+PA_GCC_UNUSED static void pa_log_##suffix(const char *format, ...) { \
+    va_list ap; \
+    va_start(ap, format); \
+    pa_log_levelv_meta(level, NULL, 0, NULL, format, ap); \
+    va_end(ap); \
+}
+
+LOG_FUNC(debug, PA_LOG_DEBUG)
+LOG_FUNC(info, PA_LOG_INFO)
+LOG_FUNC(notice, PA_LOG_NOTICE)
+LOG_FUNC(warn, PA_LOG_WARN)
+LOG_FUNC(error, PA_LOG_ERROR)
+
+#endif
+
+#define pa_log pa_log_error
+
+#endif
diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c
new file mode 100644 (file)
index 0000000..0d4c22f
--- /dev/null
@@ -0,0 +1,64 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <ctype.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "ltdl-helper.h"
+
+pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *symbol) {
+    char *sn, *c;
+    pa_void_func_t f;
+
+    pa_assert(handle);
+    pa_assert(symbol);
+
+    if ((f = ((pa_void_func_t) (size_t) lt_dlsym(handle, symbol))))
+        return f;
+
+    if (!module)
+        return NULL;
+
+    /* As the .la files might have been cleansed from the system, we should
+     * try with the ltdl prefix as well. */
+
+    sn = pa_sprintf_malloc("%s_LTX_%s", module, symbol);
+
+    for (c = sn; *c; c++)
+        if (!isalnum(*c))
+            *c = '_';
+
+    f = (pa_void_func_t) (size_t) lt_dlsym(handle, sn);
+    pa_xfree(sn);
+
+    return f;
+}
similarity index 52%
rename from polyp/play-memchunk.h
rename to src/pulsecore/ltdl-helper.h
index c69165a29c0651eff3e4be41cc1e49fc78805fba..ea73de54d441e8dff6315fea1fde7c5ae06f31c2 100644 (file)
@@ -1,30 +1,32 @@
-#ifndef fooplaychunkhfoo
-#define fooplaychunkhfoo
-
-/* $Id$ */
+#ifndef foopulsecoreltdlhelperhfoo
+#define foopulsecoreltdlhelperhfoo
 
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "sink.h"
-#include "memchunk.h"
+#include <ltdl.h>
+
+typedef void (*pa_void_func_t)(void);
 
-int pa_play_memchunk(struct pa_sink *sink, const char *name, const struct pa_sample_spec *ss, const struct pa_memchunk *chunk, pa_volume_t volume);
+pa_void_func_t pa_load_sym(lt_dlhandle handle, const char*module, const char *symbol);
 
 #endif
+
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
new file mode 100644 (file)
index 0000000..fd33b7b
--- /dev/null
@@ -0,0 +1,224 @@
+#ifndef foopulsemacrohfoo
+#define foopulsemacrohfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulsecore/log.h>
+#include <pulse/gccmacro.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+#ifndef PA_LIKELY
+#ifdef __GNUC__
+#define PA_LIKELY(x) (__builtin_expect(!!(x),1))
+#define PA_UNLIKELY(x) (__builtin_expect((x),0))
+#else
+#define PA_LIKELY(x) (x)
+#define PA_UNLIKELY(x) (x)
+#endif
+#endif
+
+#if defined(PAGE_SIZE)
+#define PA_PAGE_SIZE ((size_t) PAGE_SIZE)
+#elif defined(PAGESIZE)
+#define PA_PAGE_SIZE ((size_t) PAGESIZE)
+#elif defined(HAVE_SYSCONF)
+#define PA_PAGE_SIZE ((size_t) (sysconf(_SC_PAGE_SIZE)))
+#else
+/* Let's hope it's like x86. */
+#define PA_PAGE_SIZE ((size_t) 4096)
+#endif
+
+static inline size_t pa_align(size_t l) {
+    return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*));
+}
+#define PA_ALIGN(x) (pa_align(x))
+
+static inline void* pa_page_align_ptr(const void *p) {
+    return (void*) (((size_t) p) & ~(PA_PAGE_SIZE-1));
+}
+#define PA_PAGE_ALIGN_PTR(x) (pa_page_align_ptr(x))
+
+static inline size_t pa_page_align(size_t l) {
+    return l & ~(PA_PAGE_SIZE-1);
+}
+#define PA_PAGE_ALIGN(x) (pa_page_align(x))
+
+#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+/* The users of PA_MIN and PA_MAX should be aware that these macros on
+ * non-GCC executed code with side effects twice. It is thus
+ * considered misuse to use code with side effects as arguments to MIN
+ * and MAX. */
+
+#ifdef __GNUC__
+#define PA_MAX(a,b)                             \
+    __extension__ ({ typeof(a) _a = (a);        \
+            typeof(b) _b = (b);                 \
+            _a > _b ? _a : _b;                  \
+        })
+#else
+#define PA_MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_MIN(a,b)                             \
+    __extension__ ({ typeof(a) _a = (a);        \
+            typeof(b) _b = (b);                 \
+            _a < _b ? _a : _b;                  \
+        })
+#else
+#define PA_MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifdef __GNUC__
+#define PA_CLAMP(x, low, high)                                          \
+    __extension__ ({ typeof(x) _x = (x);                                \
+            typeof(low) _low = (low);                                   \
+            typeof(high) _high = (high);                                \
+            ((_x > _high) ? _high : ((_x < _low) ? _low : _x));         \
+        })
+#else
+#define PA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#endif
+
+#ifdef __GNUC__
+#define PA_CLAMP_UNLIKELY(x, low, high)                                 \
+    __extension__ ({ typeof(x) _x = (x);                                \
+            typeof(low) _low = (low);                                   \
+            typeof(high) _high = (high);                                \
+            (PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \
+        })
+#else
+#define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x)))
+#endif
+
+/* We don't define a PA_CLAMP_LIKELY here, because it doesn't really
+ * make sense: we cannot know if it is more likely that the values is
+ * lower or greater than the boundaries.*/
+
+/* This type is not intended to be used in exported APIs! Use classic "int" there! */
+#ifdef HAVE_STD_BOOL
+typedef _Bool pa_bool_t;
+#else
+typedef int pa_bool_t;
+#endif
+
+#ifndef FALSE
+#define FALSE ((pa_bool_t) 0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#ifdef __GNUC__
+#define PA_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#else
+#define PA_PRETTY_FUNCTION ""
+#endif
+
+#define pa_return_if_fail(expr)                                         \
+    do {                                                                \
+        if (PA_UNLIKELY(!(expr))) {                                     \
+            pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+            return;                                                     \
+        }                                                               \
+    } while(FALSE)
+
+#define pa_return_val_if_fail(expr, val)                                \
+    do {                                                                \
+        if (PA_UNLIKELY(!(expr))) {                                     \
+            pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+            return (val);                                               \
+        }                                                               \
+    } while(FALSE)
+
+#define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL)
+
+/* An assert which guarantees side effects of x, i.e. is never
+ * optimized away */
+#define pa_assert_se(expr)                                              \
+    do {                                                                \
+        if (PA_UNLIKELY(!(expr))) {                                     \
+            pa_log_error("Assertion '%s' failed at %s:%u, function %s(). Aborting.", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+            abort();                                                    \
+        }                                                               \
+    } while (FALSE)
+
+/* An assert that may be optimized away by defining NDEBUG */
+#ifdef NDEBUG
+#define pa_assert(expr) do {} while (FALSE)
+#else
+#define pa_assert(expr) pa_assert_se(expr)
+#endif
+
+#define pa_assert_not_reached()                                         \
+    do {                                                                \
+        pa_log_error("Code should not be reached at %s:%u, function %s(). Aborting.", __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
+        abort();                                                        \
+    } while (FALSE)
+
+#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p))
+#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u))
+
+#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p))
+#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR((uint32_t) u)
+
+#define PA_PTR_TO_INT(p) ((int) PA_PTR_TO_UINT(p))
+#define PA_INT_TO_PTR(u) PA_UINT_TO_PTR((int) u)
+
+#define PA_PTR_TO_INT32(p) ((int32_t) PA_PTR_TO_UINT(p))
+#define PA_INT32_TO_PTR(u) PA_UINT_TO_PTR((int32_t) u)
+
+#ifdef OS_IS_WIN32
+#define PA_PATH_SEP "\\"
+#define PA_PATH_SEP_CHAR '\\'
+#else
+#define PA_PATH_SEP "/"
+#define PA_PATH_SEP_CHAR '/'
+#endif
+
+#ifdef __GNUC__
+
+#define PA_WARN_REFERENCE(sym, msg)                  \
+    __asm__(".section .gnu.warning." #sym);          \
+    __asm__(".asciz \"" msg "\"");                   \
+    __asm__(".previous")
+
+#else
+
+#define PA_WARN_REFERENCE(sym, msg)
+
+#endif
+
+#endif
similarity index 63%
rename from polyp/mcalign.c
rename to src/pulsecore/mcalign.c
index 0b7f0db0186326c9680f3abdfc119c929251a503..a03d5ae79b5a26aa69fefccedad64853ad9ee88a 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <assert.h>
 #include <string.h>
 
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
 #include "mcalign.h"
-#include "xmalloc.h"
 
 struct pa_mcalign {
     size_t base;
-    struct pa_memchunk leftover, current;
-    struct pa_memblock_stat *memblock_stat;
+    pa_memchunk leftover, current;
 };
 
-struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s) {
-    struct pa_mcalign *m;
-    assert(base);
+pa_mcalign *pa_mcalign_new(size_t base) {
+    pa_mcalign *m;
+    pa_assert(base);
+
+    m = pa_xnew(pa_mcalign, 1);
 
-    m = pa_xmalloc(sizeof(struct pa_mcalign));
     m->base = base;
     pa_memchunk_reset(&m->leftover);
     pa_memchunk_reset(&m->current);
-    m->memblock_stat = s;
-    
+
     return m;
 }
 
-void pa_mcalign_free(struct pa_mcalign *m) {
-    assert(m);
+void pa_mcalign_free(pa_mcalign *m) {
+    pa_assert(m);
 
     if (m->leftover.memblock)
         pa_memblock_unref(m->leftover.memblock);
 
     if (m->current.memblock)
         pa_memblock_unref(m->current.memblock);
-    
+
     pa_xfree(m);
 }
 
-void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) {
-    assert(m && c && c->memblock && c->length);
-    
+void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
+    pa_assert(m);
+    pa_assert(c);
+
+    pa_assert(c->memblock);
+    pa_assert(c->length > 0);
+
+    pa_assert(!m->current.memblock);
+
     /* Append to the leftover memory block */
     if (m->leftover.memblock) {
-        assert(!m->current.memblock);
-        
+
         /* Try to merge */
         if (m->leftover.memblock == c->memblock &&
             m->leftover.index + m->leftover.length == c->index) {
@@ -80,25 +85,31 @@ void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) {
             if (m->leftover.length >= m->base) {
                 m->current = m->leftover;
                 pa_memchunk_reset(&m->leftover);
-            } 
+            }
 
         } else {
             size_t l;
+            void *lo_data, *m_data;
 
             /* We have to copy */
-            assert(m->leftover.length < m->base);
+            pa_assert(m->leftover.length < m->base);
             l = m->base - m->leftover.length;
-            
+
             if (l > c->length)
                 l = c->length;
 
             /* Can we use the current block? */
-            pa_memchunk_make_writable(&m->leftover, m->memblock_stat, m->base);
+            pa_memchunk_make_writable(&m->leftover, m->base);
 
-            memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l);
+            lo_data = pa_memblock_acquire(m->leftover.memblock);
+            m_data = pa_memblock_acquire(c->memblock);
+            memcpy((uint8_t*) lo_data + m->leftover.index + m->leftover.length, (uint8_t*) m_data + c->index, l);
+            pa_memblock_release(m->leftover.memblock);
+            pa_memblock_release(c->memblock);
             m->leftover.length += l;
 
-            assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length);
+            pa_assert(m->leftover.length <= m->base);
+            pa_assert(m->leftover.length <= pa_memblock_get_length(m->leftover.memblock));
 
             if (c->length > l) {
                 /* Save the remainder of the memory block */
@@ -109,10 +120,8 @@ void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) {
             }
         }
     } else {
-        assert(!m->leftover.memblock && !m->current.memblock);
-
         /* Nothing to merge or copy, just store it */
-        
+
         if (c->length >= m->base)
             m->current = *c;
         else
@@ -122,12 +131,14 @@ 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) {
-    assert(m && c);
+int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
+    pa_assert(m);
+    pa_assert(c);
 
     /* First test if there's a leftover memory block available */
     if (m->leftover.memblock) {
-        assert(m->leftover.length > 0 && m->leftover.length <= m->base);
+        pa_assert(m->leftover.length > 0);
+        pa_assert(m->leftover.length <= m->base);
 
         /* The leftover memory block is not yet complete */
         if (m->leftover.length < m->base)
@@ -142,20 +153,20 @@ int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) {
             m->leftover = m->current;
             pa_memchunk_reset(&m->current);
         }
-        
+
         return 0;
     }
 
     /* Now let's see if there is other data available */
     if (m->current.memblock) {
         size_t l;
-        assert(m->current.length >= m->base);
+        pa_assert(m->current.length >= m->base);
 
         /* The length of the returned memory block */
         l = m->current.length;
         l /= m->base;
         l *= m->base;
-        assert(l > 0);
+        pa_assert(l > 0);
 
         /* Prepare the returned block */
         *c = m->current;
@@ -163,7 +174,7 @@ int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) {
         c->length = l;
 
         /* Drop that from the current memory block */
-        assert(l <= m->current.length);
+        pa_assert(l <= m->current.length);
         m->current.index += l;
         m->current.length -= l;
 
@@ -172,17 +183,36 @@ int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) {
             pa_memblock_unref(m->current.memblock);
         else {
             /* Move the raimainder to leftover */
-            assert(m->current.length < m->base && !m->leftover.memblock);
+            pa_assert(m->current.length < m->base && !m->leftover.memblock);
 
             m->leftover = m->current;
         }
 
         pa_memchunk_reset(&m->current);
-            
+
         return 0;
     }
 
     /* There's simply nothing */
     return -1;
-    
+}
+
+size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
+    pa_assert(m);
+    pa_assert(l > 0);
+
+    pa_assert(!m->current.memblock);
+
+    if (m->leftover.memblock)
+        l += m->leftover.length;
+
+    return (l/m->base)*m->base;
+}
+
+void pa_mcalign_flush(pa_mcalign *m) {
+    pa_memchunk chunk;
+    pa_assert(m);
+
+    while (pa_mcalign_pop(m, &chunk) >= 0)
+        pa_memblock_unref(chunk.memblock);
 }
similarity index 64%
rename from polyp/mcalign.h
rename to src/pulsecore/mcalign.h
index 925f438a59b06296068e3c0eef2f0a919145a4b5..e82eb0078169456535b8bae4943a0d73919b03f0 100644 (file)
@@ -1,29 +1,29 @@
 #ifndef foomcalignhfoo
 #define foomcalignhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "memblock.h"
-#include "memchunk.h"
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
 
 /* An alignment object, used for aligning memchunks to multiples of
  * the frame size. */
  * 0, the memchunk *c is valid and aligned to the granularity. Some
  * pseudocode illustrating this:
  *
- * struct pa_mcalign *a = pa_mcalign_new(4, NULL);
+ * pa_mcalign *a = pa_mcalign_new(4, NULL);
  *
  * for (;;) {
- *   struct pa_memchunk input;
+ *   pa_memchunk input;
  *
- *   ... fill input ... 
+ *   ... fill input ...
  *
  *   pa_mcalign_push(m, &input);
  *   pa_memblock_unref(input.memblock);
- * 
+ *
  *   for (;;) {
- *     struct pa_memchunk output;
+ *     pa_memchunk output;
  *
  *     if (pa_mcalign_pop(m, &output) < 0)
  *       break;
  * pa_memchunk_free(a);
  * */
 
-struct pa_mcalign;
+typedef struct pa_mcalign pa_mcalign;
 
-struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s);
-void pa_mcalign_free(struct pa_mcalign *m);
+pa_mcalign *pa_mcalign_new(size_t base);
+void pa_mcalign_free(pa_mcalign *m);
 
 /* Push a new memchunk into the aligner. The caller of this routine
  * has to free the memchunk by himself. */
-void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c);
+void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c);
 
 /* Pop a new memchunk from the aligner. Returns 0 when sucessful,
  * nonzero otherwise. */
-int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c);
+int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c);
+
+/* If we pass l bytes in now, how many bytes would we get out? */
+size_t pa_mcalign_csize(pa_mcalign *m, size_t l);
+
+/* Flush what's still stored in the aligner */
+void pa_mcalign_flush(pa_mcalign *m);
 
 #endif
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
new file mode 100644 (file)
index 0000000..c2ee136
--- /dev/null
@@ -0,0 +1,1127 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/def.h>
+
+#include <pulsecore/shm.h>
+#include <pulsecore/log.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/core-util.h>
+
+#include "memblock.h"
+
+/* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please
+ * note that the footprint is usually much smaller, since the data is
+ * stored in SHM and our OS does not commit the memory before we use
+ * it for the first time. */
+#define PA_MEMPOOL_SLOTS_MAX 1024
+#define PA_MEMPOOL_SLOT_SIZE (64*1024)
+
+#define PA_MEMEXPORT_SLOTS_MAX 128
+
+#define PA_MEMIMPORT_SLOTS_MAX 128
+#define PA_MEMIMPORT_SEGMENTS_MAX 16
+
+struct pa_memblock {
+    PA_REFCNT_DECLARE; /* the reference counter */
+    pa_mempool *pool;
+
+    pa_memblock_type_t type;
+
+    pa_bool_t read_only:1;
+    pa_bool_t is_silence:1;
+
+    pa_atomic_ptr_t data;
+    size_t length;
+
+    pa_atomic_t n_acquired;
+    pa_atomic_t please_signal;
+
+    union {
+        struct {
+            /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
+            pa_free_cb_t free_cb;
+        } user;
+
+        struct  {
+            uint32_t id;
+            pa_memimport_segment *segment;
+        } imported;
+    } per_type;
+};
+
+struct pa_memimport_segment {
+    pa_memimport *import;
+    pa_shm memory;
+    unsigned n_blocks;
+};
+
+struct pa_memimport {
+    pa_mutex *mutex;
+
+    pa_mempool *pool;
+    pa_hashmap *segments;
+    pa_hashmap *blocks;
+
+    /* Called whenever an imported memory block is no longer
+     * needed. */
+    pa_memimport_release_cb_t release_cb;
+    void *userdata;
+
+    PA_LLIST_FIELDS(pa_memimport);
+};
+
+struct memexport_slot {
+    PA_LLIST_FIELDS(struct memexport_slot);
+    pa_memblock *block;
+};
+
+struct pa_memexport {
+    pa_mutex *mutex;
+    pa_mempool *pool;
+
+    struct memexport_slot slots[PA_MEMEXPORT_SLOTS_MAX];
+
+    PA_LLIST_HEAD(struct memexport_slot, free_slots);
+    PA_LLIST_HEAD(struct memexport_slot, used_slots);
+    unsigned n_init;
+
+    /* Called whenever a client from which we imported a memory block
+       which we in turn exported to another client dies and we need to
+       revoke the memory block accordingly */
+    pa_memexport_revoke_cb_t revoke_cb;
+    void *userdata;
+
+    PA_LLIST_FIELDS(pa_memexport);
+};
+
+struct pa_mempool {
+    pa_semaphore *semaphore;
+    pa_mutex *mutex;
+
+    pa_shm memory;
+    size_t block_size;
+    unsigned n_blocks;
+
+    pa_atomic_t n_init;
+
+    PA_LLIST_HEAD(pa_memimport, imports);
+    PA_LLIST_HEAD(pa_memexport, exports);
+
+    /* A list of free slots that may be reused */
+    pa_flist *free_slots;
+
+    pa_mempool_stat stat;
+};
+
+static void segment_detach(pa_memimport_segment *seg);
+
+PA_STATIC_FLIST_DECLARE(unused_memblocks, 0, pa_xfree);
+
+/* No lock necessary */
+static void stat_add(pa_memblock*b) {
+    pa_assert(b);
+    pa_assert(b->pool);
+
+    pa_atomic_inc(&b->pool->stat.n_allocated);
+    pa_atomic_add(&b->pool->stat.allocated_size, b->length);
+
+    pa_atomic_inc(&b->pool->stat.n_accumulated);
+    pa_atomic_add(&b->pool->stat.accumulated_size, b->length);
+
+    if (b->type == PA_MEMBLOCK_IMPORTED) {
+        pa_atomic_inc(&b->pool->stat.n_imported);
+        pa_atomic_add(&b->pool->stat.imported_size, b->length);
+    }
+
+    pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
+    pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
+}
+
+/* No lock necessary */
+static void stat_remove(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(b->pool);
+
+    pa_assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0);
+    pa_assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
+
+    pa_atomic_dec(&b->pool->stat.n_allocated);
+    pa_atomic_sub(&b->pool->stat.allocated_size,  b->length);
+
+    if (b->type == PA_MEMBLOCK_IMPORTED) {
+        pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
+        pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
+
+        pa_atomic_dec(&b->pool->stat.n_imported);
+        pa_atomic_sub(&b->pool->stat.imported_size, b->length);
+    }
+
+    pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
+}
+
+static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length);
+
+/* No lock necessary */
+pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
+    pa_memblock *b;
+
+    pa_assert(p);
+    pa_assert(length);
+
+    if (!(b = pa_memblock_new_pool(p, length)))
+        b = memblock_new_appended(p, length);
+
+    return b;
+}
+
+/* No lock necessary */
+static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
+    pa_memblock *b;
+
+    pa_assert(p);
+    pa_assert(length);
+
+    /* If -1 is passed as length we choose the size for the caller. */
+
+    if (length == (size_t) -1)
+        length = p->block_size - PA_ALIGN(sizeof(pa_memblock));
+
+    b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length);
+    PA_REFCNT_INIT(b);
+    b->pool = p;
+    b->type = PA_MEMBLOCK_APPENDED;
+    b->read_only = b->is_silence = FALSE;
+    pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
+    b->length = length;
+    pa_atomic_store(&b->n_acquired, 0);
+    pa_atomic_store(&b->please_signal, 0);
+
+    stat_add(b);
+    return b;
+}
+
+/* No lock necessary */
+static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
+    struct mempool_slot *slot;
+    pa_assert(p);
+
+    if (!(slot = pa_flist_pop(p->free_slots))) {
+        int idx;
+
+        /* The free list was empty, we have to allocate a new entry */
+
+        if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks)
+            pa_atomic_dec(&p->n_init);
+        else
+            slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx));
+
+        if (!slot) {
+            pa_log_info("Pool full");
+            pa_atomic_inc(&p->stat.n_pool_full);
+            return NULL;
+        }
+    }
+
+    return slot;
+}
+
+/* No lock necessary, totally redundant anyway */
+static inline void* mempool_slot_data(struct mempool_slot *slot) {
+    return slot;
+}
+
+/* No lock necessary */
+static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) {
+    pa_assert(p);
+
+    pa_assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
+    pa_assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
+
+    return ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size;
+}
+
+/* No lock necessary */
+static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {
+    unsigned idx;
+
+    if ((idx = mempool_slot_idx(p, ptr)) == (unsigned) -1)
+        return NULL;
+
+    return (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (idx * p->block_size));
+}
+
+/* No lock necessary */
+pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
+    pa_memblock *b = NULL;
+    struct mempool_slot *slot;
+
+    pa_assert(p);
+    pa_assert(length);
+
+    /* If -1 is passed as length we choose the size for the caller: we
+     * take the largest size that fits in one of our slots. */
+
+    if (length == (size_t) -1)
+        length = pa_mempool_block_size_max(p);
+
+    if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) {
+
+        if (!(slot = mempool_allocate_slot(p)))
+            return NULL;
+
+        b = mempool_slot_data(slot);
+        b->type = PA_MEMBLOCK_POOL;
+        pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
+
+    } else if (p->block_size >= length) {
+
+        if (!(slot = mempool_allocate_slot(p)))
+            return NULL;
+
+        if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+            b = pa_xnew(pa_memblock, 1);
+
+        b->type = PA_MEMBLOCK_POOL_EXTERNAL;
+        pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
+
+    } else {
+        pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size);
+        pa_atomic_inc(&p->stat.n_too_large_for_pool);
+        return NULL;
+    }
+
+    PA_REFCNT_INIT(b);
+    b->pool = p;
+    b->read_only = b->is_silence = FALSE;
+    b->length = length;
+    pa_atomic_store(&b->n_acquired, 0);
+    pa_atomic_store(&b->please_signal, 0);
+
+    stat_add(b);
+    return b;
+}
+
+/* No lock necessary */
+pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_bool_t read_only) {
+    pa_memblock *b;
+
+    pa_assert(p);
+    pa_assert(d);
+    pa_assert(length != (size_t) -1);
+    pa_assert(length);
+
+    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+        b = pa_xnew(pa_memblock, 1);
+    PA_REFCNT_INIT(b);
+    b->pool = p;
+    b->type = PA_MEMBLOCK_FIXED;
+    b->read_only = read_only;
+    b->is_silence = FALSE;
+    pa_atomic_ptr_store(&b->data, d);
+    b->length = length;
+    pa_atomic_store(&b->n_acquired, 0);
+    pa_atomic_store(&b->please_signal, 0);
+
+    stat_add(b);
+    return b;
+}
+
+/* No lock necessary */
+pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only) {
+    pa_memblock *b;
+
+    pa_assert(p);
+    pa_assert(d);
+    pa_assert(length);
+    pa_assert(length != (size_t) -1);
+    pa_assert(free_cb);
+
+    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+        b = pa_xnew(pa_memblock, 1);
+    PA_REFCNT_INIT(b);
+    b->pool = p;
+    b->type = PA_MEMBLOCK_USER;
+    b->read_only = read_only;
+    b->is_silence = FALSE;
+    pa_atomic_ptr_store(&b->data, d);
+    b->length = length;
+    pa_atomic_store(&b->n_acquired, 0);
+    pa_atomic_store(&b->please_signal, 0);
+
+    b->per_type.user.free_cb = free_cb;
+
+    stat_add(b);
+    return b;
+}
+
+/* No lock necessary */
+pa_bool_t pa_memblock_is_read_only(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    return b->read_only && PA_REFCNT_VALUE(b) == 1;
+}
+
+/* No lock necessary */
+pa_bool_t pa_memblock_is_silence(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    return b->is_silence;
+}
+
+/* No lock necessary */
+void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    b->is_silence = v;
+}
+
+/* No lock necessary */
+pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) {
+    int r;
+    pa_assert(b);
+
+    pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
+
+    return r == 1;
+}
+
+/* No lock necessary */
+void* pa_memblock_acquire(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    pa_atomic_inc(&b->n_acquired);
+
+    return pa_atomic_ptr_load(&b->data);
+}
+
+/* No lock necessary, in corner cases locks by its own */
+void pa_memblock_release(pa_memblock *b) {
+    int r;
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    r = pa_atomic_dec(&b->n_acquired);
+    pa_assert(r >= 1);
+
+    /* Signal a waiting thread that this memblock is no longer used */
+    if (r == 1 && pa_atomic_load(&b->please_signal))
+        pa_semaphore_post(b->pool->semaphore);
+}
+
+size_t pa_memblock_get_length(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    return b->length;
+}
+
+pa_mempool* pa_memblock_get_pool(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    return b->pool;
+}
+
+/* No lock necessary */
+pa_memblock* pa_memblock_ref(pa_memblock*b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    PA_REFCNT_INC(b);
+    return b;
+}
+
+static void memblock_free(pa_memblock *b) {
+    pa_assert(b);
+
+    pa_assert(pa_atomic_load(&b->n_acquired) == 0);
+
+    stat_remove(b);
+
+    switch (b->type) {
+        case PA_MEMBLOCK_USER :
+            pa_assert(b->per_type.user.free_cb);
+            b->per_type.user.free_cb(pa_atomic_ptr_load(&b->data));
+
+            /* Fall through */
+
+        case PA_MEMBLOCK_FIXED:
+        case PA_MEMBLOCK_APPENDED :
+            if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+                pa_xfree(b);
+
+            break;
+
+        case PA_MEMBLOCK_IMPORTED : {
+            pa_memimport_segment *segment;
+            pa_memimport *import;
+
+            /* FIXME! This should be implemented lock-free */
+
+            segment = b->per_type.imported.segment;
+            pa_assert(segment);
+            import = segment->import;
+            pa_assert(import);
+
+            pa_mutex_lock(import->mutex);
+            pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id));
+            if (-- segment->n_blocks <= 0)
+                segment_detach(segment);
+
+            pa_mutex_unlock(import->mutex);
+
+            import->release_cb(import, b->per_type.imported.id, import->userdata);
+
+            if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+                pa_xfree(b);
+            break;
+        }
+
+        case PA_MEMBLOCK_POOL_EXTERNAL:
+        case PA_MEMBLOCK_POOL: {
+            struct mempool_slot *slot;
+            int call_free;
+
+            slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data));
+            pa_assert(slot);
+
+            call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL;
+
+            /* The free list dimensions should easily allow all slots
+             * to fit in, hence try harder if pushing this slot into
+             * the free list fails */
+            while (pa_flist_push(b->pool->free_slots, slot) < 0)
+                ;
+
+            if (call_free)
+                if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+                    pa_xfree(b);
+
+            break;
+        }
+
+        case PA_MEMBLOCK_TYPE_MAX:
+        default:
+            pa_assert_not_reached();
+    }
+}
+
+/* No lock necessary */
+void pa_memblock_unref(pa_memblock*b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    if (PA_REFCNT_DEC(b) > 0)
+        return;
+
+    memblock_free(b);
+}
+
+/* Self locked */
+static void memblock_wait(pa_memblock *b) {
+    pa_assert(b);
+
+    if (pa_atomic_load(&b->n_acquired) > 0) {
+        /* We need to wait until all threads gave up access to the
+         * memory block before we can go on. Unfortunately this means
+         * that we have to lock and wait here. Sniff! */
+
+        pa_atomic_inc(&b->please_signal);
+
+        while (pa_atomic_load(&b->n_acquired) > 0)
+            pa_semaphore_wait(b->pool->semaphore);
+
+        pa_atomic_dec(&b->please_signal);
+    }
+}
+
+/* No lock necessary. This function is not multiple caller safe! */
+static void memblock_make_local(pa_memblock *b) {
+    pa_assert(b);
+
+    pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
+
+    if (b->length <= b->pool->block_size) {
+        struct mempool_slot *slot;
+
+        if ((slot = mempool_allocate_slot(b->pool))) {
+            void *new_data;
+            /* We can move it into a local pool, perfect! */
+
+            new_data = mempool_slot_data(slot);
+            memcpy(new_data, pa_atomic_ptr_load(&b->data), b->length);
+            pa_atomic_ptr_store(&b->data, new_data);
+
+            b->type = PA_MEMBLOCK_POOL_EXTERNAL;
+            b->read_only = FALSE;
+
+            goto finish;
+        }
+    }
+
+    /* Humm, not enough space in the pool, so lets allocate the memory with malloc() */
+    b->per_type.user.free_cb = pa_xfree;
+    pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length));
+
+    b->type = PA_MEMBLOCK_USER;
+    b->read_only = FALSE;
+
+finish:
+    pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
+    pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
+    memblock_wait(b);
+}
+
+/* No lock necessary. This function is not multiple caller safe*/
+void pa_memblock_unref_fixed(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+    pa_assert(b->type == PA_MEMBLOCK_FIXED);
+
+    if (PA_REFCNT_VALUE(b) > 1)
+        memblock_make_local(b);
+
+    pa_memblock_unref(b);
+}
+
+/* No lock necessary. */
+pa_memblock *pa_memblock_will_need(pa_memblock *b) {
+    void *p;
+
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    p = pa_memblock_acquire(b);
+    pa_will_need(p, b->length);
+    pa_memblock_release(b);
+
+    return b;
+}
+
+/* Self-locked. This function is not multiple-caller safe */
+static void memblock_replace_import(pa_memblock *b) {
+    pa_memimport_segment *seg;
+
+    pa_assert(b);
+    pa_assert(b->type == PA_MEMBLOCK_IMPORTED);
+
+    pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
+    pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
+    pa_atomic_dec(&b->pool->stat.n_imported);
+    pa_atomic_sub(&b->pool->stat.imported_size, b->length);
+
+    seg = b->per_type.imported.segment;
+    pa_assert(seg);
+    pa_assert(seg->import);
+
+    pa_mutex_lock(seg->import->mutex);
+
+    pa_hashmap_remove(
+            seg->import->blocks,
+            PA_UINT32_TO_PTR(b->per_type.imported.id));
+
+    memblock_make_local(b);
+
+    if (-- seg->n_blocks <= 0) {
+        pa_mutex_unlock(seg->import->mutex);
+        segment_detach(seg);
+    } else
+        pa_mutex_unlock(seg->import->mutex);
+}
+
+pa_mempool* pa_mempool_new(pa_bool_t shared) {
+    pa_mempool *p;
+
+    p = pa_xnew(pa_mempool, 1);
+
+    p->mutex = pa_mutex_new(TRUE, TRUE);
+    p->semaphore = pa_semaphore_new(0);
+
+    p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE);
+    if (p->block_size < PA_PAGE_SIZE)
+        p->block_size = PA_PAGE_SIZE;
+
+    p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
+
+    if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
+        pa_xfree(p);
+        return NULL;
+    }
+
+    memset(&p->stat, 0, sizeof(p->stat));
+    pa_atomic_store(&p->n_init, 0);
+
+    PA_LLIST_HEAD_INIT(pa_memimport, p->imports);
+    PA_LLIST_HEAD_INIT(pa_memexport, p->exports);
+
+    p->free_slots = pa_flist_new(p->n_blocks*2);
+
+    return p;
+}
+
+void pa_mempool_free(pa_mempool *p) {
+    pa_assert(p);
+
+    pa_mutex_lock(p->mutex);
+
+    while (p->imports)
+        pa_memimport_free(p->imports);
+
+    while (p->exports)
+        pa_memexport_free(p->exports);
+
+    pa_mutex_unlock(p->mutex);
+
+    pa_flist_free(p->free_slots, NULL);
+
+    if (pa_atomic_load(&p->stat.n_allocated) > 0) {
+/*         raise(SIGTRAP);  */
+        pa_log_warn("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
+    }
+
+    pa_shm_free(&p->memory);
+
+    pa_mutex_free(p->mutex);
+    pa_semaphore_free(p->semaphore);
+
+    pa_xfree(p);
+}
+
+/* No lock necessary */
+const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
+    pa_assert(p);
+
+    return &p->stat;
+}
+
+/* No lock necessary */
+size_t pa_mempool_block_size_max(pa_mempool *p) {
+    pa_assert(p);
+
+    return p->block_size - PA_ALIGN(sizeof(pa_memblock));
+}
+
+/* No lock necessary */
+void pa_mempool_vacuum(pa_mempool *p) {
+    struct mempool_slot *slot;
+    pa_flist *list;
+
+    pa_assert(p);
+
+    list = pa_flist_new(p->n_blocks*2);
+
+    while ((slot = pa_flist_pop(p->free_slots)))
+        while (pa_flist_push(list, slot) < 0)
+            ;
+
+    while ((slot = pa_flist_pop(list))) {
+        pa_shm_punch(&p->memory, (uint8_t*) slot - (uint8_t*) p->memory.ptr, p->block_size);
+
+        while (pa_flist_push(p->free_slots, slot))
+            ;
+    }
+
+    pa_flist_free(list, NULL);
+}
+
+/* No lock necessary */
+int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
+    pa_assert(p);
+
+    if (!p->memory.shared)
+        return -1;
+
+    *id = p->memory.id;
+
+    return 0;
+}
+
+/* No lock necessary */
+pa_bool_t pa_mempool_is_shared(pa_mempool *p) {
+    pa_assert(p);
+
+    return !!p->memory.shared;
+}
+
+/* For recieving blocks from other nodes */
+pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata) {
+    pa_memimport *i;
+
+    pa_assert(p);
+    pa_assert(cb);
+
+    i = pa_xnew(pa_memimport, 1);
+    i->mutex = pa_mutex_new(TRUE, TRUE);
+    i->pool = p;
+    i->segments = pa_hashmap_new(NULL, NULL);
+    i->blocks = pa_hashmap_new(NULL, NULL);
+    i->release_cb = cb;
+    i->userdata = userdata;
+
+    pa_mutex_lock(p->mutex);
+    PA_LLIST_PREPEND(pa_memimport, p->imports, i);
+    pa_mutex_unlock(p->mutex);
+
+    return i;
+}
+
+static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i);
+
+/* Should be called locked */
+static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
+    pa_memimport_segment* seg;
+
+    if (pa_hashmap_size(i->segments) >= PA_MEMIMPORT_SEGMENTS_MAX)
+        return NULL;
+
+    seg = pa_xnew(pa_memimport_segment, 1);
+
+    if (pa_shm_attach_ro(&seg->memory, shm_id) < 0) {
+        pa_xfree(seg);
+        return NULL;
+    }
+
+    seg->import = i;
+    seg->n_blocks = 0;
+
+    pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(shm_id), seg);
+    return seg;
+}
+
+/* Should be called locked */
+static void segment_detach(pa_memimport_segment *seg) {
+    pa_assert(seg);
+
+    pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));
+    pa_shm_free(&seg->memory);
+    pa_xfree(seg);
+}
+
+/* Self-locked. Not multiple-caller safe */
+void pa_memimport_free(pa_memimport *i) {
+    pa_memexport *e;
+    pa_memblock *b;
+
+    pa_assert(i);
+
+    pa_mutex_lock(i->mutex);
+
+    while ((b = pa_hashmap_get_first(i->blocks)))
+        memblock_replace_import(b);
+
+    pa_assert(pa_hashmap_size(i->segments) == 0);
+
+    pa_mutex_unlock(i->mutex);
+
+    pa_mutex_lock(i->pool->mutex);
+
+    /* If we've exported this block further we need to revoke that export */
+    for (e = i->pool->exports; e; e = e->next)
+        memexport_revoke_blocks(e, i);
+
+    PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i);
+
+    pa_mutex_unlock(i->pool->mutex);
+
+    pa_hashmap_free(i->blocks, NULL, NULL);
+    pa_hashmap_free(i->segments, NULL, NULL);
+
+    pa_mutex_free(i->mutex);
+
+    pa_xfree(i);
+}
+
+/* Self-locked */
+pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id, size_t offset, size_t size) {
+    pa_memblock *b = NULL;
+    pa_memimport_segment *seg;
+
+    pa_assert(i);
+
+    pa_mutex_lock(i->mutex);
+
+    if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX)
+        goto finish;
+
+    if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id))))
+        if (!(seg = segment_attach(i, shm_id)))
+            goto finish;
+
+    if (offset+size > seg->memory.size)
+        goto finish;
+
+    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
+        b = pa_xnew(pa_memblock, 1);
+
+    PA_REFCNT_INIT(b);
+    b->pool = i->pool;
+    b->type = PA_MEMBLOCK_IMPORTED;
+    b->read_only = TRUE;
+    b->is_silence = FALSE;
+    pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
+    b->length = size;
+    pa_atomic_store(&b->n_acquired, 0);
+    pa_atomic_store(&b->please_signal, 0);
+    b->per_type.imported.id = block_id;
+    b->per_type.imported.segment = seg;
+
+    pa_hashmap_put(i->blocks, PA_UINT32_TO_PTR(block_id), b);
+
+    seg->n_blocks++;
+
+finish:
+    pa_mutex_unlock(i->mutex);
+
+    if (b)
+    stat_add(b);
+
+    return b;
+}
+
+int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {
+    pa_memblock *b;
+    int ret = 0;
+    pa_assert(i);
+
+    pa_mutex_lock(i->mutex);
+
+    if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) {
+        ret = -1;
+        goto finish;
+    }
+
+    memblock_replace_import(b);
+
+finish:
+    pa_mutex_unlock(i->mutex);
+
+    return ret;
+}
+
+/* For sending blocks to other nodes */
+pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata) {
+    pa_memexport *e;
+
+    pa_assert(p);
+    pa_assert(cb);
+
+    if (!p->memory.shared)
+        return NULL;
+
+    e = pa_xnew(pa_memexport, 1);
+    e->mutex = pa_mutex_new(TRUE, TRUE);
+    e->pool = p;
+    PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);
+    PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots);
+    e->n_init = 0;
+    e->revoke_cb = cb;
+    e->userdata = userdata;
+
+    pa_mutex_lock(p->mutex);
+    PA_LLIST_PREPEND(pa_memexport, p->exports, e);
+    pa_mutex_unlock(p->mutex);
+    return e;
+}
+
+void pa_memexport_free(pa_memexport *e) {
+    pa_assert(e);
+
+    pa_mutex_lock(e->mutex);
+    while (e->used_slots)
+        pa_memexport_process_release(e, e->used_slots - e->slots);
+    pa_mutex_unlock(e->mutex);
+
+    pa_mutex_lock(e->pool->mutex);
+    PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e);
+    pa_mutex_unlock(e->pool->mutex);
+
+    pa_mutex_free(e->mutex);
+    pa_xfree(e);
+}
+
+/* Self-locked */
+int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
+    pa_memblock *b;
+
+    pa_assert(e);
+
+    pa_mutex_lock(e->mutex);
+
+    if (id >= e->n_init)
+        goto fail;
+
+    if (!e->slots[id].block)
+        goto fail;
+
+    b = e->slots[id].block;
+    e->slots[id].block = NULL;
+
+    PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]);
+    PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]);
+
+    pa_mutex_unlock(e->mutex);
+
+/*     pa_log("Processing release for %u", id); */
+
+    pa_assert(pa_atomic_load(&e->pool->stat.n_exported) > 0);
+    pa_assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);
+
+    pa_atomic_dec(&e->pool->stat.n_exported);
+    pa_atomic_sub(&e->pool->stat.exported_size, b->length);
+
+    pa_memblock_unref(b);
+
+    return 0;
+
+fail:
+    pa_mutex_unlock(e->mutex);
+
+    return -1;
+}
+
+/* Self-locked */
+static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
+    struct memexport_slot *slot, *next;
+    pa_assert(e);
+    pa_assert(i);
+
+    pa_mutex_lock(e->mutex);
+
+    for (slot = e->used_slots; slot; slot = next) {
+        uint32_t idx;
+        next = slot->next;
+
+        if (slot->block->type != PA_MEMBLOCK_IMPORTED ||
+            slot->block->per_type.imported.segment->import != i)
+            continue;
+
+        idx = slot - e->slots;
+        e->revoke_cb(e, idx, e->userdata);
+        pa_memexport_process_release(e, idx);
+    }
+
+    pa_mutex_unlock(e->mutex);
+}
+
+/* No lock necessary */
+static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {
+    pa_memblock *n;
+
+    pa_assert(p);
+    pa_assert(b);
+
+    if (b->type == PA_MEMBLOCK_IMPORTED ||
+        b->type == PA_MEMBLOCK_POOL ||
+        b->type == PA_MEMBLOCK_POOL_EXTERNAL) {
+        pa_assert(b->pool == p);
+        return pa_memblock_ref(b);
+    }
+
+    if (!(n = pa_memblock_new_pool(p, b->length)))
+        return NULL;
+
+    memcpy(pa_atomic_ptr_load(&n->data), pa_atomic_ptr_load(&b->data), b->length);
+    return n;
+}
+
+/* Self-locked */
+int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32_t *shm_id, size_t *offset, size_t * size) {
+    pa_shm *memory;
+    struct memexport_slot *slot;
+    void *data;
+
+    pa_assert(e);
+    pa_assert(b);
+    pa_assert(block_id);
+    pa_assert(shm_id);
+    pa_assert(offset);
+    pa_assert(size);
+    pa_assert(b->pool == e->pool);
+
+    if (!(b = memblock_shared_copy(e->pool, b)))
+        return -1;
+
+    pa_mutex_lock(e->mutex);
+
+    if (e->free_slots) {
+        slot = e->free_slots;
+        PA_LLIST_REMOVE(struct memexport_slot, e->free_slots, slot);
+    } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX)
+        slot = &e->slots[e->n_init++];
+    else {
+        pa_mutex_unlock(e->mutex);
+        pa_memblock_unref(b);
+        return -1;
+    }
+
+    PA_LLIST_PREPEND(struct memexport_slot, e->used_slots, slot);
+    slot->block = b;
+    *block_id = slot - e->slots;
+
+    pa_mutex_unlock(e->mutex);
+/*     pa_log("Got block id %u", *block_id); */
+
+    data = pa_memblock_acquire(b);
+
+    if (b->type == PA_MEMBLOCK_IMPORTED) {
+        pa_assert(b->per_type.imported.segment);
+        memory = &b->per_type.imported.segment->memory;
+    } else {
+        pa_assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
+        pa_assert(b->pool);
+        memory = &b->pool->memory;
+    }
+
+    pa_assert(data >= memory->ptr);
+    pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);
+
+    *shm_id = memory->id;
+    *offset = (uint8_t*) data - (uint8_t*) memory->ptr;
+    *size = b->length;
+
+    pa_memblock_release(b);
+
+    pa_atomic_inc(&e->pool->stat.n_exported);
+    pa_atomic_add(&e->pool->stat.exported_size, b->length);
+
+    return 0;
+}
diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h
new file mode 100644 (file)
index 0000000..efe55b0
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef foopulsememblockhfoo
+#define foopulsememblockhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include <pulse/def.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/atomic.h>
+
+/* A pa_memblock is a reference counted memory block. PulseAudio
+ * passed references to pa_memblocks around instead of copying
+ * data. See pa_memchunk for a structure that describes parts of
+ * memory blocks. */
+
+/* The type of memory this block points to */
+typedef enum pa_memblock_type {
+    PA_MEMBLOCK_POOL,             /* Memory is part of the memory pool */
+    PA_MEMBLOCK_POOL_EXTERNAL,    /* Data memory is part of the memory pool but the pa_memblock structure itself not */
+    PA_MEMBLOCK_APPENDED,         /* the data is appended to the memory block */
+    PA_MEMBLOCK_USER,             /* User supplied memory, to be freed with free_cb */
+    PA_MEMBLOCK_FIXED,            /* data is a pointer to fixed memory that needs not to be freed */
+    PA_MEMBLOCK_IMPORTED,         /* Memory is imported from another process via shm */
+    PA_MEMBLOCK_TYPE_MAX
+} pa_memblock_type_t;
+
+typedef struct pa_memblock pa_memblock;
+typedef struct pa_mempool pa_mempool;
+typedef struct pa_mempool_stat pa_mempool_stat;
+typedef struct pa_memimport_segment pa_memimport_segment;
+typedef struct pa_memimport pa_memimport;
+typedef struct pa_memexport pa_memexport;
+
+typedef void (*pa_memimport_release_cb_t)(pa_memimport *i, uint32_t block_id, void *userdata);
+typedef void (*pa_memexport_revoke_cb_t)(pa_memexport *e, uint32_t block_id, void *userdata);
+
+/* Please note that updates to this structure are not locked,
+ * i.e. n_allocated might be updated at a point in time where
+ * n_accumulated is not yet. Take these values with a grain of salt,
+ * they are here for purely statistical reasons.*/
+struct pa_mempool_stat {
+    pa_atomic_t n_allocated;
+    pa_atomic_t n_accumulated;
+    pa_atomic_t n_imported;
+    pa_atomic_t n_exported;
+    pa_atomic_t allocated_size;
+    pa_atomic_t accumulated_size;
+    pa_atomic_t imported_size;
+    pa_atomic_t exported_size;
+
+    pa_atomic_t n_too_large_for_pool;
+    pa_atomic_t n_pool_full;
+
+    pa_atomic_t n_allocated_by_type[PA_MEMBLOCK_TYPE_MAX];
+    pa_atomic_t n_accumulated_by_type[PA_MEMBLOCK_TYPE_MAX];
+};
+
+/* Allocate a new memory block of type PA_MEMBLOCK_MEMPOOL or PA_MEMBLOCK_APPENDED, depending on the size */
+pa_memblock *pa_memblock_new(pa_mempool *, size_t length);
+
+/* Allocate a new memory block of type PA_MEMBLOCK_MEMPOOL. If the requested size is too large, return NULL */
+pa_memblock *pa_memblock_new_pool(pa_mempool *, size_t length);
+
+/* Allocate a new memory block of type PA_MEMBLOCK_USER */
+pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only);
+
+/* A special case of pa_memblock_new_user: take a memory buffer previously allocated with pa_xmalloc()  */
+#define pa_memblock_new_malloced(p,data,length) pa_memblock_new_user(p, data, length, pa_xfree, 0)
+
+/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */
+pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, pa_bool_t read_only);
+
+void pa_memblock_unref(pa_memblock*b);
+pa_memblock* pa_memblock_ref(pa_memblock*b);
+
+/* This special unref function has to be called by the owner of the
+memory of a static memory block when he wants to release all
+references to the memory. This causes the memory to be copied and
+converted into a pool or malloc'ed memory block. Please note that this
+function is not multiple caller safe, i.e. needs to be locked
+manually if called from more than one thread at the same time.  */
+void pa_memblock_unref_fixed(pa_memblock*b);
+
+pa_bool_t pa_memblock_is_read_only(pa_memblock *b);
+pa_bool_t pa_memblock_is_silence(pa_memblock *b);
+pa_bool_t pa_memblock_ref_is_one(pa_memblock *b);
+void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v);
+
+void* pa_memblock_acquire(pa_memblock *b);
+void pa_memblock_release(pa_memblock *b);
+size_t pa_memblock_get_length(pa_memblock *b);
+pa_mempool * pa_memblock_get_pool(pa_memblock *b);
+
+pa_memblock *pa_memblock_will_need(pa_memblock *b);
+
+/* The memory block manager */
+pa_mempool* pa_mempool_new(pa_bool_t shared);
+void pa_mempool_free(pa_mempool *p);
+const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p);
+void pa_mempool_vacuum(pa_mempool *p);
+int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id);
+pa_bool_t pa_mempool_is_shared(pa_mempool *p);
+size_t pa_mempool_block_size_max(pa_mempool *p);
+
+/* For recieving blocks from other nodes */
+pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata);
+void pa_memimport_free(pa_memimport *i);
+pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id, size_t offset, size_t size);
+int pa_memimport_process_revoke(pa_memimport *i, uint32_t block_id);
+
+/* For sending blocks to other nodes */
+pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata);
+void pa_memexport_free(pa_memexport *e);
+int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32_t *shm_id, size_t *offset, size_t *size);
+int pa_memexport_process_release(pa_memexport *e, uint32_t id);
+
+#endif
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
new file mode 100644 (file)
index 0000000..a9f28a0
--- /dev/null
@@ -0,0 +1,917 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/mcalign.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+
+#include "memblockq.h"
+
+struct list_item {
+    struct list_item *next, *prev;
+    int64_t index;
+    pa_memchunk chunk;
+};
+
+PA_STATIC_FLIST_DECLARE(list_items, 0, pa_xfree);
+
+struct pa_memblockq {
+    struct list_item *blocks, *blocks_tail;
+    struct list_item *current_read, *current_write;
+    unsigned n_blocks;
+    size_t maxlength, tlength, base, prebuf, minreq, maxrewind;
+    int64_t read_index, write_index;
+    pa_bool_t in_prebuf;
+    pa_memchunk silence;
+    pa_mcalign *mcalign;
+    int64_t missing;
+    size_t requested;
+};
+
+pa_memblockq* pa_memblockq_new(
+        int64_t idx,
+        size_t maxlength,
+        size_t tlength,
+        size_t base,
+        size_t prebuf,
+        size_t minreq,
+        size_t maxrewind,
+        pa_memchunk *silence) {
+
+    pa_memblockq* bq;
+
+    pa_assert(base > 0);
+
+    bq = pa_xnew(pa_memblockq, 1);
+    bq->blocks = bq->blocks_tail = NULL;
+    bq->current_read = bq->current_write = NULL;
+    bq->n_blocks = 0;
+
+    bq->base = base;
+    bq->read_index = bq->write_index = idx;
+
+    pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
+                 (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind);
+
+    bq->missing = bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = bq->maxrewind = 0;
+    bq->in_prebuf = TRUE;
+
+    pa_memblockq_set_maxlength(bq, maxlength);
+    pa_memblockq_set_tlength(bq, tlength);
+    pa_memblockq_set_prebuf(bq, prebuf);
+    pa_memblockq_set_minreq(bq, minreq);
+    pa_memblockq_set_maxrewind(bq, maxrewind);
+
+    pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
+                 (unsigned long) bq->maxlength, (unsigned long) bq->tlength, (unsigned long) bq->base, (unsigned long) bq->prebuf, (unsigned long) bq->minreq, (unsigned long) bq->maxrewind);
+
+    if (silence) {
+        bq->silence = *silence;
+        pa_memblock_ref(bq->silence.memblock);
+    } else
+        pa_memchunk_reset(&bq->silence);
+
+    bq->mcalign = pa_mcalign_new(bq->base);
+
+    return bq;
+}
+
+void pa_memblockq_free(pa_memblockq* bq) {
+    pa_assert(bq);
+
+    pa_memblockq_flush(bq);
+
+    if (bq->silence.memblock)
+        pa_memblock_unref(bq->silence.memblock);
+
+    if (bq->mcalign)
+        pa_mcalign_free(bq->mcalign);
+
+    pa_xfree(bq);
+}
+
+static void fix_current_read(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (PA_UNLIKELY(!bq->blocks)) {
+        bq->current_read = NULL;
+        return;
+    }
+
+    if (PA_UNLIKELY(!bq->current_read))
+        bq->current_read = bq->blocks;
+
+    /* Scan left */
+    while (PA_UNLIKELY(bq->current_read->index > bq->read_index))
+
+        if (bq->current_read->prev)
+            bq->current_read = bq->current_read->prev;
+        else
+            break;
+
+    /* Scan right */
+    while (PA_LIKELY(bq->current_read != NULL) && PA_UNLIKELY(bq->current_read->index + (int64_t) bq->current_read->chunk.length <= bq->read_index))
+        bq->current_read = bq->current_read->next;
+
+    /* At this point current_read will either point at or left of the
+       next block to play. It may be NULL in case everything in
+       the queue was already played */
+}
+
+static void fix_current_write(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (PA_UNLIKELY(!bq->blocks)) {
+        bq->current_write = NULL;
+        return;
+    }
+
+    if (PA_UNLIKELY(!bq->current_write))
+        bq->current_write = bq->blocks_tail;
+
+    /* Scan right */
+    while (PA_UNLIKELY(bq->current_write->index + (int64_t) bq->current_write->chunk.length <= bq->write_index))
+
+        if (bq->current_write->next)
+            bq->current_write = bq->current_write->next;
+        else
+            break;
+
+    /* Scan left */
+    while (PA_LIKELY(bq->current_write != NULL) && PA_UNLIKELY(bq->current_write->index > bq->write_index))
+        bq->current_write = bq->current_write->prev;
+
+    /* At this point current_write will either point at or right of
+       the next block to write data to. It may be NULL in case
+       everything in the queue is still to be played */
+}
+
+static void drop_block(pa_memblockq *bq, struct list_item *q) {
+    pa_assert(bq);
+    pa_assert(q);
+
+    pa_assert(bq->n_blocks >= 1);
+
+    if (q->prev)
+        q->prev->next = q->next;
+    else {
+        pa_assert(bq->blocks == q);
+        bq->blocks = q->next;
+    }
+
+    if (q->next)
+        q->next->prev = q->prev;
+    else {
+        pa_assert(bq->blocks_tail == q);
+        bq->blocks_tail = q->prev;
+    }
+
+    if (bq->current_write == q)
+        bq->current_write = q->prev;
+
+    if (bq->current_read == q)
+        bq->current_read = q->next;
+
+    pa_memblock_unref(q->chunk.memblock);
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(list_items), q) < 0)
+        pa_xfree(q);
+
+    bq->n_blocks--;
+}
+
+static void drop_backlog(pa_memblockq *bq) {
+    int64_t boundary;
+    pa_assert(bq);
+
+    boundary = bq->read_index - bq->maxrewind;
+
+    while (bq->blocks && (bq->blocks->index + (int64_t) bq->blocks->chunk.length <= boundary))
+        drop_block(bq, bq->blocks);
+}
+
+static pa_bool_t can_push(pa_memblockq *bq, size_t l) {
+    int64_t end;
+
+    pa_assert(bq);
+
+    if (bq->read_index > bq->write_index) {
+        size_t d =  bq->read_index - bq->write_index;
+
+        if (l > d)
+            l -= d;
+        else
+            return TRUE;
+    }
+
+    end = bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->write_index;
+
+    /* Make sure that the list doesn't get too long */
+    if (bq->write_index + (int64_t) l > end)
+        if (bq->write_index + l - bq->read_index > bq->maxlength)
+            return FALSE;
+
+    return TRUE;
+}
+
+int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
+    struct list_item *q, *n;
+    pa_memchunk chunk;
+    int64_t old, delta;
+
+    pa_assert(bq);
+    pa_assert(uchunk);
+    pa_assert(uchunk->memblock);
+    pa_assert(uchunk->length > 0);
+    pa_assert(uchunk->index + uchunk->length <= pa_memblock_get_length(uchunk->memblock));
+
+    if (uchunk->length % bq->base)
+        return -1;
+
+    if (!can_push(bq, uchunk->length))
+        return -1;
+
+    old = bq->write_index;
+    chunk = *uchunk;
+
+    fix_current_write(bq);
+    q = bq->current_write;
+
+    /* First we advance the q pointer right of where we want to
+     * write to */
+
+    if (q) {
+        while (bq->write_index + (int64_t) chunk.length > q->index)
+            if (q->next)
+                q = q->next;
+            else
+                break;
+    }
+
+    if (!q)
+        q = bq->blocks_tail;
+
+    /* We go from back to front to look for the right place to add
+     * this new entry. Drop data we will overwrite on the way */
+
+    while (q) {
+
+        if (bq->write_index >= q->index + (int64_t) q->chunk.length)
+            /* We found the entry where we need to place the new entry immediately after */
+            break;
+        else if (bq->write_index + (int64_t) chunk.length <= q->index) {
+            /* This entry isn't touched at all, let's skip it */
+            q = q->prev;
+        } else if (bq->write_index <= q->index &&
+            bq->write_index + chunk.length >= q->index + q->chunk.length) {
+
+            /* This entry is fully replaced by the new entry, so let's drop it */
+
+            struct list_item *p;
+            p = q;
+            q = q->prev;
+            drop_block(bq, p);
+        } else if (bq->write_index >= q->index) {
+            /* The write index points into this memblock, so let's
+             * truncate or split it */
+
+            if (bq->write_index + chunk.length < q->index + q->chunk.length) {
+
+                /* We need to save the end of this memchunk */
+                struct list_item *p;
+                size_t d;
+
+                /* Create a new list entry for the end of thie memchunk */
+                if (!(p = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
+                    p = pa_xnew(struct list_item, 1);
+
+                p->chunk = q->chunk;
+                pa_memblock_ref(p->chunk.memblock);
+
+                /* Calculate offset */
+                d = bq->write_index + chunk.length - q->index;
+                pa_assert(d > 0);
+
+                /* Drop it from the new entry */
+                p->index = q->index + d;
+                p->chunk.length -= d;
+
+                /* Add it to the list */
+                p->prev = q;
+                if ((p->next = q->next))
+                    q->next->prev = p;
+                else
+                    bq->blocks_tail = p;
+                q->next = p;
+
+                bq->n_blocks++;
+            }
+
+            /* Truncate the chunk */
+            if (!(q->chunk.length = bq->write_index - q->index)) {
+                struct list_item *p;
+                p = q;
+                q = q->prev;
+                drop_block(bq, p);
+            }
+
+            /* We had to truncate this block, hence we're now at the right position */
+            break;
+        } else {
+            size_t d;
+
+            pa_assert(bq->write_index + (int64_t)chunk.length > q->index &&
+                   bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length &&
+                   bq->write_index < q->index);
+
+            /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
+
+            d = bq->write_index + chunk.length - q->index;
+            q->index += d;
+            q->chunk.index += d;
+            q->chunk.length -= d;
+
+            q = q->prev;
+        }
+    }
+
+    if (q) {
+        pa_assert(bq->write_index >=  q->index + (int64_t)q->chunk.length);
+        pa_assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index));
+
+        /* Try to merge memory blocks */
+
+        if (q->chunk.memblock == chunk.memblock &&
+            q->chunk.index + (int64_t)q->chunk.length == chunk.index &&
+            bq->write_index == q->index + (int64_t)q->chunk.length) {
+
+            q->chunk.length += chunk.length;
+            bq->write_index += chunk.length;
+            goto finish;
+        }
+    } else
+        pa_assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index));
+
+    if (!(n = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
+        n = pa_xnew(struct list_item, 1);
+
+    n->chunk = chunk;
+    pa_memblock_ref(n->chunk.memblock);
+    n->index = bq->write_index;
+    bq->write_index += n->chunk.length;
+
+    n->next = q ? q->next : bq->blocks;
+    n->prev = q;
+
+    if (n->next)
+        n->next->prev = n;
+    else
+        bq->blocks_tail = n;
+
+    if (n->prev)
+        n->prev->next = n;
+    else
+        bq->blocks = n;
+
+    bq->n_blocks++;
+
+finish:
+
+    delta = bq->write_index - old;
+
+    if (delta >= (int64_t) bq->requested) {
+        delta -= bq->requested;
+        bq->requested = 0;
+    } else {
+        bq->requested -= delta;
+        delta = 0;
+    }
+
+    bq->missing -= delta;
+
+    return 0;
+}
+
+pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (bq->in_prebuf)
+        return pa_memblockq_get_length(bq) < bq->prebuf;
+    else
+        return bq->prebuf > 0 && bq->read_index >= bq->write_index;
+}
+
+static pa_bool_t update_prebuf(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (bq->in_prebuf) {
+
+        if (pa_memblockq_get_length(bq) < bq->prebuf)
+            return TRUE;
+
+        bq->in_prebuf = FALSE;
+        return FALSE;
+    } else {
+
+        if (bq->prebuf > 0 && bq->read_index >= bq->write_index) {
+            bq->in_prebuf = TRUE;
+            return TRUE;
+        }
+
+        return FALSE;
+    }
+}
+
+int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
+    int64_t d;
+    pa_assert(bq);
+    pa_assert(chunk);
+
+    /* We need to pre-buffer */
+    if (update_prebuf(bq))
+        return -1;
+
+    fix_current_read(bq);
+
+    /* Do we need to spit out silence? */
+    if (!bq->current_read || bq->current_read->index > bq->read_index) {
+
+        size_t length;
+
+        /* How much silence shall we return? */
+        if (bq->current_read)
+            length = bq->current_read->index - bq->read_index;
+        else if (bq->write_index > bq->read_index)
+            length = (size_t) (bq->write_index - bq->read_index);
+        else
+            length = 0;
+
+        /* We need to return silence, since no data is yet available */
+        if (bq->silence.memblock) {
+            *chunk = bq->silence;
+            pa_memblock_ref(chunk->memblock);
+
+            if (length > 0 && length < chunk->length)
+                chunk->length = length;
+
+        } else {
+
+            /* If the memblockq is empty, return -1, otherwise return
+             * the time to sleep */
+            if (length <= 0)
+                return -1;
+
+            chunk->memblock = NULL;
+            chunk->length = length;
+        }
+
+        chunk->index = 0;
+        return 0;
+    }
+
+    /* Ok, let's pass real data to the caller */
+    *chunk = bq->current_read->chunk;
+    pa_memblock_ref(chunk->memblock);
+
+    pa_assert(bq->read_index >= bq->current_read->index);
+    d = bq->read_index - bq->current_read->index;
+    chunk->index += d;
+    chunk->length -= d;
+
+    return 0;
+}
+
+void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
+    int64_t old, delta;
+    pa_assert(bq);
+    pa_assert(length % bq->base == 0);
+
+    old = bq->read_index;
+
+    while (length > 0) {
+
+        /* Do not drop any data when we are in prebuffering mode */
+        if (update_prebuf(bq))
+            break;
+
+        fix_current_read(bq);
+
+        if (bq->current_read) {
+            int64_t p, d;
+
+            /* We go through this piece by piece to make sure we don't
+             * drop more than allowed by prebuf */
+
+            p = bq->current_read->index + bq->current_read->chunk.length;
+            pa_assert(p >= bq->read_index);
+            d = p - bq->read_index;
+
+            if (d > (int64_t) length)
+                d = length;
+
+            bq->read_index += d;
+            length -= d;
+
+        } else {
+
+            /* The list is empty, there's nothing we could drop */
+            bq->read_index += length;
+            break;
+        }
+    }
+
+    drop_backlog(bq);
+
+    delta = bq->read_index - old;
+    bq->missing += delta;
+}
+
+void pa_memblockq_rewind(pa_memblockq *bq, size_t length) {
+    pa_assert(bq);
+    pa_assert(length % bq->base == 0);
+
+    /* This is kind of the inverse of pa_memblockq_drop() */
+
+    bq->read_index -= length;
+    bq->missing -= length;
+}
+
+pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (pa_memblockq_prebuf_active(bq))
+        return FALSE;
+
+    if (pa_memblockq_get_length(bq) <= 0)
+        return FALSE;
+
+    return TRUE;
+}
+
+size_t pa_memblockq_get_length(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (bq->write_index <= bq->read_index)
+        return 0;
+
+    return (size_t) (bq->write_index - bq->read_index);
+}
+
+size_t pa_memblockq_missing(pa_memblockq *bq) {
+    size_t l;
+    pa_assert(bq);
+
+    if ((l = pa_memblockq_get_length(bq)) >= bq->tlength)
+        return 0;
+
+    l = bq->tlength - l;
+
+    return l >= bq->minreq ? l : 0;
+}
+
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
+    int64_t old, delta;
+    pa_assert(bq);
+
+    old = bq->write_index;
+
+    switch (seek) {
+        case PA_SEEK_RELATIVE:
+            bq->write_index += offset;
+            break;
+        case PA_SEEK_ABSOLUTE:
+            bq->write_index = offset;
+            break;
+        case PA_SEEK_RELATIVE_ON_READ:
+            bq->write_index = bq->read_index + offset;
+            break;
+        case PA_SEEK_RELATIVE_END:
+            bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->read_index) + offset;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    drop_backlog(bq);
+
+    delta = bq->write_index - old;
+
+    if (delta >= (int64_t) bq->requested) {
+        delta -= bq->requested;
+        bq->requested = 0;
+    } else if (delta >= 0) {
+        bq->requested -= delta;
+        delta = 0;
+    }
+
+    bq->missing -= delta;
+}
+
+void pa_memblockq_flush(pa_memblockq *bq) {
+    int64_t old, delta;
+    pa_assert(bq);
+
+    pa_memblockq_silence(bq);
+
+    old = bq->write_index;
+    bq->write_index = bq->read_index;
+
+    pa_memblockq_prebuf_force(bq);
+
+    delta = bq->write_index - old;
+
+    if (delta >= (int64_t) bq->requested) {
+        delta -= bq->requested;
+        bq->requested = 0;
+    } else if (delta >= 0) {
+        bq->requested -= delta;
+        delta = 0;
+    }
+
+    bq->missing -= delta;
+}
+
+size_t pa_memblockq_get_tlength(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->tlength;
+}
+
+size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->minreq;
+}
+
+int64_t pa_memblockq_get_read_index(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->read_index;
+}
+
+int64_t pa_memblockq_get_write_index(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->write_index;
+}
+
+int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
+    pa_memchunk rchunk;
+
+    pa_assert(bq);
+    pa_assert(chunk);
+
+    if (bq->base == 1)
+        return pa_memblockq_push(bq, chunk);
+
+    if (!can_push(bq, pa_mcalign_csize(bq->mcalign, chunk->length)))
+        return -1;
+
+    pa_mcalign_push(bq->mcalign, chunk);
+
+    while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
+        int r;
+        r = pa_memblockq_push(bq, &rchunk);
+        pa_memblock_unref(rchunk.memblock);
+
+        if (r < 0) {
+            pa_mcalign_flush(bq->mcalign);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    bq->in_prebuf = FALSE;
+}
+
+void pa_memblockq_prebuf_force(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    if (bq->prebuf > 0)
+        bq->in_prebuf = TRUE;
+}
+
+size_t pa_memblockq_get_maxlength(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->maxlength;
+}
+
+size_t pa_memblockq_get_prebuf(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->prebuf;
+}
+
+size_t pa_memblockq_pop_missing(pa_memblockq *bq) {
+    size_t l;
+
+    pa_assert(bq);
+
+/*     pa_log("pop: %lli", bq->missing); */
+
+    if (bq->missing <= 0)
+        return 0;
+
+    l = (size_t) bq->missing;
+    bq->missing = 0;
+    bq->requested += l;
+
+    return l;
+}
+
+void pa_memblockq_set_maxlength(pa_memblockq *bq, size_t maxlength) {
+    pa_assert(bq);
+
+    bq->maxlength = ((maxlength+bq->base-1)/bq->base)*bq->base;
+
+    if (bq->maxlength < bq->base)
+        bq->maxlength = bq->base;
+
+    if (bq->tlength > bq->maxlength)
+        pa_memblockq_set_tlength(bq, bq->maxlength);
+
+    if (bq->prebuf > bq->maxlength)
+        pa_memblockq_set_prebuf(bq, bq->maxlength);
+}
+
+void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
+    size_t old_tlength;
+    pa_assert(bq);
+
+    if (tlength <= 0)
+        tlength = bq->maxlength;
+
+    old_tlength = bq->tlength;
+    bq->tlength = ((tlength+bq->base-1)/bq->base)*bq->base;
+
+    if (bq->tlength > bq->maxlength)
+        bq->tlength = bq->maxlength;
+
+    if (bq->prebuf > bq->tlength)
+        pa_memblockq_set_prebuf(bq, bq->tlength);
+
+    if (bq->minreq > bq->tlength)
+        pa_memblockq_set_minreq(bq, bq->tlength);
+
+    bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
+}
+
+void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
+    pa_assert(bq);
+
+    if (prebuf == (size_t) -1)
+        prebuf = bq->tlength;
+
+    bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base;
+
+    if (prebuf > 0 && bq->prebuf < bq->base)
+        bq->prebuf = bq->base;
+
+    if (bq->prebuf > bq->tlength)
+        bq->prebuf = bq->tlength;
+
+    if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf)
+        bq->in_prebuf = FALSE;
+
+    if (bq->minreq > bq->prebuf)
+        pa_memblockq_set_minreq(bq, bq->prebuf);
+}
+
+void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
+    pa_assert(bq);
+
+    bq->minreq = (minreq/bq->base)*bq->base;
+
+    if (bq->minreq > bq->tlength)
+        bq->minreq = bq->tlength;
+
+    if (bq->minreq > bq->prebuf)
+        bq->minreq = bq->prebuf;
+
+    if (bq->minreq < bq->base)
+        bq->minreq = bq->base;
+}
+
+void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
+    pa_assert(bq);
+
+    bq->maxrewind = (maxrewind/bq->base)*bq->base;
+}
+
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
+
+    pa_assert(bq);
+    pa_assert(source);
+
+    pa_memblockq_prebuf_disable(bq);
+
+    for (;;) {
+        pa_memchunk chunk;
+
+        if (pa_memblockq_peek(source, &chunk) < 0)
+            return 0;
+
+        pa_assert(chunk.length > 0);
+
+        if (chunk.memblock) {
+
+            if (pa_memblockq_push_align(bq, &chunk) < 0) {
+                pa_memblock_unref(chunk.memblock);
+                return -1;
+            }
+
+            pa_memblock_unref(chunk.memblock);
+        } else
+            pa_memblockq_seek(bq, chunk.length, PA_SEEK_RELATIVE);
+
+        pa_memblockq_drop(bq, chunk.length);
+    }
+}
+
+void pa_memblockq_willneed(pa_memblockq *bq) {
+    struct list_item *q;
+
+    pa_assert(bq);
+
+    fix_current_read(bq);
+
+    for (q = bq->current_read; q; q = q->next)
+        pa_memchunk_will_need(&q->chunk);
+}
+
+void pa_memblockq_set_silence(pa_memblockq *bq, pa_memchunk *silence) {
+    pa_assert(bq);
+
+    if (bq->silence.memblock)
+        pa_memblock_unref(bq->silence.memblock);
+
+    if (silence) {
+        bq->silence = *silence;
+        pa_memblock_ref(bq->silence.memblock);
+    } else
+        pa_memchunk_reset(&bq->silence);
+}
+
+pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return !bq->blocks;
+}
+
+void pa_memblockq_silence(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    while (bq->blocks)
+        drop_block(bq, bq->blocks);
+
+    pa_assert(bq->n_blocks == 0);
+}
+
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->n_blocks;
+}
+
+size_t pa_memblockq_get_base(pa_memblockq *bq) {
+    pa_assert(bq);
+
+    return bq->base;
+}
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
new file mode 100644 (file)
index 0000000..81f7cbb
--- /dev/null
@@ -0,0 +1,172 @@
+#ifndef foomemblockqhfoo
+#define foomemblockqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
+#include <pulse/def.h>
+
+/* A memblockq is a queue of pa_memchunks (yepp, the name is not
+ * perfect). It is similar to the ring buffers used by most other
+ * audio software. In contrast to a ring buffer this memblockq data
+ * type doesn't need to copy any data around, it just maintains
+ * references to reference counted memory blocks. */
+
+typedef struct pa_memblockq pa_memblockq;
+
+
+/* Parameters:
+
+   - idx:       start value for both read and write index
+
+   - maxlength: maximum length of queue. If more data is pushed into
+                the queue, the operation will fail. Must not be 0.
+
+   - tlength:   the target length of the queue. Pass 0 for the default.
+
+   - base:      a base value for all metrics. Only multiples of this value
+                are popped from the queue or should be pushed into
+                it. Must not be 0.
+
+   - prebuf:    If the queue runs empty wait until this many bytes are in
+                queue again before passing the first byte out. If set
+                to 0 pa_memblockq_pop() will return a silence memblock
+                if no data is in the queue and will never fail. Pass
+                (size_t) -1 for the default.
+
+   - minreq:    pa_memblockq_missing() will only return values greater
+                than this value. Pass 0 for the default.
+
+   - maxrewind: how many bytes of history to keep in the queue
+
+   - silence:   return this memchunk when reading unitialized data
+*/
+pa_memblockq* pa_memblockq_new(
+        int64_t idx,
+        size_t maxlength,
+        size_t tlength,
+        size_t base,
+        size_t prebuf,
+        size_t minreq,
+        size_t maxrewind,
+        pa_memchunk *silence);
+
+void pa_memblockq_free(pa_memblockq*bq);
+
+/* Push a new memory chunk into the queue.  */
+int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk);
+
+/* Push a new memory chunk into the queue, but filter it through a
+ * pa_mcalign object. Don't mix this with pa_memblockq_seek() unless
+ * you know what you do. */
+int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk);
+
+/* Return a copy of the next memory chunk in the queue. It is not
+ * removed from the queue. There are two reasons this function might
+ * fail: 1. prebuffering is active, 2. queue is empty and no silence
+ * memblock was passed at initialization. If the queue is not empty,
+ * but we're currently at a hole in the queue and no silence memblock
+ * was passed we return the length of the hole in chunk->length. */
+int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
+
+/* Drop the specified bytes from the queue. */
+void pa_memblockq_drop(pa_memblockq *bq, size_t length);
+
+/* Test if the pa_memblockq is currently readable, that is, more data than base */
+pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq);
+
+/* Return the length of the queue in bytes */
+size_t pa_memblockq_get_length(pa_memblockq *bq);
+
+/* Return how many bytes are missing in queue to the specified fill amount */
+size_t pa_memblockq_missing(pa_memblockq *bq);
+
+/* Return the number of bytes that are missing since the last call to
+ * this function, reset the internal counter to 0. */
+size_t pa_memblockq_pop_missing(pa_memblockq *bq);
+
+/* Directly moves the data from the source memblockq into bq */
+int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source);
+
+/* Returns the minimal request value */
+size_t pa_memblockq_get_minreq(pa_memblockq *bq);
+
+/* Manipulate the write pointer */
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek);
+
+/* Set the queue to silence, set write index to read index */
+void pa_memblockq_flush(pa_memblockq *bq);
+
+/* Get Target length */
+size_t pa_memblockq_get_tlength(pa_memblockq *bq);
+
+/* Return the current read index */
+int64_t pa_memblockq_get_read_index(pa_memblockq *bq);
+
+/* Return the current write index */
+int64_t pa_memblockq_get_write_index(pa_memblockq *bq);
+
+/* Rewind the read index. If the history is shorter than the specified length we'll point to silence afterwards. */
+void pa_memblockq_rewind(pa_memblockq *bq, size_t length);
+
+/* Ignore prebuf for now */
+void pa_memblockq_prebuf_disable(pa_memblockq *bq);
+
+/* Force prebuf */
+void pa_memblockq_prebuf_force(pa_memblockq *bq);
+
+/* Return the maximum length of the queue in bytes */
+size_t pa_memblockq_get_maxlength(pa_memblockq *bq);
+
+/* Return the prebuffer length in bytes */
+size_t pa_memblockq_get_prebuf(pa_memblockq *bq);
+
+/* Change metrics. Always call in order. */
+void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength); /* might modify tlength, prebuf, minreq too */
+void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength); /* might modify minreq, too */
+void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf); /* might modify minreq, too */
+void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq);
+void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t rewind); /* Set the maximum history size */
+void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence);
+
+/* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */
+void pa_memblockq_willneed(pa_memblockq *bq);
+
+/* Check whether the memblockq is completely empty, i.e. no data
+ * neither left nor right of the read pointer, and hence no buffered
+ * data for the future nor data in the backlog. */
+pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq);
+
+void pa_memblockq_silence(pa_memblockq *bq);
+
+/* Check whether we currently are in prebuf state */
+pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq);
+
+unsigned pa_memblockq_get_nblocks(pa_memblockq *bq);
+
+size_t pa_memblockq_get_base(pa_memblockq *bq);
+
+#endif
diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c
new file mode 100644 (file)
index 0000000..0bbf859
--- /dev/null
@@ -0,0 +1,112 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <string.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "memchunk.h"
+
+pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
+    pa_memblock *n;
+    size_t l;
+    void *tdata, *sdata;
+
+    pa_assert(c);
+    pa_assert(c->memblock);
+
+    if (pa_memblock_ref_is_one(c->memblock) &&
+        !pa_memblock_is_read_only(c->memblock) &&
+        pa_memblock_get_length(c->memblock) >= c->index+min)
+        return c;
+
+    l = PA_MAX(c->length, min);
+
+    n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l);
+
+    sdata = pa_memblock_acquire(c->memblock);
+    tdata = pa_memblock_acquire(n);
+
+    memcpy(tdata, (uint8_t*) sdata + c->index, c->length);
+
+    pa_memblock_release(c->memblock);
+    pa_memblock_release(n);
+
+    pa_memblock_unref(c->memblock);
+
+    c->memblock = n;
+    c->index = 0;
+
+    return c;
+}
+
+pa_memchunk* pa_memchunk_reset(pa_memchunk *c) {
+    pa_assert(c);
+
+    memset(c, 0, sizeof(*c));
+
+    return c;
+}
+
+pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c) {
+    void *p;
+
+    pa_assert(c);
+    pa_assert(c->memblock);
+
+    /* A version of pa_memblock_will_need() that works on memchunks
+     * instead of memblocks */
+
+    p = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
+    pa_will_need(p, c->length);
+    pa_memblock_release(c->memblock);
+
+    return (pa_memchunk*) c;
+}
+
+pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src) {
+    void *p, *q;
+
+    pa_assert(dst);
+    pa_assert(src);
+    pa_assert(dst->length == src->length);
+
+    p = pa_memblock_acquire(dst->memblock);
+    q = pa_memblock_acquire(src->memblock);
+
+    memmove((uint8_t*) p + dst->index,
+            (uint8_t*) q + src->index,
+            dst->length);
+
+    pa_memblock_release(dst->memblock);
+    pa_memblock_release(src->memblock);
+
+    return dst;
+}
diff --git a/src/pulsecore/memchunk.h b/src/pulsecore/memchunk.h
new file mode 100644 (file)
index 0000000..9458f4f
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef foomemchunkhfoo
+#define foomemchunkhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/memblock.h>
+
+/* A memchunk describes a part of a memblock. In contrast to the memblock, a
+ * memchunk is not allocated dynamically or reference counted, instead
+ * it is usually stored on the stack and copied around */
+
+typedef struct pa_memchunk {
+    pa_memblock *memblock;
+    size_t index, length;
+} pa_memchunk;
+
+/* Make a memchunk writable, i.e. make sure that the caller may have
+ * exclusive access to the memblock and it is not read_only. If needed
+ * the memblock in the structure is replaced by a copy. If min is not
+ * 0 it is made sure that the returned memblock is at least of the
+ * specified size, i.e. is enlarged if necessary. */
+pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min);
+
+/* Invalidate a memchunk. This does not free the cotaining memblock,
+ * but sets all members to zero. */
+pa_memchunk* pa_memchunk_reset(pa_memchunk *c);
+
+/* Map a memory chunk back into memory if it was swapped out */
+pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c);
+
+/* Copy the data in the src memchunk to the dst memchunk */
+pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src);
+
+#endif
similarity index 63%
rename from polyp/modargs.c
rename to src/pulsecore/modargs.c
index 9437d839bd066322416cb02cb4ac8fc51f0c8f9b..5f5902c9da28be631e94f256bcef1946ed418e0c 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #endif
 
 #include <ctype.h>
-#include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include "hashmap.h"
-#include "modargs.h"
-#include "idxset.h"
-#include "sample-util.h"
-#include "namereg.h"
-#include "sink.h"
-#include "source.h"
-#include "xmalloc.h"
-#include "util.h"
+#include <pulse/xmalloc.h>
 
-struct pa_modargs;
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "modargs.h"
 
 struct entry {
     char *key, *value;
 };
 
-static int add_key_value(struct pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) {
+static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) {
     struct entry *e;
-    assert(map && key && value);
+
+    pa_assert(map);
+    pa_assert(key);
+    pa_assert(value);
+
+    if (pa_hashmap_get(map, key)) {
+        pa_xfree(key);
+        pa_xfree(value);
+        return -1;
+    }
 
     if (valid_keys) {
         const char*const* v;
@@ -60,25 +69,33 @@ static int add_key_value(struct pa_hashmap *map, char *key, char *value, const c
             return -1;
         }
     }
-    
-    e = pa_xmalloc(sizeof(struct entry));
+
+    e = pa_xnew(struct entry, 1);
     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;
+pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
+    pa_hashmap *map = NULL;
 
     map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    assert(map);
 
     if (args) {
-        enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state;
+        enum {
+            WHITESPACE,
+            KEY,
+            VALUE_START,
+            VALUE_SIMPLE,
+            VALUE_DOUBLE_QUOTES,
+            VALUE_TICKS
+        } state;
+
         const char *p, *key, *value;
         size_t key_len = 0, value_len = 0;
-        
+
         key = value = NULL;
         state = WHITESPACE;
         for (p = args; *p; p++) {
@@ -95,6 +112,8 @@ struct pa_modargs *pa_modargs_new(const char *args, const char* const* valid_key
                 case KEY:
                     if (*p == '=')
                         state = VALUE_START;
+                    else if (isspace(*p))
+                        goto fail;
                     else
                         key_len++;
                     break;
@@ -154,32 +173,32 @@ struct pa_modargs *pa_modargs_new(const char *args, const char* const* valid_key
             goto fail;
     }
 
-    return (struct pa_modargs*) map;
+    return (pa_modargs*) map;
 
 fail:
 
     if (map)
-        pa_modargs_free((struct pa_modargs*) map);
-                      
+        pa_modargs_free((pa_modargs*) map);
+
     return NULL;
 }
 
-
-static void free_func(void *p, void*userdata) {
+static void free_func(void *p, PA_GCC_UNUSED void*userdata) {
     struct entry *e = p;
-    assert(e);
+    pa_assert(e);
+
     pa_xfree(e->key);
     pa_xfree(e->value);
     pa_xfree(e);
 }
 
-void pa_modargs_free(struct pa_modargs*ma) {
-    struct pa_hashmap *map = (struct pa_hashmap*) ma;
+void pa_modargs_free(pa_modargs*ma) {
+    pa_hashmap *map = (pa_hashmap*) ma;
     pa_hashmap_free(map, free_func, NULL);
 }
 
-const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def) {
-    struct pa_hashmap *map = (struct pa_hashmap*) ma;
+const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def) {
+    pa_hashmap *map = (pa_hashmap*) ma;
     struct entry*e;
 
     if (!(e = pa_hashmap_get(map, key)))
@@ -188,9 +207,12 @@ const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const c
     return e->value;
 }
 
-int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value) {
+int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {
     const char *v;
-    assert(ma && key && value);
+
+    pa_assert(ma);
+    pa_assert(key);
+    pa_assert(value);
 
     if (!(v = pa_modargs_get_value(ma, key, NULL)))
         return 0;
@@ -201,23 +223,29 @@ int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *v
     return 0;
 }
 
-int pa_modargs_get_value_s32(struct pa_modargs *ma, const char *key, int32_t *value) {
+int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {
     const char *v;
-    assert(ma && key && value);
+
+    pa_assert(ma);
+    pa_assert(key);
+    pa_assert(value);
 
     if (!(v = pa_modargs_get_value(ma, key, NULL)))
         return 0;
 
     if (pa_atoi(v, value) < 0)
         return -1;
-            
+
     return 0;
 }
 
-int pa_modargs_get_value_boolean(struct pa_modargs *ma, const char *key, int *value) {
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, pa_bool_t *value) {
     const char *v;
     int r;
-    assert(ma && key && value);
+
+    pa_assert(ma);
+    pa_assert(key);
+    pa_assert(value);
 
     if (!(v = pa_modargs_get_value(ma, key, NULL)))
         return 0;
@@ -232,14 +260,14 @@ int pa_modargs_get_value_boolean(struct pa_modargs *ma, const char *key, int *va
     return 0;
 }
 
-int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *rss) {
+int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
     const char *format;
     uint32_t channels;
-    struct pa_sample_spec ss;
-    assert(ma && rss);
+    pa_sample_spec ss;
+
+    pa_assert(ma);
+    pa_assert(rss);
 
-/*    DEBUG_TRAP;*/
-    
     ss = *rss;
     if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0)
         return -1;
@@ -257,6 +285,54 @@ int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *rss
         return -1;
 
     *rss = ss;
-    
+
+    return 0;
+}
+
+int pa_modargs_get_channel_map(pa_modargs *ma, const char *name, pa_channel_map *rmap) {
+    pa_channel_map map;
+    const char *cm;
+
+    pa_assert(ma);
+    pa_assert(rmap);
+
+    map = *rmap;
+
+    if ((cm = pa_modargs_get_value(ma, name ? name : "channel_map", NULL)))
+        if (!pa_channel_map_parse(&map, cm))
+            return -1;
+
+    if (!pa_channel_map_valid(&map))
+        return -1;
+
+    *rmap = map;
+    return 0;
+}
+
+int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *rss, pa_channel_map *rmap, pa_channel_map_def_t def) {
+    pa_sample_spec ss;
+    pa_channel_map map;
+
+    pa_assert(ma);
+    pa_assert(rss);
+    pa_assert(rmap);
+
+    ss = *rss;
+
+    if (pa_modargs_get_sample_spec(ma, &ss) < 0)
+        return -1;
+
+    if (!pa_channel_map_init_auto(&map, ss.channels, def))
+        map.channels = 0;
+
+    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0)
+        return -1;
+
+    if (map.channels != ss.channels)
+        return -1;
+
+    *rmap = map;
+    *rss = ss;
+
     return 0;
 }
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
new file mode 100644 (file)
index 0000000..23766cf
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef foomodargshfoo
+#define foomodargshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_modargs pa_modargs;
+
+/* A generic parser for module arguments */
+
+/* Parse the string args. The NULL-terminated array keys contains all valid arguments. */
+pa_modargs *pa_modargs_new(const char *args, const char* const keys[]);
+void pa_modargs_free(pa_modargs*ma);
+
+/* Return the module argument for the specified name as a string. If
+ * the argument was not specified, return def instead.*/
+const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def);
+
+/* Return a module argument as unsigned 32bit value in *value */
+int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value);
+int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value);
+int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, pa_bool_t *value);
+
+/* Return sample spec data from the three arguments "rate", "format" and "channels" */
+int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss);
+
+/* Return channel map data from the argument "channel_map" if name is NULL, otherwise read from the specified argument */
+int pa_modargs_get_channel_map(pa_modargs *ma, const char *name, pa_channel_map *map);
+
+/* Combination of pa_modargs_get_sample_spec() and
+pa_modargs_get_channel_map(). Not always suitable, since this routine
+initializes the map parameter based on the channels field of the ss
+structure if no channel_map is found, using pa_channel_map_init_auto() */
+
+int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *ss, pa_channel_map *map, pa_channel_map_def_t def);
+
+#endif
diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c
new file mode 100644 (file)
index 0000000..ac4ca88
--- /dev/null
@@ -0,0 +1,95 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ltdl.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/ltdl-helper.h>
+
+#include "modinfo.h"
+
+#define PA_SYMBOL_AUTHOR "pa__get_author"
+#define PA_SYMBOL_DESCRIPTION "pa__get_description"
+#define PA_SYMBOL_USAGE "pa__get_usage"
+#define PA_SYMBOL_VERSION "pa__get_version"
+#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
+
+pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name) {
+    pa_modinfo *i;
+    const char* (*func)(void);
+    pa_bool_t (*func2) (void);
+
+    pa_assert(dl);
+
+    i = pa_xnew0(pa_modinfo, 1);
+
+    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_AUTHOR)))
+        i->author = pa_xstrdup(func());
+
+    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_DESCRIPTION)))
+        i->description = pa_xstrdup(func());
+
+    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_USAGE)))
+        i->usage = pa_xstrdup(func());
+
+    if ((func = (const char* (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_VERSION)))
+        i->version = pa_xstrdup(func());
+
+    if ((func2 = (pa_bool_t (*)(void)) pa_load_sym(dl, module_name, PA_SYMBOL_LOAD_ONCE)))
+        i->load_once = func2();
+
+    return i;
+}
+
+pa_modinfo *pa_modinfo_get_by_name(const char *name) {
+    lt_dlhandle dl;
+    pa_modinfo *i;
+
+    pa_assert(name);
+
+    if (!(dl = lt_dlopenext(name))) {
+        pa_log("Failed to open module \"%s\": %s", name, lt_dlerror());
+        return NULL;
+    }
+
+    i = pa_modinfo_get_by_handle(dl, name);
+    lt_dlclose(dl);
+
+    return i;
+}
+
+void pa_modinfo_free(pa_modinfo *i) {
+    pa_assert(i);
+
+    pa_xfree(i->author);
+    pa_xfree(i->description);
+    pa_xfree(i->usage);
+    pa_xfree(i->version);
+    pa_xfree(i);
+}
similarity index 56%
rename from polyp/modinfo.h
rename to src/pulsecore/modinfo.h
index 9da9dc4ebc0772726505b635aa5893ba97d5914f..605637c4f878aadef7e377998bab55c11a1bd2f2 100644 (file)
@@ -1,43 +1,45 @@
 #ifndef foomodinfohfoo
 #define foomodinfohfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-/* Some functions for reading module meta data from Polypaudio modules */
+/* Some functions for reading module meta data from PulseAudio modules */
+#include <pulsecore/macro.h>
 
-struct pa_modinfo {
+typedef struct pa_modinfo {
     char *author;
     char *description;
     char *usage;
     char *version;
-};
+    pa_bool_t load_once;
+} pa_modinfo;
 
 /* Read meta data from an libtool handle */
-struct pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl);
+pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl, const char *module_name);
 
 /* Read meta data from a module file */
-struct pa_modinfo *pa_modinfo_get_by_name(const char *name);
+pa_modinfo *pa_modinfo_get_by_name(const char *name);
 
 /* Free meta data */
-void pa_modinfo_free(struct pa_modinfo *i);
+void pa_modinfo_free(pa_modinfo *i);
 
 #endif
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
new file mode 100644 (file)
index 0000000..f1eeb76
--- /dev/null
@@ -0,0 +1,306 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/ltdl-helper.h>
+
+#include "module.h"
+
+#define PA_SYMBOL_INIT "pa__init"
+#define PA_SYMBOL_DONE "pa__done"
+#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
+
+#define UNLOAD_POLL_TIME 2
+
+static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
+    pa_core *c = PA_CORE(userdata);
+    struct timeval ntv;
+
+    pa_core_assert_ref(c);
+    pa_assert(c->mainloop == m);
+    pa_assert(c->module_auto_unload_event == e);
+
+    pa_module_unload_unused(c);
+
+    pa_gettimeofday(&ntv);
+    pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);
+    m->time_restart(e, &ntv);
+}
+
+pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
+    pa_module *m = NULL;
+    pa_bool_t (*load_once)(void);
+
+    pa_assert(c);
+    pa_assert(name);
+
+    if (c->disallow_module_loading)
+        goto fail;
+
+    m = pa_xnew(pa_module, 1);
+    m->name = pa_xstrdup(name);
+    m->argument = pa_xstrdup(argument);
+
+    if (!(m->dl = lt_dlopenext(name))) {
+        pa_log("Failed to open module \"%s\": %s", name, lt_dlerror());
+        goto fail;
+    }
+
+    if ((load_once = (pa_bool_t (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) {
+
+        if (load_once() && c->modules) {
+            pa_module *i;
+            uint32_t idx;
+            /* OK, the module only wants to be loaded once, let's make sure it is */
+
+            for (i = pa_idxset_first(c->modules, &idx); i; i = pa_idxset_next(c->modules, &idx)) {
+                if (strcmp(name, i->name) == 0) {
+                    pa_log("Module \"%s\" should be loaded once at most. Refusing to load.", name);
+                    goto fail;
+                }
+            }
+        }
+    }
+
+    if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) {
+        pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
+        goto fail;
+    }
+
+    m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE);
+    m->userdata = NULL;
+    m->core = c;
+    m->n_used = -1;
+    m->auto_unload = FALSE;
+    m->unload_requested = FALSE;
+
+    if (m->init(m) < 0) {
+        pa_log_error("Failed to load  module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
+        goto fail;
+    }
+
+    if (!c->modules)
+        c->modules = pa_idxset_new(NULL, NULL);
+
+    if (m->auto_unload && !c->module_auto_unload_event) {
+        struct timeval ntv;
+        pa_gettimeofday(&ntv);
+        pa_timeval_add(&ntv, UNLOAD_POLL_TIME*1000000);
+        c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
+    }
+
+    pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
+    pa_assert(m->index != PA_IDXSET_INVALID);
+
+    pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : "");
+
+    pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
+
+    return m;
+
+fail:
+
+    if (m) {
+        pa_xfree(m->argument);
+        pa_xfree(m->name);
+
+        if (m->dl)
+            lt_dlclose(m->dl);
+
+        pa_xfree(m);
+    }
+
+    return NULL;
+}
+
+static void pa_module_free(pa_module *m) {
+    pa_assert(m);
+    pa_assert(m->core);
+
+    if (m->core->disallow_module_loading)
+        return;
+
+    pa_log_info("Unloading \"%s\" (index: #%u).", m->name, m->index);
+
+    if (m->done)
+        m->done(m);
+
+    lt_dlclose(m->dl);
+
+    pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index);
+
+    pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
+
+    pa_xfree(m->name);
+    pa_xfree(m->argument);
+    pa_xfree(m);
+}
+
+void pa_module_unload(pa_core *c, pa_module *m) {
+    pa_assert(c);
+    pa_assert(m);
+
+    pa_assert(c->modules);
+    if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))
+        return;
+
+    pa_module_free(m);
+}
+
+void pa_module_unload_by_index(pa_core *c, uint32_t idx) {
+    pa_module *m;
+    pa_assert(c);
+    pa_assert(idx != PA_IDXSET_INVALID);
+
+    if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
+        return;
+
+    pa_module_free(m);
+}
+
+static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {
+    pa_module *m = p;
+    pa_assert(m);
+    pa_module_free(m);
+}
+
+void pa_module_unload_all(pa_core *c) {
+    pa_module *m;
+
+    pa_assert(c);
+
+    if (!c->modules)
+        return;
+
+    while ((m = pa_idxset_first(c->modules, NULL)))
+        pa_module_unload(c, m);
+
+    pa_idxset_free(c->modules, free_callback, NULL);
+    c->modules = NULL;
+
+    if (c->module_auto_unload_event) {
+        c->mainloop->time_free(c->module_auto_unload_event);
+        c->module_auto_unload_event = NULL;
+    }
+
+    if (c->module_defer_unload_event) {
+        c->mainloop->defer_free(c->module_defer_unload_event);
+        c->module_defer_unload_event = NULL;
+    }
+}
+
+static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) {
+    pa_module *m = p;
+    time_t *now = userdata;
+
+    pa_assert(m);
+    pa_assert(del);
+    pa_assert(now);
+
+    if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) {
+        pa_module_free(m);
+        *del = 1;
+    }
+
+    return 0;
+}
+
+void pa_module_unload_unused(pa_core *c) {
+    time_t now;
+    pa_assert(c);
+
+    if (!c->modules)
+        return;
+
+    time(&now);
+    pa_idxset_foreach(c->modules, unused_callback, &now);
+}
+
+static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) {
+    pa_module *m = p;
+    pa_assert(m);
+
+    if (m->unload_requested) {
+        pa_module_free(m);
+        *del = 1;
+    }
+
+    return 0;
+}
+
+static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
+    pa_core *core = PA_CORE(userdata);
+
+    pa_core_assert_ref(core);
+    api->defer_enable(e, 0);
+
+    if (!core->modules)
+        return;
+
+    pa_idxset_foreach(core->modules, unload_callback, NULL);
+}
+
+void pa_module_unload_request(pa_module *m) {
+    pa_assert(m);
+
+    m->unload_requested = TRUE;
+
+    if (!m->core->module_defer_unload_event)
+        m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
+
+    m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
+}
+
+void pa_module_set_used(pa_module*m, int used) {
+    pa_assert(m);
+
+    if (m->n_used != used)
+        pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
+
+    if (used == 0 && m->n_used > 0)
+        time(&m->last_used_time);
+
+    m->n_used = used;
+}
+
+pa_modinfo *pa_module_get_info(pa_module *m) {
+    pa_assert(m);
+
+    return pa_modinfo_get_by_handle(m->dl, m->name);
+}
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
new file mode 100644 (file)
index 0000000..ec582f2
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef foomodulehfoo
+#define foomodulehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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>
+
+typedef struct pa_module pa_module;
+
+#include <pulsecore/core.h>
+#include <pulsecore/modinfo.h>
+
+struct pa_module {
+    pa_core *core;
+    char *name, *argument;
+    uint32_t index;
+
+    lt_dlhandle dl;
+
+    int (*init)(pa_module*m);
+    void (*done)(pa_module*m);
+
+    void *userdata;
+
+    int n_used;
+    pa_bool_t auto_unload;
+    time_t last_used_time;
+
+    pa_bool_t unload_requested;
+};
+
+pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
+void pa_module_unload(pa_core *c, pa_module *m);
+void pa_module_unload_by_index(pa_core *c, uint32_t idx);
+
+void pa_module_unload_all(pa_core *c);
+void pa_module_unload_unused(pa_core *c);
+
+void pa_module_unload_request(pa_module *m);
+
+void pa_module_set_used(pa_module*m, int used);
+
+#define PA_MODULE_AUTHOR(s)                                     \
+    const char *pa__get_author(void) { return s; }              \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_DESCRIPTION(s)                                \
+    const char *pa__get_description(void) { return s; }         \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_USAGE(s)                                      \
+    const char *pa__get_usage(void) { return s; }               \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_VERSION(s)                                    \
+    const char * pa__get_version(void) { return s; }            \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_MODULE_LOAD_ONCE(b)                                  \
+    pa_bool_t pa__load_once(void) { return b; }                 \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+pa_modinfo *pa_module_get_info(pa_module *m);
+
+#endif
diff --git a/src/pulsecore/msgobject.c b/src/pulsecore/msgobject.c
new file mode 100644 (file)
index 0000000..81417ea
--- /dev/null
@@ -0,0 +1,47 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 "msgobject.h"
+
+PA_DEFINE_CHECK_TYPE(pa_msgobject, pa_object);
+
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
+    pa_msgobject *o;
+
+    pa_assert(size > sizeof(pa_msgobject));
+    pa_assert(type_name);
+
+    if (!check_type)
+        check_type = pa_msgobject_check_type;
+
+    pa_assert(check_type(type_name));
+    pa_assert(check_type("pa_object"));
+    pa_assert(check_type("pa_msgobject"));
+
+    o = PA_MSGOBJECT(pa_object_new_internal(size, type_name, check_type));
+    o->process_msg = NULL;
+    return o;
+}
diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h
new file mode 100644 (file)
index 0000000..1a43fa3
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef foopulsemsgobjecthfoo
+#define foopulsemsgobjecthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/object.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_msgobject pa_msgobject;
+
+struct pa_msgobject {
+    pa_object parent;
+    int (*process_msg)(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+};
+
+pa_msgobject *pa_msgobject_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
+
+int pa_msgobject_check_type(const char *type);
+
+#define pa_msgobject_new(type) ((type*) pa_msgobject_new_internal(sizeof(type), #type, type##_check_type))
+#define pa_msgobject_free ((void (*) (pa_msgobject* o)) pa_object_free)
+
+#define PA_MSGOBJECT(o) pa_msgobject_cast(o)
+
+PA_DECLARE_CLASS(pa_msgobject);
+
+#endif
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
new file mode 100644 (file)
index 0000000..35465b7
--- /dev/null
@@ -0,0 +1,140 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pthread.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+
+#include "mutex.h"
+
+struct pa_mutex {
+    pthread_mutex_t mutex;
+};
+
+struct pa_cond {
+    pthread_cond_t cond;
+};
+
+pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
+    pa_mutex *m;
+    pthread_mutexattr_t attr;
+    int r;
+
+    pa_assert_se(pthread_mutexattr_init(&attr) == 0);
+
+    if (recursive)
+        pa_assert_se(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) == 0);
+
+#ifdef HAVE_PTHREAD_PRIO_INHERIT
+    if (inherit_priority)
+        pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT) == 0);
+#endif
+
+    m = pa_xnew(pa_mutex, 1);
+
+#ifndef HAVE_PTHREAD_PRIO_INHERIT
+    pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
+
+#else
+    if ((r = pthread_mutex_init(&m->mutex, &attr))) {
+
+        /* If this failed, then this was probably due to non-available
+         * priority inheritance. In which case we fall back to normal
+         * mutexes. */
+        pa_assert(r == ENOTSUP && inherit_priority);
+
+        pa_assert_se(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_NONE) == 0);
+        pa_assert_se(pthread_mutex_init(&m->mutex, &attr) == 0);
+    }
+#endif
+
+    return m;
+}
+
+void pa_mutex_free(pa_mutex *m) {
+    pa_assert(m);
+
+    pa_assert_se(pthread_mutex_destroy(&m->mutex) == 0);
+    pa_xfree(m);
+}
+
+void pa_mutex_lock(pa_mutex *m) {
+    pa_assert(m);
+
+    pa_assert_se(pthread_mutex_lock(&m->mutex) == 0);
+}
+
+pa_bool_t pa_mutex_try_lock(pa_mutex *m) {
+    int r;
+    pa_assert(m);
+
+    if ((r = pthread_mutex_trylock(&m->mutex)) != 0) {
+        pa_assert(r == EBUSY);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+void pa_mutex_unlock(pa_mutex *m) {
+    pa_assert(m);
+
+    pa_assert_se(pthread_mutex_unlock(&m->mutex) == 0);
+}
+
+pa_cond *pa_cond_new(void) {
+    pa_cond *c;
+
+    c = pa_xnew(pa_cond, 1);
+    pa_assert_se(pthread_cond_init(&c->cond, NULL) == 0);
+    return c;
+}
+
+void pa_cond_free(pa_cond *c) {
+    pa_assert(c);
+
+    pa_assert_se(pthread_cond_destroy(&c->cond) == 0);
+    pa_xfree(c);
+}
+
+void pa_cond_signal(pa_cond *c, int broadcast) {
+    pa_assert(c);
+
+    if (broadcast)
+        pa_assert_se(pthread_cond_broadcast(&c->cond) == 0);
+    else
+        pa_assert_se(pthread_cond_signal(&c->cond) == 0);
+}
+
+int pa_cond_wait(pa_cond *c, pa_mutex *m) {
+    pa_assert(c);
+    pa_assert(m);
+
+    return pthread_cond_wait(&c->cond, &m->mutex);
+}
diff --git a/src/pulsecore/mutex-win32.c b/src/pulsecore/mutex-win32.c
new file mode 100644 (file)
index 0000000..5e884e7
--- /dev/null
@@ -0,0 +1,133 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <windows.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/hashmap.h>
+
+#include "mutex.h"
+
+struct pa_mutex {
+    CRITICAL_SECTION mutex;
+};
+
+struct pa_cond {
+    pa_hashmap *wait_events;
+};
+
+pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority) {
+    pa_mutex *m;
+
+    m = pa_xnew(pa_mutex, 1);
+
+    InitializeCriticalSection(&m->mutex);
+
+    return m;
+}
+
+void pa_mutex_free(pa_mutex *m) {
+    assert(m);
+
+    DeleteCriticalSection(&m->mutex);
+    pa_xfree(m);
+}
+
+void pa_mutex_lock(pa_mutex *m) {
+    assert(m);
+
+    EnterCriticalSection(&m->mutex);
+}
+
+void pa_mutex_unlock(pa_mutex *m) {
+    assert(m);
+
+    LeaveCriticalSection(&m->mutex);
+}
+
+pa_cond *pa_cond_new(void) {
+    pa_cond *c;
+
+    c = pa_xnew(pa_cond, 1);
+    c->wait_events = pa_hashmap_new(NULL, NULL);
+    assert(c->wait_events);
+
+    return c;
+}
+
+void pa_cond_free(pa_cond *c) {
+    assert(c);
+
+    pa_hashmap_free(c->wait_events, NULL, NULL);
+    pa_xfree(c);
+}
+
+void pa_cond_signal(pa_cond *c, int broadcast) {
+    assert(c);
+
+    if (pa_hashmap_size(c->wait_events) == 0)
+        return;
+
+    if (broadcast)
+        SetEvent(pa_hashmap_get_first(c->wait_events));
+    else {
+        void *iter;
+        const void *key;
+        HANDLE event;
+
+        iter = NULL;
+        while (1) {
+            pa_hashmap_iterate(c->wait_events, &iter, &key);
+            if (key == NULL)
+                break;
+            event = (HANDLE)pa_hashmap_get(c->wait_events, key);
+            SetEvent(event);
+        }
+    }
+}
+
+int pa_cond_wait(pa_cond *c, pa_mutex *m) {
+    HANDLE event;
+
+    assert(c);
+    assert(m);
+
+    event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    assert(event);
+
+    pa_hashmap_put(c->wait_events, event, event);
+
+    pa_mutex_unlock(m);
+
+    WaitForSingleObject(event, INFINITE);
+
+    pa_mutex_lock(m);
+
+    pa_hashmap_remove(c->wait_events, event);
+
+    CloseHandle(event);
+
+    return 0;
+}
diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h
new file mode 100644 (file)
index 0000000..36e1d63
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef foopulsemutexhfoo
+#define foopulsemutexhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/macro.h>
+
+typedef struct pa_mutex pa_mutex;
+
+/* Please think twice before enabling priority inheritance. This is no
+ * magic wand! Use it only when the potentially priorized threads are
+ * good candidates for it. Don't use this blindly! Also, note that
+ * only very few operating systems actually implement this, hence this
+ * is merely a hint. */
+pa_mutex* pa_mutex_new(pa_bool_t recursive, pa_bool_t inherit_priority);
+
+void pa_mutex_free(pa_mutex *m);
+void pa_mutex_lock(pa_mutex *m);
+pa_bool_t pa_mutex_try_lock(pa_mutex *m);
+void pa_mutex_unlock(pa_mutex *m);
+
+typedef struct pa_cond pa_cond;
+
+pa_cond *pa_cond_new(void);
+void pa_cond_free(pa_cond *c);
+void pa_cond_signal(pa_cond *c, int broadcast);
+int pa_cond_wait(pa_cond *c, pa_mutex *m);
+
+#endif
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
new file mode 100644 (file)
index 0000000..cc18ada
--- /dev/null
@@ -0,0 +1,298 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+#include <stdio.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/autoload.h>
+#include <pulsecore/source.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "namereg.h"
+
+struct namereg_entry {
+    pa_namereg_type_t type;
+    char *name;
+    void *data;
+};
+
+static int is_valid_char(char c) {
+    return
+        (c >= 'a' && c <= 'z') ||
+        (c >= 'A' && c <= 'Z') ||
+        (c >= '0' && c <= '9') ||
+        c == '.' ||
+        c == '_';
+}
+
+pa_bool_t pa_namereg_is_valid_name(const char *name) {
+    const char *c;
+
+    if (*name == 0)
+        return FALSE;
+
+    for (c = name; *c && (c-name < PA_NAME_MAX); c++)
+        if (!is_valid_char(*c))
+            return FALSE;
+
+    if (*c)
+        return FALSE;
+
+    return TRUE;
+}
+
+static char* cleanup_name(const char *name) {
+    const char *a;
+    char *b, *n;
+
+    if (*name == 0)
+        return NULL;
+
+    n = pa_xnew(char, strlen(name)+1);
+
+    for (a = name, b = n; *a && (a-name < PA_NAME_MAX); a++, b++)
+        *b = is_valid_char(*a) ? *a : '_';
+
+    *b = 0;
+
+    return n;
+}
+
+void pa_namereg_free(pa_core *c) {
+    pa_assert(c);
+
+    if (!c->namereg)
+        return;
+
+    pa_assert(pa_hashmap_size(c->namereg) == 0);
+    pa_hashmap_free(c->namereg, NULL, NULL);
+}
+
+const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail) {
+    struct namereg_entry *e;
+    char *n = NULL;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(data);
+
+    if (!*name)
+        return NULL;
+
+    if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) &&
+        !pa_namereg_is_valid_name(name) ) {
+
+        if (fail)
+            return NULL;
+
+        if (!(name = n = cleanup_name(name)))
+            return NULL;
+    }
+
+    if (!c->namereg)
+        c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    if ((e = pa_hashmap_get(c->namereg, name)) && fail) {
+        pa_xfree(n);
+        return NULL;
+    }
+
+    if (e) {
+        unsigned i;
+        size_t l = strlen(name);
+        char *k;
+
+        if (l+4 > PA_NAME_MAX) {
+            pa_xfree(n);
+            return NULL;
+        }
+
+        k = pa_xnew(char, l+4);
+
+        for (i = 2; i <= 99; i++) {
+            pa_snprintf(k, l+4, "%s.%u", name, i);
+
+            if (!(e = pa_hashmap_get(c->namereg, k)))
+                break;
+        }
+
+        if (e) {
+            pa_xfree(n);
+            pa_xfree(k);
+            return NULL;
+        }
+
+        pa_xfree(n);
+        n = k;
+    }
+
+    e = pa_xnew(struct namereg_entry, 1);
+    e->type = type;
+    e->name = n ? n : pa_xstrdup(name);
+    e->data = data;
+
+    pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0);
+
+    return e->name;
+}
+
+void pa_namereg_unregister(pa_core *c, const char *name) {
+    struct namereg_entry *e;
+
+    pa_assert(c);
+    pa_assert(name);
+
+    pa_assert_se(e = pa_hashmap_remove(c->namereg, name));
+
+    pa_xfree(e->name);
+    pa_xfree(e);
+}
+
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload) {
+    struct namereg_entry *e;
+    uint32_t idx;
+    pa_assert(c);
+
+    if (!name) {
+
+        if (type == PA_NAMEREG_SOURCE)
+            name = pa_namereg_get_default_source_name(c);
+        else if (type == PA_NAMEREG_SINK)
+            name = pa_namereg_get_default_sink_name(c);
+
+    } else if (strcmp(name, "@DEFAULT_SINK@") == 0) {
+        if (type == PA_NAMEREG_SINK)
+               name = pa_namereg_get_default_sink_name(c);
+
+    } else if (strcmp(name, "@DEFAULT_SOURCE@") == 0) {
+        if (type == PA_NAMEREG_SOURCE)
+            name = pa_namereg_get_default_source_name(c);
+
+    } else if (strcmp(name, "@DEFAULT_MONITOR@") == 0) {
+        if (type == PA_NAMEREG_SOURCE) {
+            pa_sink *k;
+
+            if ((k = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, autoload)))
+                return k->monitor_source;
+        }
+    } else if (*name == '@')
+        name = NULL;
+
+    if (!name)
+        return NULL;
+
+    if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
+        if (e->type == type)
+            return e->data;
+
+    if (pa_atou(name, &idx) < 0) {
+
+        if (autoload) {
+            pa_autoload_request(c, name, type);
+
+            if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
+                if (e->type == type)
+                    return e->data;
+        }
+
+        return NULL;
+    }
+
+    if (type == PA_NAMEREG_SINK)
+        return pa_idxset_get_by_index(c->sinks, idx);
+    else if (type == PA_NAMEREG_SOURCE)
+        return pa_idxset_get_by_index(c->sources, idx);
+    else if (type == PA_NAMEREG_SAMPLE && c->scache)
+        return pa_idxset_get_by_index(c->scache, idx);
+
+    return NULL;
+}
+
+int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) {
+    char **s;
+
+    pa_assert(c);
+    pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
+
+    s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name;
+
+    if (!name && !*s)
+        return 0;
+
+    if (name && *s && !strcmp(name, *s))
+        return 0;
+
+    if (!pa_namereg_is_valid_name(name))
+        return -1;
+
+    pa_xfree(*s);
+    *s = pa_xstrdup(name);
+    pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+
+    return 0;
+}
+
+const char *pa_namereg_get_default_sink_name(pa_core *c) {
+    pa_sink *s;
+
+    pa_assert(c);
+
+    if (c->default_sink_name)
+        return c->default_sink_name;
+
+    if ((s = pa_idxset_first(c->sinks, NULL)))
+        pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK);
+
+    return c->default_sink_name;
+}
+
+const char *pa_namereg_get_default_source_name(pa_core *c) {
+    pa_source *s;
+    uint32_t idx;
+
+    pa_assert(c);
+
+    if (c->default_source_name)
+        return c->default_source_name;
+
+    for (s = pa_idxset_first(c->sources, &idx); s; s = pa_idxset_next(c->sources, &idx))
+        if (!s->monitor_of) {
+            pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
+            break;
+        }
+
+    if (!c->default_source_name)
+        if ((s = pa_idxset_first(c->sources, NULL)))
+            pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
+
+    return c->default_source_name;
+}
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
new file mode 100644 (file)
index 0000000..af0153e
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef foonamereghfoo
+#define foonamereghfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
+
+#define PA_NAME_MAX 128
+
+typedef enum pa_namereg_type {
+    PA_NAMEREG_SINK,
+    PA_NAMEREG_SOURCE,
+    PA_NAMEREG_SAMPLE
+} pa_namereg_type_t;
+
+void pa_namereg_free(pa_core *c);
+
+const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail);
+void pa_namereg_unregister(pa_core *c, const char *name);
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload);
+int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
+
+const char *pa_namereg_get_default_sink_name(pa_core *c);
+const char *pa_namereg_get_default_source_name(pa_core *c);
+
+pa_bool_t pa_namereg_is_valid_name(const char *name);
+
+#endif
similarity index 57%
rename from polyp/native-common.h
rename to src/pulsecore/native-common.h
index 892629e8a2b9c8df6d4d390dfb3b146cf90c7f37..809d6c75c6fc81c99b60139344fecd02b17ec499 100644 (file)
@@ -1,49 +1,50 @@
 #ifndef foonativecommonhfoo
 #define foonativecommonhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "cdecl.h"
-#include "polyplib-def.h"
+#include <pulse/cdecl.h>
+#include <pulse/def.h>
 
 PA_C_DECL_BEGIN
 
 enum {
+    /* Generic commands */
     PA_COMMAND_ERROR,
     PA_COMMAND_TIMEOUT, /* pseudo command */
     PA_COMMAND_REPLY,
-    PA_COMMAND_CREATE_PLAYBACK_STREAM,
+
+    /* CLIENT->SERVER */
+    PA_COMMAND_CREATE_PLAYBACK_STREAM,        /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
     PA_COMMAND_DELETE_PLAYBACK_STREAM,
-    PA_COMMAND_CREATE_RECORD_STREAM,
+    PA_COMMAND_CREATE_RECORD_STREAM,          /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
     PA_COMMAND_DELETE_RECORD_STREAM,
     PA_COMMAND_EXIT,
-    PA_COMMAND_REQUEST,
     PA_COMMAND_AUTH,
     PA_COMMAND_SET_CLIENT_NAME,
     PA_COMMAND_LOOKUP_SINK,
     PA_COMMAND_LOOKUP_SOURCE,
     PA_COMMAND_DRAIN_PLAYBACK_STREAM,
-    PA_COMMAND_PLAYBACK_STREAM_KILLED,
-    PA_COMMAND_RECORD_STREAM_KILLED,
     PA_COMMAND_STAT,
     PA_COMMAND_GET_PLAYBACK_LATENCY,
     PA_COMMAND_CREATE_UPLOAD_STREAM,
@@ -51,6 +52,7 @@ enum {
     PA_COMMAND_FINISH_UPLOAD_STREAM,
     PA_COMMAND_PLAY_SAMPLE,
     PA_COMMAND_REMOVE_SAMPLE,
+
     PA_COMMAND_GET_SERVER_INFO,
     PA_COMMAND_GET_SINK_INFO,
     PA_COMMAND_GET_SINK_INFO_LIST,
@@ -60,41 +62,97 @@ enum {
     PA_COMMAND_GET_MODULE_INFO_LIST,
     PA_COMMAND_GET_CLIENT_INFO,
     PA_COMMAND_GET_CLIENT_INFO_LIST,
-    PA_COMMAND_GET_SINK_INPUT_INFO,
-    PA_COMMAND_GET_SINK_INPUT_INFO_LIST,
+    PA_COMMAND_GET_SINK_INPUT_INFO,          /* Payload changed in v11 (0.9.7) */
+    PA_COMMAND_GET_SINK_INPUT_INFO_LIST,     /* Payload changed in v11 (0.9.7) */
     PA_COMMAND_GET_SOURCE_OUTPUT_INFO,
     PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST,
     PA_COMMAND_GET_SAMPLE_INFO,
     PA_COMMAND_GET_SAMPLE_INFO_LIST,
     PA_COMMAND_SUBSCRIBE,
-    PA_COMMAND_SUBSCRIBE_EVENT,
+
     PA_COMMAND_SET_SINK_VOLUME,
     PA_COMMAND_SET_SINK_INPUT_VOLUME,
+    PA_COMMAND_SET_SOURCE_VOLUME,
+
+    PA_COMMAND_SET_SINK_MUTE,
+    PA_COMMAND_SET_SOURCE_MUTE,
+
     PA_COMMAND_CORK_PLAYBACK_STREAM,
     PA_COMMAND_FLUSH_PLAYBACK_STREAM,
     PA_COMMAND_TRIGGER_PLAYBACK_STREAM,
+
     PA_COMMAND_SET_DEFAULT_SINK,
     PA_COMMAND_SET_DEFAULT_SOURCE,
+
     PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
     PA_COMMAND_SET_RECORD_STREAM_NAME,
+
     PA_COMMAND_KILL_CLIENT,
     PA_COMMAND_KILL_SINK_INPUT,
     PA_COMMAND_KILL_SOURCE_OUTPUT,
+
     PA_COMMAND_LOAD_MODULE,
     PA_COMMAND_UNLOAD_MODULE,
+
     PA_COMMAND_ADD_AUTOLOAD,
     PA_COMMAND_REMOVE_AUTOLOAD,
     PA_COMMAND_GET_AUTOLOAD_INFO,
     PA_COMMAND_GET_AUTOLOAD_INFO_LIST,
+
     PA_COMMAND_GET_RECORD_LATENCY,
     PA_COMMAND_CORK_RECORD_STREAM,
     PA_COMMAND_FLUSH_RECORD_STREAM,
     PA_COMMAND_PREBUF_PLAYBACK_STREAM,
+
+    /* SERVER->CLIENT */
+    PA_COMMAND_REQUEST,
+    PA_COMMAND_OVERFLOW,
+    PA_COMMAND_UNDERFLOW,
+    PA_COMMAND_PLAYBACK_STREAM_KILLED,
+    PA_COMMAND_RECORD_STREAM_KILLED,
+    PA_COMMAND_SUBSCRIBE_EVENT,
+
+    /* A few more client->server commands */
+
+    /* Supported since protocol v10 (0.9.5) */
+    PA_COMMAND_MOVE_SINK_INPUT,
+    PA_COMMAND_MOVE_SOURCE_OUTPUT,
+
+    /* Supported since protocol v11 (0.9.7) */
+    PA_COMMAND_SET_SINK_INPUT_MUTE,
+
+    PA_COMMAND_SUSPEND_SINK,
+    PA_COMMAND_SUSPEND_SOURCE,
+
+    /* Supported since protocol v12 (0.9.8) */
+    PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
+    PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR,
+
+    PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
+    PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE,
+
+    /* SERVER->CLIENT */
+    PA_COMMAND_PLAYBACK_STREAM_SUSPENDED,
+    PA_COMMAND_RECORD_STREAM_SUSPENDED,
+    PA_COMMAND_PLAYBACK_STREAM_MOVED,
+    PA_COMMAND_RECORD_STREAM_MOVED,
+
+    /* Supported since protocol v13 (0.9.10) */
+    PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST,
+    PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
+    PA_COMMAND_UPDATE_CLIENT_PROPLIST,
+    PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST,
+    PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
+    PA_COMMAND_REMOVE_CLIENT_PROPLIST,
+
+    /* SERVER->CLIENT */
+    PA_COMMAND_STARTED,
+
     PA_COMMAND_MAX
 };
 
 #define PA_NATIVE_COOKIE_LENGTH 256
-#define PA_NATIVE_COOKIE_FILE ".polypaudio-cookie"
+#define PA_NATIVE_COOKIE_FILE ".pulse-cookie"
 
 #define PA_NATIVE_DEFAULT_PORT 4713
 
diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c
new file mode 100644 (file)
index 0000000..9a2f28f
--- /dev/null
@@ -0,0 +1,70 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 "object.h"
+
+pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name)) {
+    pa_object *o;
+
+    pa_assert(size > sizeof(pa_object));
+    pa_assert(type_name);
+
+    if (!check_type)
+        check_type = pa_object_check_type;
+
+    pa_assert(check_type(type_name));
+    pa_assert(check_type("pa_object"));
+
+    o = pa_xmalloc(size);
+    PA_REFCNT_INIT(o);
+    o->type_name = type_name;
+    o->free = pa_object_free;
+    o->check_type = check_type;
+
+    return o;
+}
+
+pa_object *pa_object_ref(pa_object *o) {
+    pa_object_assert_ref(o);
+
+    PA_REFCNT_INC(o);
+    return o;
+}
+
+void pa_object_unref(pa_object *o) {
+    pa_object_assert_ref(o);
+
+    if (PA_REFCNT_DEC(o) <= 0) {
+        pa_assert(o->free);
+        o->free(o);
+    }
+}
+
+int pa_object_check_type(const char *type_name) {
+    pa_assert(type_name);
+
+    return strcmp(type_name, "pa_object") == 0;
+}
diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h
new file mode 100644 (file)
index 0000000..7dcfa2e
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef foopulseobjecthfoo
+#define foopulseobjecthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_object pa_object;
+
+struct pa_object {
+    PA_REFCNT_DECLARE;
+    const char *type_name;
+    void (*free)(pa_object *o);
+    int (*check_type)(const char *type_name);
+};
+
+pa_object *pa_object_new_internal(size_t size, const char *type_name, int (*check_type)(const char *type_name));
+#define pa_object_new(type) ((type*) pa_object_new_internal(sizeof(type), #type, type##_check_type)
+
+#define pa_object_free ((void (*) (pa_object* o)) pa_xfree)
+
+int pa_object_check_type(const char *type);
+
+static inline int pa_object_isinstance(void *o) {
+    pa_object *obj = (pa_object*) o;
+    return obj ? obj->check_type("pa_object") : 0;
+}
+
+pa_object *pa_object_ref(pa_object *o);
+void pa_object_unref(pa_object *o);
+
+static inline int pa_object_refcnt(pa_object *o) {
+    return o ? PA_REFCNT_VALUE(o) : 0;
+}
+
+static inline pa_object* pa_object_cast(void *o) {
+    pa_object *obj = (pa_object*) o;
+    pa_assert(!obj || obj->check_type("pa_object"));
+    return obj;
+}
+
+#define pa_object_assert_ref(o) pa_assert(pa_object_refcnt(o) > 0)
+
+#define PA_OBJECT(o) pa_object_cast(o)
+
+#define PA_DECLARE_CLASS(c)                                             \
+    static inline int c##_isinstance(void *o) {                         \
+        pa_object *obj = (pa_object*) o;                                \
+        return obj ? obj->check_type(#c) : 1;                           \
+    }                                                                   \
+    static inline c* c##_cast(void *o) {                                \
+        pa_assert(c##_isinstance(o));                                   \
+        return (c*) o;                                                  \
+    }                                                                   \
+    static inline c* c##_ref(c *o) {                                    \
+        return (c*) pa_object_ref(PA_OBJECT(o));                        \
+    }                                                                   \
+    static inline void c##_unref(c* o) {                                \
+        pa_object_unref(PA_OBJECT(o));                                  \
+    }                                                                   \
+    static inline int c##_refcnt(c* o) {                                \
+        return pa_object_refcnt(PA_OBJECT(o));                          \
+    }                                                                   \
+    static inline void c##_assert_ref(c *o) {                           \
+        pa_object_assert_ref(PA_OBJECT(o));                             \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#define PA_DEFINE_CHECK_TYPE(c, parent)                                 \
+    int c##_check_type(const char *type) {                              \
+        pa_assert(type);                                                \
+        if (strcmp(type, #c) == 0)                                      \
+            return 1;                                                   \
+        return parent##_check_type(type);                               \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+
+#endif
diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c
new file mode 100644 (file)
index 0000000..989741d
--- /dev/null
@@ -0,0 +1,94 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulsecore/macro.h>
+#include <pulsecore/mutex.h>
+
+#include "once.h"
+
+int pa_once_begin(pa_once *control) {
+    pa_mutex *m;
+
+    pa_assert(control);
+
+    if (pa_atomic_load(&control->done))
+        return 0;
+
+    pa_atomic_inc(&control->ref);
+
+    /* Caveat: We have to make sure that the once func has completed
+     * before returning, even if the once func is not actually
+     * executed by us. Hence the awkward locking. */
+
+    for (;;) {
+
+        if ((m = pa_atomic_ptr_load(&control->mutex))) {
+
+            /* The mutex is stored in locked state, hence let's just
+             * wait until it is unlocked */
+            pa_mutex_lock(m);
+
+            pa_once_end(control);
+            return 0;
+        }
+
+        pa_assert_se(m = pa_mutex_new(FALSE, FALSE));
+        pa_mutex_lock(m);
+
+        if (pa_atomic_ptr_cmpxchg(&control->mutex, NULL, m))
+            return 1;
+
+        pa_mutex_unlock(m);
+        pa_mutex_free(m);
+    }
+}
+
+void pa_once_end(pa_once *control) {
+    pa_mutex *m;
+
+    pa_assert(control);
+
+    pa_atomic_store(&control->done, 1);
+
+    pa_assert_se(m = pa_atomic_ptr_load(&control->mutex));
+    pa_mutex_unlock(m);
+
+    if (pa_atomic_dec(&control->ref) <= 1) {
+        pa_assert_se(pa_atomic_ptr_cmpxchg(&control->mutex, m, NULL));
+        pa_mutex_free(m);
+    }
+}
+
+/* Not reentrant -- how could it be? */
+void pa_run_once(pa_once *control, pa_once_func_t func) {
+    pa_assert(control);
+    pa_assert(func);
+
+    if (pa_once_begin(control)) {
+        func();
+        pa_once_end(control);
+    }
+}
+
diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h
new file mode 100644 (file)
index 0000000..576d40f
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef foopulseoncehfoo
+#define foopulseoncehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/mutex.h>
+#include <pulsecore/atomic.h>
+
+typedef struct pa_once {
+    pa_atomic_ptr_t mutex;
+    pa_atomic_t ref, done;
+} pa_once;
+
+#define PA_ONCE_INIT                                                    \
+    {                                                                   \
+        .mutex = PA_ATOMIC_PTR_INIT(NULL),                              \
+        .ref = PA_ATOMIC_INIT(0),                                       \
+        .done = PA_ATOMIC_INIT(0)                                       \
+    }
+
+/* Not to be called directly, use the macros defined below instead */
+int pa_once_begin(pa_once *o);
+void pa_once_end(pa_once *o);
+
+#define PA_ONCE_BEGIN                                                   \
+    do {                                                                \
+        static pa_once _once = PA_ONCE_INIT;                            \
+        if (pa_once_begin(&_once)) {{
+
+#define PA_ONCE_END                                                     \
+            }                                                           \
+            pa_once_end(&_once);                                        \
+        }                                                               \
+    } while(0)
+
+/*
+
+  Usage of these macros is like this:
+
+  void foo() {
+
+      PA_ONCE_BEGIN {
+
+          ... stuff to be called just once ...
+
+      } PA_ONCE_END;
+  }
+
+*/
+
+/* Same API but calls a function */
+typedef void (*pa_once_func_t) (void);
+void pa_run_once(pa_once *o, pa_once_func_t f);
+
+#endif
diff --git a/src/pulsecore/packet.c b/src/pulsecore/packet.c
new file mode 100644 (file)
index 0000000..cee468b
--- /dev/null
@@ -0,0 +1,79 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "packet.h"
+
+pa_packet* pa_packet_new(size_t length) {
+    pa_packet *p;
+
+    pa_assert(length > 0);
+
+    p = pa_xmalloc(PA_ALIGN(sizeof(pa_packet)) + length);
+    PA_REFCNT_INIT(p);
+    p->length = length;
+    p->data = (uint8_t*) p + PA_ALIGN(sizeof(pa_packet));
+    p->type = PA_PACKET_APPENDED;
+
+    return p;
+}
+
+pa_packet* pa_packet_new_dynamic(void* data, size_t length) {
+    pa_packet *p;
+
+    pa_assert(data);
+    pa_assert(length > 0);
+
+    p = pa_xnew(pa_packet, 1);
+    PA_REFCNT_INIT(p);
+    p->length = length;
+    p->data = data;
+    p->type = PA_PACKET_DYNAMIC;
+
+    return p;
+}
+
+pa_packet* pa_packet_ref(pa_packet *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    PA_REFCNT_INC(p);
+    return p;
+}
+
+void pa_packet_unref(pa_packet *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    if (PA_REFCNT_DEC(p) <= 0) {
+        if (p->type == PA_PACKET_DYNAMIC)
+            pa_xfree(p->data);
+        pa_xfree(p);
+    }
+}
similarity index 57%
rename from polyp/packet.h
rename to src/pulsecore/packet.h
index 5e4e92f784978fccb23f90159cab347e496b9736..5989b1fa8463e0c6ad27daf88b76aa8fa33273ba 100644 (file)
@@ -1,23 +1,23 @@
 #ifndef foopackethfoo
 #define foopackethfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; 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>
 
-struct pa_packet {
+#include <pulsecore/refcnt.h>
+
+typedef struct pa_packet {
+    PA_REFCNT_DECLARE;
     enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type;
-    unsigned ref;
     size_t length;
     uint8_t *data;
-};
+} pa_packet;
 
-struct pa_packet* pa_packet_new(size_t length);
-struct pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length);
+pa_packet* pa_packet_new(size_t length);
+pa_packet* pa_packet_new_dynamic(void* data, size_t length);
 
-struct pa_packet* pa_packet_ref(struct pa_packet *p);
-void pa_packet_unref(struct pa_packet *p);
+pa_packet* pa_packet_ref(pa_packet *p);
+void pa_packet_unref(pa_packet *p);
 
 #endif
similarity index 76%
rename from polyp/parseaddr.c
rename to src/pulsecore/parseaddr.c
index 05ed508b6f6b266beb1050fc7178c76e4e82c793..f2b6b2cfe9087e661f16d217d395bed69d01aa90 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #endif
 
 #include <string.h>
-#include <assert.h>
 #include <stdlib.h>
 
-#include "xmalloc.h"
-#include "util.h"
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
 #include "parseaddr.h"
 
 /* Parse addresses in one of the following forms:
@@ -40,7 +43,9 @@
  *  Return a newly allocated string of the hostname and fill in *ret_port if specified  */
 
 static char *parse_host(const char *s, uint16_t *ret_port) {
-    assert(s && ret_port);
+    pa_assert(s);
+    pa_assert(ret_port);
+
     if (*s == '[') {
         char *e;
         if (!(e = strchr(s+1, ']')))
@@ -50,11 +55,11 @@ static char *parse_host(const char *s, uint16_t *ret_port) {
             *ret_port = atoi(e+2);
         else if (e[1] != 0)
             return NULL;
-        
+
         return pa_xstrndup(s+1, e-s-1);
     } else {
         char *e;
-        
+
         if (!(e = strrchr(s, ':')))
             return pa_xstrdup(s);
 
@@ -63,39 +68,45 @@ static char *parse_host(const char *s, uint16_t *ret_port) {
     }
 }
 
-int pa_parse_address(const char *name, struct pa_parsed_address *ret_p) {
+int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
     const char *p;
-    assert(name && ret_p);
-    memset(ret_p, 0, sizeof(struct pa_parsed_address));
+
+    pa_assert(name);
+    pa_assert(ret_p);
+
+    memset(ret_p, 0, sizeof(pa_parsed_address));
     ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
 
     if (*name == '{') {
         char hn[256], *pfx;
         /* The URL starts with a host specification for detecting local connections */
-        
+
         if (!pa_get_host_name(hn, sizeof(hn)))
             return -1;
-                
+
         pfx = pa_sprintf_malloc("{%s}", hn);
         if (!pa_startswith(name, pfx)) {
             pa_xfree(pfx);
             /* Not local */
             return -1;
         }
-        
+
         p = name + strlen(pfx);
         pa_xfree(pfx);
     } else
         p = name;
-    
+
     if (*p == '/')
         ret_p->type = PA_PARSED_ADDRESS_UNIX;
     else if (pa_startswith(p, "unix:")) {
         ret_p->type = PA_PARSED_ADDRESS_UNIX;
         p += sizeof("unix:")-1;
-    } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) {
+    } else if (pa_startswith(p, "tcp:")) {
         ret_p->type = PA_PARSED_ADDRESS_TCP4;
         p += sizeof("tcp:")-1;
+    } else if (pa_startswith(p, "tcp4:")) {
+        ret_p->type = PA_PARSED_ADDRESS_TCP4;
+        p += sizeof("tcp4:")-1;
     } else if (pa_startswith(p, "tcp6:")) {
         ret_p->type = PA_PARSED_ADDRESS_TCP6;
         p += sizeof("tcp6:")-1;
@@ -106,7 +117,6 @@ int pa_parse_address(const char *name, struct pa_parsed_address *ret_p) {
     else
         if (!(ret_p->path_or_host = parse_host(p, &ret_p->port)))
             return -1;
-    
-        
+
     return 0;
 }
similarity index 61%
rename from polyp/parseaddr.h
rename to src/pulsecore/parseaddr.h
index 5ddc0351e838d114d68094b76de1cb5cf9f64d62..5fbcb9a7907136095724ebc41b9d71b8fc1f7527 100644 (file)
@@ -1,42 +1,42 @@
 #ifndef fooparseaddrhfoo
 #define fooparseaddrhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include <inttypes.h>
 
-enum pa_parsed_address_type {
+typedef enum pa_parsed_address_type {
     PA_PARSED_ADDRESS_UNIX,
     PA_PARSED_ADDRESS_TCP4,
     PA_PARSED_ADDRESS_TCP6,
     PA_PARSED_ADDRESS_TCP_AUTO
-};
+} pa_parsed_address_type_t;
 
-struct pa_parsed_address {
-    enum pa_parsed_address_type type;
+typedef struct pa_parsed_address {
+    pa_parsed_address_type_t type;
     char *path_or_host;
     uint16_t port;
-};
+} pa_parsed_address;
 
-int pa_parse_address(const char *a, struct pa_parsed_address *ret_p);
+int pa_parse_address(const char *a, pa_parsed_address *ret_p);
 
 #endif
similarity index 57%
rename from polyp/pdispatch.c
rename to src/pulsecore/pdispatch.c
index 7a9e9c68060d32de767d7ef7f8dbf7547f4ae0ec..e6a6ae4d7f230833a1b8d238b24d197028f00e99 100644 (file)
@@ -1,20 +1,21 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <assert.h>
+
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/native-common.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/flist.h>
 
 #include "pdispatch.h"
-#include "native-common.h"
-#include "xmalloc.h"
-#include "llist.h"
-#include "log.h"
 
 /*#define DEBUG_OPCODES */
 
@@ -80,6 +87,7 @@ static const char *command_names[PA_COMMAND_MAX] = {
     [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
     [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
     [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
+    [PA_COMMAND_SET_SOURCE_VOLUME] = "SET_SOURCE_VOLME",
     [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
     [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
     [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
@@ -89,77 +97,89 @@ static const char *command_names[PA_COMMAND_MAX] = {
 
 #endif
 
+PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
+
 struct reply_info {
-    struct pa_pdispatch *pdispatch;
+    pa_pdispatch *pdispatch;
     PA_LLIST_FIELDS(struct reply_info);
-    void (*callback)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+    pa_pdispatch_cb_t callback;
     void *userdata;
+    pa_free_cb_t free_cb;
     uint32_t tag;
-    struct pa_time_event *time_event;
+    pa_time_event *time_event;
 };
 
 struct pa_pdispatch {
-    int ref;
-    struct pa_mainloop_api *mainloop;
-    const struct pa_pdispatch_command *command_table;
+    PA_REFCNT_DECLARE;
+    pa_mainloop_api *mainloop;
+    const pa_pdispatch_cb_t *callback_table;
     unsigned n_commands;
     PA_LLIST_HEAD(struct reply_info, replies);
-    void (*drain_callback)(struct pa_pdispatch *pd, void *userdata);
+    pa_pdispatch_drain_callback drain_callback;
     void *drain_userdata;
+    const pa_creds *creds;
 };
 
 static void reply_info_free(struct reply_info *r) {
-    assert(r && r->pdispatch && r->pdispatch->mainloop);
+    pa_assert(r);
+    pa_assert(r->pdispatch);
+    pa_assert(r->pdispatch->mainloop);
 
     if (r->time_event)
         r->pdispatch->mainloop->time_free(r->time_event);
-    
+
     PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
-    
-    pa_xfree(r);
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
+        pa_xfree(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);
+pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
+    pa_pdispatch *pd;
+    pa_assert(mainloop);
+
+    pa_assert((entries && table) || (!entries && !table));
 
-    assert((entries && table) || (!entries && !table));
-    
-    pd = pa_xmalloc(sizeof(struct pa_pdispatch));
-    pd->ref = 1;
+    pd = pa_xnew(pa_pdispatch, 1);
+    PA_REFCNT_INIT(pd);
     pd->mainloop = mainloop;
-    pd->command_table = table;
+    pd->callback_table = table;
     pd->n_commands = entries;
-    PA_LLIST_HEAD_INIT(struct pa_reply_info, pd->replies);
+    PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
     pd->drain_callback = NULL;
     pd->drain_userdata = NULL;
+    pd->creds = NULL;
 
     return pd;
 }
 
-void pdispatch_free(struct pa_pdispatch *pd) {
-    assert(pd);
+static void pdispatch_free(pa_pdispatch *pd) {
+    pa_assert(pd);
+
+    while (pd->replies) {
+        if (pd->replies->free_cb)
+            pd->replies->free_cb(pd->replies->userdata);
 
-    while (pd->replies)
         reply_info_free(pd->replies);
-    
+    }
+
     pa_xfree(pd);
 }
 
-static void run_action(struct pa_pdispatch *pd, struct reply_info *r, uint32_t command, struct pa_tagstruct *ts) {
-    void (*callback)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
+    pa_pdispatch_cb_t callback;
     void *userdata;
     uint32_t tag;
-    assert(r);
+    pa_assert(r);
 
     pa_pdispatch_ref(pd);
-    
+
     callback = r->callback;
     userdata = r->userdata;
     tag = r->tag;
-    
+
     reply_info_free(r);
-    
+
     callback(pd, command, tag, ts, userdata);
 
     if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
@@ -168,20 +188,24 @@ static void run_action(struct pa_pdispatch *pd, struct reply_info *r, uint32_t c
     pa_pdispatch_unref(pd);
 }
 
-int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*packet, void *userdata) {
+int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, void *userdata) {
     uint32_t tag, command;
-    struct pa_tagstruct *ts = NULL;
+    pa_tagstruct *ts = NULL;
     int ret = -1;
-    assert(pd && packet && packet->data);
+
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+    pa_assert(packet);
+    pa_assert(PA_REFCNT_VALUE(packet) >= 1);
+    pa_assert(packet->data);
 
     pa_pdispatch_ref(pd);
-    
+
     if (packet->length <= 8)
         goto finish;
 
     ts = pa_tagstruct_new(packet->data, packet->length);
-    assert(ts);
-    
+
     if (pa_tagstruct_getu32(ts, &command) < 0 ||
         pa_tagstruct_getu32(ts, &tag) < 0)
         goto finish;
@@ -191,12 +215,14 @@ int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*packet, void *use
     char t[256];
     char const *p;
     if (!(p = command_names[command]))
-        snprintf((char*) (p = t), sizeof(t), "%u", command);
-        
-    pa_log(__FILE__": Recieved opcode <%s>\n", p);
+        pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
+
+    pa_log("Recieved opcode <%s>", p);
 }
 #endif
 
+    pd->creds = creds;
+
     if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
         struct reply_info *r;
 
@@ -207,18 +233,20 @@ int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*packet, void *use
         if (r)
             run_action(pd, r, command, ts);
 
-    } else if (pd->command_table && (command < pd->n_commands) && pd->command_table[command].proc) {
-        const struct pa_pdispatch_command *c = pd->command_table+command;
+    } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
+        const pa_pdispatch_cb_t *c = pd->callback_table+command;
 
-        c->proc(pd, command, tag, ts, userdata);
+        (*c)(pd, command, tag, ts, userdata);
     } else {
-        pa_log(__FILE__": Recieved unsupported command %u\n", command);
+        pa_log("Recieved unsupported command %u", command);
         goto finish;
     }
 
     ret = 0;
-        
+
 finish:
+    pd->creds = NULL;
+
     if (ts)
         pa_tagstruct_free(ts);
 
@@ -227,68 +255,92 @@ finish:
     return ret;
 }
 
-static void timeout_callback(struct pa_mainloop_api*m, struct pa_time_event*e, const struct timeval *tv, void *userdata) {
+static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
     struct reply_info*r = userdata;
-    assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
+
+    pa_assert(r);
+    pa_assert(r->time_event == e);
+    pa_assert(r->pdispatch);
+    pa_assert(r->pdispatch->mainloop == m);
+    pa_assert(r->callback);
 
     run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
 }
 
-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) {
+void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {
     struct reply_info *r;
     struct timeval tv;
-    assert(pd && pd->ref >= 1 && cb);
 
-    r = pa_xmalloc(sizeof(struct reply_info));
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+    pa_assert(cb);
+
+    if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
+        r = pa_xnew(struct reply_info, 1);
+
     r->pdispatch = pd;
     r->callback = cb;
     r->userdata = userdata;
+    r->free_cb = free_cb;
     r->tag = tag;
-    
-    gettimeofday(&tv, NULL);
+
+    pa_gettimeofday(&tv);
     tv.tv_sec += timeout;
 
-    r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r);
-    assert(r->time_event);
+    pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r));
 
     PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
 }
 
-int pa_pdispatch_is_pending(struct pa_pdispatch *pd) {
-    assert(pd);
+int pa_pdispatch_is_pending(pa_pdispatch *pd) {
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
 
     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));
+void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+    pa_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) {
+void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
     struct reply_info *r, *n;
-    assert(pd);
+
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
 
     for (r = pd->replies; r; r = n) {
         n = r->next;
 
-        if (r->userdata == userdata) 
+        if (r->userdata == userdata)
             reply_info_free(r);
     }
 }
 
-void pa_pdispatch_unref(struct pa_pdispatch *pd) {
-    assert(pd && pd->ref >= 1);
+void pa_pdispatch_unref(pa_pdispatch *pd) {
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
 
-    if (!(--(pd->ref)))
+    if (PA_REFCNT_DEC(pd) <= 0)
         pdispatch_free(pd);
 }
 
-struct pa_pdispatch* pa_pdispatch_ref(struct pa_pdispatch *pd) {
-    assert(pd && pd->ref >= 1);
-    pd->ref++;
+pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
+    PA_REFCNT_INC(pd);
     return pd;
 }
+
+const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
+    pa_assert(pd);
+    pa_assert(PA_REFCNT_VALUE(pd) >= 1);
+
+    return pd->creds;
+}
diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h
new file mode 100644 (file)
index 0000000..5c31d80
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef foopdispatchhfoo
+#define foopdispatchhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/def.h>
+
+#include <pulsecore/tagstruct.h>
+#include <pulsecore/packet.h>
+#include <pulsecore/creds.h>
+
+typedef struct pa_pdispatch pa_pdispatch;
+
+typedef void (*pa_pdispatch_cb_t)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata);
+
+pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, const pa_pdispatch_cb_t*table, unsigned entries);
+void pa_pdispatch_unref(pa_pdispatch *pd);
+pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd);
+
+int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, const pa_creds *creds, void *userdata);
+
+void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t callback, void *userdata, pa_free_cb_t free_cb);
+
+int pa_pdispatch_is_pending(pa_pdispatch *pd);
+
+void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata);
+
+/* Remove all reply slots with the give userdata parameter */
+void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata);
+
+const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd);
+
+#endif
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
new file mode 100644 (file)
index 0000000..addb17c
--- /dev/null
@@ -0,0 +1,386 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <signal.h>
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "pid.h"
+
+/* Read the PID data from the file descriptor fd, and return it. If no
+ * pid could be read, return 0, on failure (pid_t) -1 */
+static pid_t read_pid(const char *fn, int fd) {
+    ssize_t r;
+    char t[20], *e;
+    uint32_t pid;
+
+    pa_assert(fn);
+    pa_assert(fd >= 0);
+
+    if ((r = pa_loop_read(fd, t, sizeof(t)-1, NULL)) < 0) {
+        pa_log_warn("Failed to read PID file '%s': %s", fn, pa_cstrerror(errno));
+        return (pid_t) -1;
+    }
+
+    if (r == 0)
+        return (pid_t) 0;
+
+    t[r] = 0;
+    if ((e = strchr(t, '\n')))
+        *e = 0;
+
+    if (pa_atou(t, &pid) < 0) {
+        pa_log_warn("Failed to parse PID file '%s'", fn);
+        return (pid_t) -1;
+    }
+
+    return (pid_t) pid;
+}
+
+static int open_pid_file(const char *fn, int mode) {
+    int fd = -1;
+
+    pa_assert(fn);
+
+    for (;;) {
+        struct stat st;
+
+        if ((fd = open(fn, mode
+#ifdef O_NOCTTY
+                       |O_NOCTTY
+#endif
+#ifdef O_NOFOLLOW
+                       |O_NOFOLLOW
+#endif
+                       , S_IRUSR|S_IWUSR
+             )) < 0) {
+            if (mode != O_RDONLY || errno != ENOENT)
+                pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        /* Try to lock the file. If that fails, go without */
+        if (pa_lock_fd(fd, 1) < 0)
+            goto fail;
+
+        if (fstat(fd, &st) < 0) {
+            pa_log_warn("Failed to fstat() PID file '%s': %s", fn, pa_cstrerror(errno));
+            goto fail;
+        }
+
+        /* Does the file still exist in the file system? When ye, w're done, otherwise restart */
+        if (st.st_nlink >= 1)
+            break;
+
+        if (pa_lock_fd(fd, 0) < 0)
+            goto fail;
+
+        if (pa_close(fd) < 0) {
+            pa_log_warn("Failed to close file '%s': %s", fn, pa_cstrerror(errno));
+            fd = -1;
+            goto fail;
+        }
+
+        fd = -1;
+    }
+
+    return fd;
+
+fail:
+
+    if (fd >= 0) {
+        pa_lock_fd(fd, 0);
+        pa_close(fd);
+    }
+
+    return -1;
+}
+
+static int proc_name_ours(pid_t pid, const char *procname) {
+#ifdef __linux__
+    char bn[PATH_MAX];
+    FILE *f;
+
+    pa_snprintf(bn, sizeof(bn), "/proc/%lu/stat", (unsigned long) pid);
+
+    if (!(f = fopen(bn, "r"))) {
+        pa_log_info("Failed to open %s: %s", bn, pa_cstrerror(errno));
+        return -1;
+    } else {
+        char *expected;
+        pa_bool_t good;
+        char stored[64];
+
+        if (!(fgets(stored, sizeof(stored), f))) {
+            pa_log_info("Failed to read from %s: %s", bn, feof(f) ? "EOF" : pa_cstrerror(errno));
+            fclose(f);
+            return -1;
+        }
+
+        fclose(f);
+
+        expected = pa_sprintf_malloc("%lu (%s)", (unsigned long) pid, procname);
+        good = pa_startswith(stored, expected);
+        pa_xfree(expected);
+
+#if !defined(__OPTIMIZE__)
+        if (!good) {
+            /* libtool likes to rename our binary names ... */
+            expected = pa_sprintf_malloc("%lu (lt-%s)", (unsigned long) pid, procname);
+            good = pa_startswith(stored, expected);
+            pa_xfree(expected);
+        }
+#endif
+
+        return !!good;
+    }
+#endif
+
+    return 1;
+}
+
+/* Create a new PID file for the current process. */
+int pa_pid_file_create(const char *procname) {
+    int fd = -1;
+    int ret = -1;
+    char t[20];
+    pid_t pid;
+    size_t l;
+    char *fn;
+
+#ifdef OS_IS_WIN32
+    HANDLE process;
+#endif
+
+    if (!(fn = pa_runtime_path("pid")))
+        goto fail;
+
+    if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
+        goto fail;
+
+    if ((pid = read_pid(fn, fd)) == (pid_t) -1)
+        pa_log_warn("Corrupt PID file, overwriting.");
+    else if (pid > 0) {
+
+#ifdef OS_IS_WIN32
+        if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
+            CloseHandle(process);
+#else
+        if (kill(pid, 0) >= 0 || errno != ESRCH) {
+#endif
+            int ours = 1;
+
+            if (procname)
+                if ((ours = proc_name_ours(pid, procname)) < 0)
+                    goto fail;
+
+            if (ours) {
+                pa_log("Daemon already running.");
+                ret = 1;
+                goto fail;
+            }
+        }
+
+        pa_log_warn("Stale PID file, overwriting.");
+    }
+
+    /* Overwrite the current PID file */
+    if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) {
+        pa_log("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
+    l = strlen(t);
+
+    if (pa_loop_write(fd, t, l, NULL) != (ssize_t) l) {
+        pa_log("Failed to write PID file.");
+        goto fail;
+    }
+
+    ret = 0;
+
+fail:
+    if (fd >= 0) {
+        pa_lock_fd(fd, 0);
+
+        if (pa_close(fd) < 0) {
+            pa_log("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno));
+            ret = -1;
+        }
+    }
+
+    pa_xfree(fn);
+
+    return ret;
+}
+
+/* Remove the PID file, if it is ours */
+int pa_pid_file_remove(void) {
+    int fd = -1;
+    char *fn;
+    int ret = -1;
+    pid_t pid;
+
+    if (!(fn = pa_runtime_path("pid")))
+        goto fail;
+
+    if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
+        pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if ((pid = read_pid(fn, fd)) == (pid_t) -1)
+        goto fail;
+
+    if (pid != getpid()) {
+        pa_log("PID file '%s' not mine!", fn);
+        goto fail;
+    }
+
+    if (ftruncate(fd, 0) < 0) {
+        pa_log_warn("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
+        goto fail;
+    }
+
+#ifdef OS_IS_WIN32
+    pa_lock_fd(fd, 0);
+    pa_close(fd);
+    fd = -1;
+#endif
+
+    if (unlink(fn) < 0) {
+        pa_log_warn("Failed to remove PID file '%s': %s", fn, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    ret = 0;
+
+fail:
+
+    if (fd >= 0) {
+        pa_lock_fd(fd, 0);
+
+        if (pa_close(fd) < 0) {
+            pa_log_warn("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno));
+            ret = -1;
+        }
+    }
+
+    pa_xfree(fn);
+
+    return ret;
+}
+
+/* Check whether the daemon is currently running, i.e. if a PID file
+ * exists and the PID therein too. Returns 0 on succcess, -1
+ * otherwise. If pid is non-NULL and a running daemon was found,
+ * return its PID therein */
+int pa_pid_file_check_running(pid_t *pid, const char *procname) {
+    return pa_pid_file_kill(0, pid, procname);
+}
+
+#ifndef OS_IS_WIN32
+
+/* Kill a current running daemon. Return non-zero on success, -1
+ * otherwise. If successful *pid contains the PID of the daemon
+ * process. */
+int pa_pid_file_kill(int sig, pid_t *pid, const char *procname) {
+    int fd = -1;
+    char *fn;
+    int ret = -1;
+    pid_t _pid;
+#ifdef __linux__
+    char *e = NULL;
+#endif
+
+    if (!pid)
+        pid = &_pid;
+
+    if (!(fn = pa_runtime_path("pid")))
+        goto fail;
+
+    if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
+        goto fail;
+
+    if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
+        goto fail;
+
+    if (procname) {
+        int ours;
+
+        if ((ours = proc_name_ours(*pid, procname)) < 0)
+            goto fail;
+
+        if (!ours)
+            goto fail;
+    }
+
+    ret = kill(*pid, sig);
+
+fail:
+
+    if (fd >= 0) {
+        pa_lock_fd(fd, 0);
+        pa_close(fd);
+    }
+
+#ifdef __linux__
+    pa_xfree(e);
+#endif
+
+    pa_xfree(fn);
+
+    return ret;
+
+}
+
+#else /* OS_IS_WIN32 */
+
+int pa_pid_file_kill(int sig, pid_t *pid, const char *exe_name) {
+    return -1;
+}
+
+#endif
similarity index 57%
rename from polyp/pid.h
rename to src/pulsecore/pid.h
index 906ab6da896e8902d39f277fd0ff74dd376c41e9..3c8a9de35bb9643fbe1630724b7458403551934c 100644 (file)
@@ -1,30 +1,30 @@
 #ifndef foopidhfoo
 #define foopidhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-int pa_pid_file_create(void);
+int pa_pid_file_create(const char *procname);
 int pa_pid_file_remove(void);
-int pa_pid_file_check_running(pid_t *pid);
-int pa_pid_file_kill(int sig, pid_t *pid);
+int pa_pid_file_check_running(pid_t *pid, const char *procname);
+int pa_pid_file_kill(int sig, pid_t *pid, const char *procname);
 
 #endif
diff --git a/src/pulsecore/pipe.c b/src/pulsecore/pipe.c
new file mode 100644 (file)
index 0000000..93d78a2
--- /dev/null
@@ -0,0 +1,160 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <unistd.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include "winsock.h"
+
+#include "pipe.h"
+
+#ifndef HAVE_PIPE
+
+static int set_block(int fd, int blocking) {
+#ifdef O_NONBLOCK
+
+    int v;
+
+    assert(fd >= 0);
+
+    if ((v = fcntl(fd, F_GETFL)) < 0)
+        return -1;
+
+    if (blocking)
+        v &= ~O_NONBLOCK;
+    else
+        v |= O_NONBLOCK;
+
+    if (fcntl(fd, F_SETFL, v) < 0)
+        return -1;
+
+    return 0;
+
+#elif defined(OS_IS_WIN32)
+
+    u_long arg;
+
+    arg = !blocking;
+
+    if (ioctlsocket(fd, FIONBIO, &arg) < 0)
+        return -1;
+
+    return 0;
+
+#else
+
+    return -1;
+
+#endif
+}
+
+int pipe(int filedes[2]) {
+    int listener;
+    struct sockaddr_in addr, peer;
+    socklen_t len;
+
+    listener = -1;
+    filedes[0] = -1;
+    filedes[1] = -1;
+
+    listener = socket(PF_INET, SOCK_STREAM, 0);
+    if (listener < 0)
+        goto error;
+
+    filedes[0] = socket(PF_INET, SOCK_STREAM, 0);
+    if (filedes[0] < 0)
+        goto error;
+
+    filedes[1] = socket(PF_INET, SOCK_STREAM, 0);
+    if (filedes[1] < 0)
+        goto error;
+
+    /* Make non-blocking so that connect() won't block */
+    if (set_block(filedes[0], 0) < 0)
+        goto error;
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = 0;
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) < 0)
+        goto error;
+
+    if (listen(listener, 1) < 0)
+        goto error;
+
+    len = sizeof(addr);
+    if (getsockname(listener, (struct sockaddr*)&addr, &len) < 0)
+        goto error;
+
+    if (connect(filedes[0], (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+#ifdef OS_IS_WIN32
+        if (WSAGetLastError() != EWOULDBLOCK)
+#else
+        if (errno != EINPROGRESS)
+#endif
+            goto error;
+    }
+
+    len = sizeof(peer);
+    filedes[1] = accept(listener, (struct sockaddr*)&peer, &len);
+    if (filedes[1] < 0)
+        goto error;
+
+    /* Restore blocking */
+    if (set_block(filedes[0], 1) < 0)
+        goto error;
+
+    len = sizeof(addr);
+    if (getsockname(filedes[0], (struct sockaddr*)&addr, &len) < 0)
+        goto error;
+
+    /* Check that someone else didn't steal the connection */
+    if ((addr.sin_port != peer.sin_port) || (addr.sin_addr.s_addr != peer.sin_addr.s_addr))
+        goto error;
+
+    pa_close(listener);
+
+    return 0;
+
+error:
+        if (listener >= 0)
+                pa_close(listener);
+        if (filedes[0] >= 0)
+                pa_close(filedes[0]);
+        if (filedes[1] >= 0)
+                pa_close(filedes[0]);
+
+        return -1;
+}
+
+#endif /* HAVE_PIPE */
diff --git a/src/pulsecore/pipe.h b/src/pulsecore/pipe.h
new file mode 100644 (file)
index 0000000..9a7e62c
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef foopipehfoo
+#define foopipehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifndef HAVE_PIPE
+
+int pipe(int filedes[2]);
+
+#endif
+
+#endif
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
new file mode 100644 (file)
index 0000000..8b3e79b
--- /dev/null
@@ -0,0 +1,278 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/sample-util.h>
+
+#include "play-memblockq.h"
+
+typedef struct memblockq_stream {
+    pa_msgobject parent;
+    pa_core *core;
+    pa_sink_input *sink_input;
+    pa_memblockq *memblockq;
+} memblockq_stream;
+
+enum {
+    MEMBLOCKQ_STREAM_MESSAGE_UNLINK,
+};
+
+PA_DECLARE_CLASS(memblockq_stream);
+#define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(memblockq_stream, pa_msgobject);
+
+static void memblockq_stream_unlink(memblockq_stream *u) {
+    pa_assert(u);
+
+    if (!u->sink_input)
+        return;
+
+    pa_sink_input_unlink(u->sink_input);
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    memblockq_stream_unref(u);
+}
+
+static void memblockq_stream_free(pa_object *o) {
+    memblockq_stream *u = MEMBLOCKQ_STREAM(o);
+    pa_assert(u);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    pa_xfree(u);
+}
+
+static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    memblockq_stream *u = MEMBLOCKQ_STREAM(o);
+    memblockq_stream_assert_ref(u);
+
+    switch (code) {
+        case MEMBLOCKQ_STREAM_MESSAGE_UNLINK:
+            memblockq_stream_unlink(u);
+            break;
+    }
+
+    return 0;
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    memblockq_stream_unlink(u);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT)
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return -1;
+
+    if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
+
+        if (pa_sink_input_safe_to_remove(i)) {
+
+            pa_memblockq_free(u->memblockq);
+            u->memblockq = NULL;
+
+            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+        }
+
+        return -1;
+    }
+
+    chunk->length = PA_MIN(chunk->length, nbytes);
+    pa_memblockq_drop(u->memblockq, chunk->length);
+
+    return 0;
+}
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(nbytes > 0);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return;
+
+    pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return;
+
+    pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+}
+
+pa_sink_input* pa_memblockq_sink_input_new(
+        pa_sink *sink,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_memblockq *q,
+        pa_cvolume *volume,
+        pa_proplist *p) {
+
+    memblockq_stream *u = NULL;
+    pa_sink_input_new_data data;
+
+    pa_assert(sink);
+    pa_assert(ss);
+
+    /* We allow creating this stream with no q set, so that it can be
+     * filled in later */
+
+    u = pa_msgobject_new(memblockq_stream);
+    u->parent.parent.free = memblockq_stream_free;
+    u->parent.process_msg = memblockq_stream_process_msg;
+    u->core = sink->core;
+    u->sink_input = NULL;
+    u->memblockq = NULL;
+
+    pa_sink_input_new_data_init(&data);
+    data.sink = sink;
+    data.driver = __FILE__;
+    pa_sink_input_new_data_set_sample_spec(&data, ss);
+    pa_sink_input_new_data_set_channel_map(&data, map);
+    pa_sink_input_new_data_set_volume(&data, volume);
+    pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+
+    u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+    pa_sink_input_new_data_done(&data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->userdata = u;
+
+    if (q)
+        pa_memblockq_sink_input_set_queue(u->sink_input, q);
+
+    /* The reference to u is dangling here, because we want
+     * to keep this stream around until it is fully played. */
+
+    /* This sink input is not "put" yet, i.e. pa_sink_input_put() has
+     * not been called! */
+
+    return pa_sink_input_ref(u->sink_input);
+
+fail:
+    if (u)
+        memblockq_stream_unref(u);
+
+    return NULL;
+}
+
+int pa_play_memblockq(
+        pa_sink *sink,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_memblockq *q,
+        pa_cvolume *volume,
+        pa_proplist *p,
+        uint32_t *sink_input_index) {
+
+    pa_sink_input *i;
+
+    pa_assert(sink);
+    pa_assert(ss);
+    pa_assert(q);
+
+    if (!(i = pa_memblockq_sink_input_new(sink, ss, map, q, volume, p)))
+        return -1;
+
+    pa_sink_input_put(i);
+
+    if (sink_input_index)
+        *sink_input_index = i->index;
+
+    pa_sink_input_unref(i);
+
+    return 0;
+}
+
+void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) {
+    memblockq_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = MEMBLOCKQ_STREAM(i->userdata);
+    memblockq_stream_assert_ref(u);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    if ((u->memblockq = q)) {
+        pa_memblockq_set_prebuf(q, 0);
+        pa_memblockq_set_silence(q, NULL);
+        pa_memblockq_willneed(q);
+    }
+}
diff --git a/src/pulsecore/play-memblockq.h b/src/pulsecore/play-memblockq.h
new file mode 100644 (file)
index 0000000..1a42867
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef fooplaymemblockqhfoo
+#define fooplaymemblockqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/sink.h>
+#include <pulsecore/memblockq.h>
+
+pa_sink_input* pa_memblockq_sink_input_new(
+        pa_sink *sink,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_memblockq *q,
+        pa_cvolume *volume,
+        pa_proplist *p);
+
+void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q);
+
+int pa_play_memblockq(
+    pa_sink *sink,
+    const pa_sample_spec *ss,
+    const pa_channel_map *map,
+    pa_memblockq *q,
+    pa_cvolume *cvolume,
+    pa_proplist *p,
+    uint32_t *sink_input_index);
+
+#endif
diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c
new file mode 100644 (file)
index 0000000..0dd4825
--- /dev/null
@@ -0,0 +1,64 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/play-memblockq.h>
+
+#include "play-memchunk.h"
+
+int pa_play_memchunk(
+        pa_sink *sink,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        const pa_memchunk *chunk,
+        pa_cvolume *volume,
+        pa_proplist *p,
+        uint32_t *sink_input_index) {
+
+    pa_memblockq *q;
+    int r;
+
+    pa_assert(sink);
+    pa_assert(ss);
+    pa_assert(chunk);
+
+    q = pa_memblockq_new(0, chunk->length, 0, pa_frame_size(ss), 1, 1, 0, NULL);
+    pa_assert_se(pa_memblockq_push(q, chunk) >= 0);
+
+    if ((r = pa_play_memblockq(sink, ss, map, q, volume, p, sink_input_index)) < 0) {
+        pa_memblockq_free(q);
+        return r;
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/play-memchunk.h b/src/pulsecore/play-memchunk.h
new file mode 100644 (file)
index 0000000..c312ae8
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef fooplaychunkhfoo
+#define fooplaychunkhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/sink.h>
+#include <pulsecore/memchunk.h>
+
+int pa_play_memchunk(
+    pa_sink *sink,
+    const pa_sample_spec *ss,
+    const pa_channel_map *map,
+    const pa_memchunk *chunk,
+    pa_cvolume *cvolume,
+    pa_proplist *p,
+    uint32_t *sink_input_index);
+
+#endif
diff --git a/src/pulsecore/poll.c b/src/pulsecore/poll.c
new file mode 100644 (file)
index 0000000..88ac21e
--- /dev/null
@@ -0,0 +1,194 @@
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/***
+   Based on work for the GNU C Library.
+   Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
+***/
+
+/* Poll the file descriptors described by the NFDS structures starting at
+   FDS.  If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
+   an event to occur; if TIMEOUT is -1, block until an event occurs.
+   Returns the number of file descriptors with events, zero if timed out,
+   or -1 for errors.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include "winsock.h"
+
+#ifndef HAVE_POLL_H
+
+#include <pulsecore/core-util.h>
+
+#include "poll.h"
+
+int poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
+    struct timeval tv;
+    fd_set rset, wset, xset;
+    struct pollfd *f;
+    int ready;
+    int maxfd = 0;
+    char data[64];
+
+    FD_ZERO (&rset);
+    FD_ZERO (&wset);
+    FD_ZERO (&xset);
+
+    if (nfds == 0) {
+        if (timeout >= 0) {
+            pa_msleep(timeout);
+            return 0;
+        }
+
+#ifdef OS_IS_WIN32
+        /*
+         * Windows does not support signals properly so waiting for them would
+         * mean a deadlock.
+         */
+        pa_msleep(100);
+        return 0;
+#else
+        return select(0, NULL, NULL, NULL, NULL);
+#endif
+    }
+
+    for (f = fds; f < &fds[nfds]; ++f) {
+        if (f->fd != -1) {
+            if (f->events & POLLIN)
+                FD_SET (f->fd, &rset);
+            if (f->events & POLLOUT)
+                FD_SET (f->fd, &wset);
+            if (f->events & POLLPRI)
+                FD_SET (f->fd, &xset);
+            if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
+                maxfd = f->fd;
+        }
+    }
+
+    tv.tv_sec = timeout / 1000;
+    tv.tv_usec = (timeout % 1000) * 1000;
+
+    ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset,
+                    SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
+                    SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv));
+    if ((ready == -1) && (errno == EBADF)) {
+        ready = 0;
+
+        FD_ZERO (&rset);
+        FD_ZERO (&wset);
+        FD_ZERO (&xset);
+
+        maxfd = -1;
+
+        for (f = fds; f < &fds[nfds]; ++f) {
+            if (f->fd != -1) {
+                fd_set sngl_rset, sngl_wset, sngl_xset;
+
+                FD_ZERO (&sngl_rset);
+                FD_ZERO (&sngl_wset);
+                FD_ZERO (&sngl_xset);
+
+                if (f->events & POLLIN)
+                    FD_SET (f->fd, &sngl_rset);
+                if (f->events & POLLOUT)
+                    FD_SET (f->fd, &sngl_wset);
+                if (f->events & POLLPRI)
+                    FD_SET (f->fd, &sngl_xset);
+                if (f->events & (POLLIN|POLLOUT|POLLPRI)) {
+                    struct timeval singl_tv;
+
+                    singl_tv.tv_sec = 0;
+                    singl_tv.tv_usec = 0;
+
+                    if (select((SELECT_TYPE_ARG1) f->fd, SELECT_TYPE_ARG234 &rset,
+                               SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
+                               SELECT_TYPE_ARG5 &singl_tv) != -1) {
+                        if (f->events & POLLIN)
+                            FD_SET (f->fd, &rset);
+                        if (f->events & POLLOUT)
+                            FD_SET (f->fd, &wset);
+                        if (f->events & POLLPRI)
+                            FD_SET (f->fd, &xset);
+                        if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
+                            maxfd = f->fd;
+                        ++ready;
+                    } else if (errno == EBADF)
+                        f->revents |= POLLNVAL;
+                }
+            }
+        }
+
+        if (ready) {
+        /* Linux alters the tv struct... but it shouldn't matter here ...
+         * as we're going to be a little bit out anyway as we've just eaten
+         * more than a couple of cpu cycles above */
+            ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset,
+                            SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
+                            SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv));
+        }
+    }
+
+#ifdef OS_IS_WIN32
+    errno = WSAGetLastError();
+#endif
+
+    if (ready > 0) {
+        ready = 0;
+        for (f = fds; f < &fds[nfds]; ++f) {
+            f->revents = 0;
+            if (f->fd != -1) {
+                if (FD_ISSET (f->fd, &rset)) {
+                    /* support for POLLHUP.  An hung up descriptor does not
+                       increase the return value! */
+                    if (recv (f->fd, data, 64, MSG_PEEK) == -1) {
+                        if (errno == ESHUTDOWN || errno == ECONNRESET ||
+                            errno == ECONNABORTED || errno == ENETRESET) {
+                            fprintf(stderr, "Hangup\n");
+                            f->revents |= POLLHUP;
+                        }
+                    }
+
+                    if (f->revents == 0)
+                        f->revents |= POLLIN;
+                }
+                if (FD_ISSET (f->fd, &wset))
+                    f->revents |= POLLOUT;
+                if (FD_ISSET (f->fd, &xset))
+                    f->revents |= POLLPRI;
+            }
+            if (f->revents)
+                ready++;
+        }
+    }
+
+    return ready;
+}
+
+#endif /* HAVE_SYS_POLL_H */
diff --git a/src/pulsecore/poll.h b/src/pulsecore/poll.h
new file mode 100644 (file)
index 0000000..86c37a0
--- /dev/null
@@ -0,0 +1,58 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/***
+   Based on work for the GNU C Library.
+   Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc.
+***/
+
+/* Event types that can be polled for.  These bits may be set in `events'
+   to indicate the interesting event types; they will appear in `revents'
+   to indicate the status of the file descriptor.  */
+#define POLLIN          0x001           /* There is data to read.  */
+#define POLLPRI         0x002           /* There is urgent data to read.  */
+#define POLLOUT         0x004           /* Writing now will not block.  */
+
+/* Event types always implicitly polled for.  These bits need not be set in
+   `events', but they will appear in `revents' to indicate the status of
+   the file descriptor.  */
+#define POLLERR         0x008           /* Error condition.  */
+#define POLLHUP         0x010           /* Hung up.  */
+#define POLLNVAL        0x020           /* Invalid polling request.  */
+
+
+/* Type used for the number of file descriptors.  */
+typedef unsigned long int nfds_t;
+
+/* Data structure describing a polling request.  */
+struct pollfd
+  {
+    int fd;                     /* File descriptor to poll.  */
+    short int events;           /* Types of events poller cares about.  */
+    short int revents;          /* Types of events that actually occurred.  */
+  };
+
+/* Poll the file descriptors described by the NFDS structures starting at
+   FDS.  If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
+   an event to occur; if TIMEOUT is -1, block until an event occurs.
+   Returns the number of file descriptors with events, zero if timed out,
+   or -1 for errors.  */
+extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
new file mode 100644 (file)
index 0000000..6005775
--- /dev/null
@@ -0,0 +1,118 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <locale.h>
+
+#include <pulse/proplist.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+
+#include "proplist-util.h"
+
+void pa_init_proplist(pa_proplist *p) {
+    int a, b;
+#ifndef HAVE_DECL_ENVIRON
+    extern char **environ;
+#endif
+    char **e;
+
+    pa_assert(p);
+
+    for (e = environ; *e; e++) {
+
+        if (pa_startswith(*e, "PULSE_PROP_")) {
+            size_t kl = strcspn(*e+11, "=");
+            char *k;
+
+            if ((*e)[11+kl] != '=')
+                continue;
+
+            if (!pa_utf8_valid(*e+11+kl+1))
+                continue;
+
+            k = pa_xstrndup(*e+11, kl);
+
+            if (pa_proplist_contains(p, k)) {
+                pa_xfree(k);
+                continue;
+            }
+
+            pa_proplist_sets(p, k, *e+11+kl+1);
+            pa_xfree(k);
+        }
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) {
+        char t[32];
+        pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid());
+        pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_ID, t);
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_USER)) {
+        char t[64];
+        if (pa_get_user_name(t, sizeof(t))) {
+            char *c = pa_utf8_filter(t);
+            pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_USER, c);
+            pa_xfree(c);
+        }
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_HOST)) {
+        char t[64];
+        if (pa_get_host_name(t, sizeof(t))) {
+            char *c = pa_utf8_filter(t);
+            pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_HOST, c);
+            pa_xfree(c);
+        }
+    }
+
+    a = pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY);
+    b = pa_proplist_contains(p, PA_PROP_APPLICATION_NAME);
+
+    if (!a || !b) {
+        char t[PATH_MAX];
+        if (pa_get_binary_name(t, sizeof(t))) {
+            char *c = pa_utf8_filter(t);
+
+            if (!a)
+                pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
+            if (!b)
+                pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, c);
+
+            pa_xfree(c);
+        }
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_LANGUAGE)) {
+        const char *l;
+
+        if ((l = setlocale(LC_MESSAGES, NULL)))
+            pa_proplist_sets(p, PA_PROP_APPLICATION_LANGUAGE, l);
+    }
+}
diff --git a/src/pulsecore/proplist-util.h b/src/pulsecore/proplist-util.h
new file mode 100644 (file)
index 0000000..c6bdc10
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef fooproplistutilutilhfoo
+#define fooproplistutilutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/proplist.h>
+
+void pa_init_proplist(pa_proplist *p);
+
+#endif
similarity index 53%
rename from polyp/props.c
rename to src/pulsecore/props.c
index 1046551be34882613841edb782757ebfd77180bd..23d432ec87a044ba31f80973e51813986d93471f 100644 (file)
@@ -1,41 +1,47 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
 
-#include "xmalloc.h"
 #include "props.h"
-#include "log.h"
 
-struct pa_property {
+typedef struct pa_property {
     char *name;  /* Points to memory allocated by the property subsystem */
     void *data;  /* Points to memory maintained by the caller */
-};
+} pa_property;
 
 /* Allocate a new property object */
-static struct pa_property* property_new(const char *name, void *data) {
-    struct pa_property* p;
-    assert(name && data);
-    
-    p = pa_xmalloc(sizeof(struct pa_property));
+static pa_property* property_new(const char *name, void *data) {
+    pa_property* p;
+
+    pa_assert(name);
+    pa_assert(data);
+
+    p = pa_xnew(pa_property, 1);
     p->name = pa_xstrdup(name);
     p->data = data;
 
@@ -43,16 +49,19 @@ static struct pa_property* property_new(const char *name, void *data) {
 }
 
 /* Free a property object */
-static void property_free(struct pa_property *p) {
-    assert(p);
+static void property_free(pa_property *p) {
+    pa_assert(p);
 
     pa_xfree(p->name);
     pa_xfree(p);
 }
 
-void* pa_property_get(struct pa_core *c, const char *name) {
-    struct pa_property *p;
-    assert(c && name && c->properties);
+void* pa_property_get(pa_core *c, const char *name) {
+    pa_property *p;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(c->properties);
 
     if (!(p = pa_hashmap_get(c->properties, name)))
         return NULL;
@@ -60,9 +69,13 @@ void* pa_property_get(struct pa_core *c, const char *name) {
     return p->data;
 }
 
-int pa_property_set(struct pa_core *c, const char *name, void *data) {
-    struct pa_property *p;
-    assert(c && name && data && c->properties);
+int pa_property_set(pa_core *c, const char *name, void *data) {
+    pa_property *p;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(data);
+    pa_assert(c->properties);
 
     if (pa_hashmap_get(c->properties, name))
         return -1;
@@ -72,47 +85,53 @@ int pa_property_set(struct pa_core *c, const char *name, void *data) {
     return 0;
 }
 
-int pa_property_remove(struct pa_core *c, const char *name) {
-    struct pa_property *p;
-    assert(c && name && c->properties);
+int pa_property_remove(pa_core *c, const char *name) {
+    pa_property *p;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(c->properties);
 
     if (!(p = pa_hashmap_remove(c->properties, name)))
         return -1;
-    
+
     property_free(p);
     return 0;
 }
 
-void pa_property_init(struct pa_core *c) {
-    assert(c);
+void pa_property_init(pa_core *c) {
+    pa_assert(c);
 
     c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 }
 
-void pa_property_cleanup(struct pa_core *c) {
-    assert(c);
+void pa_property_cleanup(pa_core *c) {
+    pa_assert(c);
 
     if (!c->properties)
         return;
 
-    assert(!pa_hashmap_ncontents(c->properties));
+    pa_assert(!pa_hashmap_size(c->properties));
 
     pa_hashmap_free(c->properties, NULL, NULL);
     c->properties = NULL;
-    
+
 }
 
-void pa_property_dump(struct pa_core *c, struct pa_strbuf *s) {
+void pa_property_dump(pa_core *c, pa_strbuf *s) {
     void *state = NULL;
-    struct pa_property *p;
-    assert(c && s);
+    pa_property *p;
+
+    pa_assert(c);
+    pa_assert(s);
 
     while ((p = pa_hashmap_iterate(c->properties, &state, NULL)))
         pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data);
 }
 
-int pa_property_replace(struct pa_core *c, const char *name, void *data) {
-    assert(c && name);
+int pa_property_replace(pa_core *c, const char *name, void *data) {
+    pa_assert(c);
+    pa_assert(name);
 
     pa_property_remove(c, name);
     return pa_property_set(c, name, data);
similarity index 67%
rename from polyp/props.h
rename to src/pulsecore/props.h
index 9b379cf16e0171716b31438dafe2a5007287023e..95db229b4ee06740f4b0d2030569ec6103bcdf7e 100644 (file)
@@ -1,29 +1,29 @@
 #ifndef foopropshfoo
 #define foopropshfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "core.h"
-#include "strbuf.h"
+#include <pulsecore/core.h>
+#include <pulsecore/strbuf.h>
 
 /* The property subsystem is to be used to share data between
  * modules. Consider them to be kind of "global" variables for a
  * reference counting themselves. */
 
 /* Return a pointer to the value of the specified property. */
-void* pa_property_get(struct pa_core *c, const char *name);
+void* pa_property_get(pa_core *c, const char *name);
 
 /* Set the property 'name' to 'data'. This function fails in case a
  * property by this name already exists. The property data is not
  * copied or reference counted. This is the caller's job. */
-int pa_property_set(struct pa_core *c, const char *name, void *data);
+int pa_property_set(pa_core *c, const char *name, void *data);
 
 /* Remove the specified property. Return non-zero on failure */
-int pa_property_remove(struct pa_core *c, const char *name);
+int pa_property_remove(pa_core *c, const char *name);
 
 /* A combination of pa_property_remove() and pa_property_set() */
-int pa_property_replace(struct pa_core *c, const char *name, void *data);
+int pa_property_replace(pa_core *c, const char *name, void *data);
 
 /* Free all memory used by the property system */
-void pa_property_cleanup(struct pa_core *c);
+void pa_property_cleanup(pa_core *c);
 
 /* Initialize the properties subsystem */
-void pa_property_init(struct pa_core *c);
+void pa_property_init(pa_core *c);
 
 /* Dump the current set of properties */
-void pa_property_dump(struct pa_core *c, struct pa_strbuf *s);
+void pa_property_dump(pa_core *c, pa_strbuf *s);
 
 #endif
diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c
new file mode 100644 (file)
index 0000000..30cb475
--- /dev/null
@@ -0,0 +1,103 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/cli.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "protocol-cli.h"
+
+/* Don't allow more than this many concurrent connections */
+#define MAX_CONNECTIONS 25
+
+struct pa_protocol_cli {
+    pa_module *module;
+    pa_core *core;
+    pa_socket_server*server;
+    pa_idxset *connections;
+};
+
+static void cli_eof_cb(pa_cli*c, void*userdata) {
+    pa_protocol_cli *p = userdata;
+    pa_assert(p);
+
+    pa_idxset_remove_by_data(p->connections, c, NULL);
+    pa_cli_free(c);
+}
+
+static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+    pa_protocol_cli *p = userdata;
+    pa_cli *c;
+
+    pa_assert(s);
+    pa_assert(io);
+    pa_assert(p);
+
+    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+
+    c = pa_cli_new(p->core, io, p->module);
+    pa_cli_set_eof_callback(c, cli_eof_cb, p);
+
+    pa_idxset_put(p->connections, c, NULL);
+}
+
+pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
+    pa_protocol_cli* p;
+
+    pa_core_assert_ref(core);
+    pa_assert(server);
+
+    p = pa_xnew(pa_protocol_cli, 1);
+    p->module = m;
+    p->core = core;
+    p->server = pa_socket_server_ref(server);
+    p->connections = pa_idxset_new(NULL, NULL);
+
+    pa_socket_server_set_callback(p->server, on_connection, p);
+
+    return p;
+}
+
+static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
+    pa_assert(p);
+
+    pa_cli_free(p);
+}
+
+void pa_protocol_cli_free(pa_protocol_cli *p) {
+    pa_assert(p);
+
+    pa_idxset_free(p->connections, free_connection, NULL);
+    pa_socket_server_unref(p->server);
+    pa_xfree(p);
+}
diff --git a/src/pulsecore/protocol-cli.h b/src/pulsecore/protocol-cli.h
new file mode 100644 (file)
index 0000000..8922ac6
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef fooprotocolclihfoo
+#define fooprotocolclihfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/socket-server.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+
+typedef struct pa_protocol_cli pa_protocol_cli;
+
+pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
+void pa_protocol_cli_free(pa_protocol_cli *n);
+
+#endif
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
new file mode 100644 (file)
index 0000000..db1b430
--- /dev/null
@@ -0,0 +1,1512 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <limits.h>
+
+#include <pulse/sample.h>
+#include <pulse/timeval.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/esound.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/source.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/authkey.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/ipacl.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread-mq.h>
+
+#include "endianmacros.h"
+
+#include "protocol-esound.h"
+
+/* Don't accept more connection than this */
+#define MAX_CONNECTIONS 64
+
+/* Kick a client if it doesn't authenticate within this time */
+#define AUTH_TIMEOUT 5
+
+#define DEFAULT_COOKIE_FILE ".esd_auth"
+
+#define PLAYBACK_BUFFER_SECONDS (.25)
+#define PLAYBACK_BUFFER_FRAGMENTS (10)
+#define RECORD_BUFFER_SECONDS (5)
+
+#define MAX_CACHE_SAMPLE_SIZE (2048000)
+
+#define DEFAULT_SINK_LATENCY (150*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (150*PA_USEC_PER_MSEC)
+
+#define SCACHE_PREFIX "esound."
+
+/* This is heavily based on esound's code */
+
+typedef struct connection {
+    pa_msgobject parent;
+
+    uint32_t index;
+    pa_bool_t dead;
+    pa_protocol_esound *protocol;
+    pa_iochannel *io;
+    pa_client *client;
+    pa_bool_t authorized, swap_byte_order;
+    void *write_data;
+    size_t write_data_alloc, write_data_index, write_data_length;
+    void *read_data;
+    size_t read_data_alloc, read_data_length;
+    esd_proto_t request;
+    esd_client_state_t state;
+    pa_sink_input *sink_input;
+    pa_source_output *source_output;
+    pa_memblockq *input_memblockq, *output_memblockq;
+    pa_defer_event *defer_event;
+
+    char *original_name;
+
+    struct {
+        pa_memblock *current_memblock;
+        size_t memblock_index;
+        pa_atomic_t missing;
+        pa_bool_t underrun;
+    } playback;
+
+    struct {
+        pa_memchunk memchunk;
+        char *name;
+        pa_sample_spec sample_spec;
+    } scache;
+
+    pa_time_event *auth_timeout_event;
+} connection;
+
+PA_DECLARE_CLASS(connection);
+#define CONNECTION(o) (connection_cast(o))
+static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
+
+struct pa_protocol_esound {
+    pa_module *module;
+    pa_core *core;
+    pa_bool_t public;
+    pa_socket_server *server;
+    pa_idxset *connections;
+
+    char *sink_name, *source_name;
+    unsigned n_player;
+    uint8_t esd_key[ESD_KEY_LEN];
+    pa_ip_acl *auth_ip_acl;
+};
+
+enum {
+    SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+    SINK_INPUT_MESSAGE_DISABLE_PREBUF
+};
+
+enum {
+    CONNECTION_MESSAGE_REQUEST_DATA,
+    CONNECTION_MESSAGE_POST_DATA,
+    CONNECTION_MESSAGE_UNLINK_CONNECTION
+};
+
+typedef struct proto_handler {
+    size_t data_length;
+    int (*proc)(connection *c, esd_proto_t request, const void *data, size_t length);
+    const char *description;
+} esd_proto_handler_info_t;
+
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_kill_cb(pa_sink_input *i);
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
+static void source_output_kill_cb(pa_source_output *o);
+
+static int esd_proto_connect(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_record(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_get_latency(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length);
+
+/* the big map of protocol handler info */
+static struct proto_handler proto_map[ESD_PROTO_MAX] = {
+    { ESD_KEY_LEN + sizeof(int),      esd_proto_connect, "connect" },
+    { ESD_KEY_LEN + sizeof(int),      NULL, "lock" },
+    { ESD_KEY_LEN + sizeof(int),      NULL, "unlock" },
+
+    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" },
+    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" },
+    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" },
+
+    { ESD_NAME_MAX + 3 * sizeof(int), esd_proto_sample_cache, "sample cache" },                      /* 6 */
+    { sizeof(int),                    esd_proto_sample_free_or_play, "sample free" },
+    { sizeof(int),                    esd_proto_sample_free_or_play, "sample play" },                /* 8 */
+    { sizeof(int),                    NULL, "sample loop" },
+    { sizeof(int),                    NULL, "sample stop" },
+    { -1,                             NULL, "TODO: sample kill" },
+
+    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "standby" },  /* NOOP! */
+    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "resume" },   /* NOOP! */         /* 13 */
+
+    { ESD_NAME_MAX,                   esd_proto_sample_get_id, "sample getid" },                     /* 14 */
+    { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
+
+    { sizeof(int),                    esd_proto_server_info, "server info" },
+    { sizeof(int),                    esd_proto_all_info, "all info" },
+    { -1,                             NULL, "TODO: subscribe" },
+    { -1,                             NULL, "TODO: unsubscribe" },
+
+    { 3 * sizeof(int),                esd_proto_stream_pan, "stream pan"},
+    { 3 * sizeof(int),                NULL, "sample pan" },
+
+    { sizeof(int),                    NULL, "standby mode" },
+    { 0,                              esd_proto_get_latency, "get latency" }
+};
+
+static void connection_unlink(connection *c) {
+    pa_assert(c);
+
+    if (!c->protocol)
+        return;
+
+    if (c->sink_input) {
+        pa_sink_input_unlink(c->sink_input);
+        pa_sink_input_unref(c->sink_input);
+        c->sink_input = NULL;
+    }
+
+    if (c->source_output) {
+        pa_source_output_unlink(c->source_output);
+        pa_source_output_unref(c->source_output);
+        c->source_output = NULL;
+    }
+
+    if (c->client) {
+        pa_client_free(c->client);
+        c->client = NULL;
+    }
+
+    if (c->state == ESD_STREAMING_DATA)
+        c->protocol->n_player--;
+
+    if (c->io) {
+        pa_iochannel_free(c->io);
+        c->io = NULL;
+    }
+
+    if (c->defer_event) {
+        c->protocol->core->mainloop->defer_free(c->defer_event);
+        c->defer_event = NULL;
+    }
+
+    if (c->auth_timeout_event) {
+        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+        c->auth_timeout_event = NULL;
+    }
+
+    pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+    c->protocol = NULL;
+    connection_unref(c);
+}
+
+static void connection_free(pa_object *obj) {
+    connection *c = CONNECTION(obj);
+    pa_assert(c);
+
+    if (c->input_memblockq)
+        pa_memblockq_free(c->input_memblockq);
+    if (c->output_memblockq)
+        pa_memblockq_free(c->output_memblockq);
+
+    if (c->playback.current_memblock)
+        pa_memblock_unref(c->playback.current_memblock);
+
+    pa_xfree(c->read_data);
+    pa_xfree(c->write_data);
+
+    if (c->scache.memchunk.memblock)
+        pa_memblock_unref(c->scache.memchunk.memblock);
+    pa_xfree(c->scache.name);
+
+    pa_xfree(c->original_name);
+    pa_xfree(c);
+}
+
+static void connection_write_prepare(connection *c, size_t length) {
+    size_t t;
+    pa_assert(c);
+
+    t = c->write_data_length+length;
+
+    if (c->write_data_alloc < t)
+        c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t);
+
+    pa_assert(c->write_data);
+}
+
+static void connection_write(connection *c, const void *data, size_t length) {
+    size_t i;
+    pa_assert(c);
+
+    c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+
+    connection_write_prepare(c, length);
+
+    pa_assert(c->write_data);
+
+    i = c->write_data_length;
+    c->write_data_length += length;
+
+    memcpy((uint8_t*) c->write_data + i, data, length);
+}
+
+static void format_esd2native(int format, pa_bool_t swap_bytes, pa_sample_spec *ss) {
+    pa_assert(ss);
+
+    ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
+    if ((format & ESD_MASK_BITS) == ESD_BITS16)
+        ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE;
+    else
+        ss->format = PA_SAMPLE_U8;
+}
+
+static int format_native2esd(pa_sample_spec *ss) {
+    int format = 0;
+
+    format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
+    format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO;
+
+    return format;
+}
+
+#define CHECK_VALIDITY(expression, ...) do { \
+    if (!(expression)) { \
+        pa_log_warn(__FILE__ ": " __VA_ARGS__); \
+        return -1; \
+    } \
+} while(0);
+
+/*** esound commands ***/
+
+static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+    uint32_t ekey;
+    int ok;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
+
+    if (!c->authorized) {
+        if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) {
+            pa_log("kicked client with invalid authorization key.");
+            return -1;
+        }
+
+        c->authorized = TRUE;
+        if (c->auth_timeout_event) {
+            c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+            c->auth_timeout_event = NULL;
+        }
+    }
+
+    data = (const char*)data + ESD_KEY_LEN;
+
+    memcpy(&ekey, data, sizeof(uint32_t));
+    if (ekey == ESD_ENDIAN_KEY)
+        c->swap_byte_order = FALSE;
+    else if (ekey == ESD_SWAP_ENDIAN_KEY)
+        c->swap_byte_order = TRUE;
+    else {
+        pa_log_warn("Client sent invalid endian key");
+        return -1;
+    }
+
+    ok = 1;
+    connection_write(c, &ok, sizeof(int));
+    return 0;
+}
+
+static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+    char name[ESD_NAME_MAX], *utf8_name;
+    int32_t format, rate;
+    pa_sample_spec ss;
+    size_t l;
+    pa_sink *sink = NULL;
+    pa_sink_input_new_data sdata;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));
+
+    memcpy(&format, data, sizeof(int32_t));
+    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+    data = (const char*) data + sizeof(int32_t);
+
+    memcpy(&rate, data, sizeof(int32_t));
+    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+    data = (const char*) data + sizeof(int32_t);
+
+    ss.rate = rate;
+    format_esd2native(format, c->swap_byte_order, &ss);
+
+    CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification");
+
+    if (c->protocol->sink_name) {
+        sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1);
+        CHECK_VALIDITY(sink, "No such sink: %s", c->protocol->sink_name);
+    }
+
+    pa_strlcpy(name, data, sizeof(name));
+
+    utf8_name = pa_utf8_filter(name);
+    pa_client_set_name(c->client, utf8_name);
+    pa_xfree(utf8_name);
+
+    c->original_name = pa_xstrdup(name);
+
+    pa_assert(!c->sink_input && !c->input_memblockq);
+
+    pa_sink_input_new_data_init(&sdata);
+    sdata.driver = __FILE__;
+    sdata.module = c->protocol->module;
+    sdata.client = c->client;
+    sdata.sink = sink;
+    pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
+    pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
+
+    c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0);
+    pa_sink_input_new_data_done(&sdata);
+
+    CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
+
+    l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
+    c->input_memblockq = pa_memblockq_new(
+            0,
+            l,
+            l,
+            pa_frame_size(&ss),
+            (size_t) -1,
+            l/PLAYBACK_BUFFER_FRAGMENTS,
+            0,
+            NULL);
+    pa_iochannel_socket_set_rcvbuf(c->io, l);
+
+    c->sink_input->parent.process_msg = sink_input_process_msg;
+    c->sink_input->pop = sink_input_pop_cb;
+    c->sink_input->process_rewind = sink_input_process_rewind_cb;
+    c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    c->sink_input->kill = sink_input_kill_cb;
+    c->sink_input->userdata = c;
+
+    pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
+    c->state = ESD_STREAMING_DATA;
+
+    c->protocol->n_player++;
+
+    pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
+
+    pa_sink_input_put(c->sink_input);
+
+    return 0;
+}
+
+static int esd_proto_stream_record(connection *c, esd_proto_t request, const void *data, size_t length) {
+    char name[ESD_NAME_MAX], *utf8_name;
+    int32_t format, rate;
+    pa_source *source = NULL;
+    pa_sample_spec ss;
+    size_t l;
+    pa_source_output_new_data sdata;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));
+
+    memcpy(&format, data, sizeof(int32_t));
+    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+    data = (const char*) data + sizeof(int32_t);
+
+    memcpy(&rate, data, sizeof(int32_t));
+    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+    data = (const char*) data + sizeof(int32_t);
+
+    ss.rate = rate;
+    format_esd2native(format, c->swap_byte_order, &ss);
+
+    CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
+
+    if (request == ESD_PROTO_STREAM_MON) {
+        pa_sink* sink;
+
+        if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
+            pa_log("no such sink.");
+            return -1;
+        }
+
+        if (!(source = sink->monitor_source)) {
+            pa_log("no such monitor source.");
+            return -1;
+        }
+    } else {
+        pa_assert(request == ESD_PROTO_STREAM_REC);
+
+        if (c->protocol->source_name) {
+            if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) {
+                pa_log("no such source.");
+                return -1;
+            }
+        }
+    }
+
+    pa_strlcpy(name, data, sizeof(name));
+
+    utf8_name = pa_utf8_filter(name);
+    pa_client_set_name(c->client, utf8_name);
+    pa_xfree(utf8_name);
+
+    c->original_name = pa_xstrdup(name);
+
+    pa_assert(!c->output_memblockq && !c->source_output);
+
+    pa_source_output_new_data_init(&sdata);
+    sdata.driver = __FILE__;
+    sdata.module = c->protocol->module;
+    sdata.client = c->client;
+    sdata.source = source;
+    pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
+    pa_source_output_new_data_set_sample_spec(&sdata, &ss);
+
+    c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0);
+    pa_source_output_new_data_done(&sdata);
+
+    CHECK_VALIDITY(c->source_output, "Failed to create source output.");
+
+    l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
+    c->output_memblockq = pa_memblockq_new(
+            0,
+            l,
+            l,
+            pa_frame_size(&ss),
+            1,
+            0,
+            0,
+            NULL);
+    pa_iochannel_socket_set_sndbuf(c->io, l);
+
+    c->source_output->push = source_output_push_cb;
+    c->source_output->kill = source_output_kill_cb;
+    c->source_output->get_latency = source_output_get_latency_cb;
+    c->source_output->userdata = c;
+
+    pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
+    c->state = ESD_STREAMING_DATA;
+
+    c->protocol->n_player++;
+
+    pa_source_output_put(c->source_output);
+
+    return 0;
+}
+
+static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+    pa_sink *sink;
+    int32_t latency;
+
+    connection_ref(c);
+    pa_assert(!data);
+    pa_assert(length == 0);
+
+    if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
+        latency = 0;
+    else {
+        double usec = pa_sink_get_latency(sink);
+        latency = (int) ((usec*44100)/1000000);
+    }
+
+    latency = PA_MAYBE_INT32_SWAP(c->swap_byte_order, latency);
+    connection_write(c, &latency, sizeof(int32_t));
+    return 0;
+}
+
+static int esd_proto_server_info(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+    int32_t rate = 44100, format = ESD_STEREO|ESD_BITS16;
+    int32_t response;
+    pa_sink *sink;
+
+    connection_ref(c);
+    pa_assert(data);
+    pa_assert(length == sizeof(int32_t));
+
+    if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
+        rate = sink->sample_spec.rate;
+        format = format_native2esd(&sink->sample_spec);
+    }
+
+    connection_write_prepare(c, sizeof(int32_t) * 3);
+
+    response = 0;
+    connection_write(c, &response, sizeof(int32_t));
+    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+    connection_write(c, &rate, sizeof(int32_t));
+    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+    connection_write(c, &format, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length) {
+    size_t t, k, s;
+    connection *conn;
+    uint32_t idx = PA_IDXSET_INVALID;
+    unsigned nsamples;
+    char terminator[sizeof(int32_t)*6+ESD_NAME_MAX];
+
+    connection_ref(c);
+    pa_assert(data);
+    pa_assert(length == sizeof(int32_t));
+
+    if (esd_proto_server_info(c, request, data, length) < 0)
+        return -1;
+
+    k = sizeof(int32_t)*5+ESD_NAME_MAX;
+    s = sizeof(int32_t)*6+ESD_NAME_MAX;
+    nsamples = c->protocol->core->scache ? pa_idxset_size(c->protocol->core->scache) : 0;
+    t = s*(nsamples+1) + k*(c->protocol->n_player+1);
+
+    connection_write_prepare(c, t);
+
+    memset(terminator, 0, sizeof(terminator));
+
+    for (conn = pa_idxset_first(c->protocol->connections, &idx); conn; conn = pa_idxset_next(c->protocol->connections, &idx)) {
+        int32_t id, format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE;
+        char name[ESD_NAME_MAX];
+
+        if (conn->state != ESD_STREAMING_DATA)
+            continue;
+
+        pa_assert(t >= k*2+s);
+
+        if (conn->sink_input) {
+            pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input);
+            rate = conn->sink_input->sample_spec.rate;
+            lvolume = (volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
+            rvolume = (volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
+            format = format_native2esd(&conn->sink_input->sample_spec);
+        }
+
+        /* id */
+        id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1));
+        connection_write(c, &id, sizeof(int32_t));
+
+        /* name */
+        memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
+        if (conn->original_name)
+            strncpy(name, conn->original_name, ESD_NAME_MAX);
+        else if (conn->client && pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME))
+            strncpy(name, pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME), ESD_NAME_MAX);
+        connection_write(c, name, ESD_NAME_MAX);
+
+        /* rate */
+        rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+        connection_write(c, &rate, sizeof(int32_t));
+
+        /* left */
+        lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);
+        connection_write(c, &lvolume, sizeof(int32_t));
+
+        /*right*/
+        rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);
+        connection_write(c, &rvolume, sizeof(int32_t));
+
+        /*format*/
+        format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+        connection_write(c, &format, sizeof(int32_t));
+
+        t -= k;
+    }
+
+    pa_assert(t == s*(nsamples+1)+k);
+    t -= k;
+
+    connection_write(c, terminator, k);
+
+    if (nsamples) {
+        pa_scache_entry *ce;
+
+        idx = PA_IDXSET_INVALID;
+        for (ce = pa_idxset_first(c->protocol->core->scache, &idx); ce; ce = pa_idxset_next(c->protocol->core->scache, &idx)) {
+            int32_t id, rate, lvolume, rvolume, format, len;
+            char name[ESD_NAME_MAX];
+
+            pa_assert(t >= s*2);
+
+            /* id */
+            id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
+            connection_write(c, &id, sizeof(int32_t));
+
+            /* name */
+            memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
+            if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0)
+                strncpy(name, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);
+            else
+                pa_snprintf(name, ESD_NAME_MAX, "native.%s", ce->name);
+            connection_write(c, name, ESD_NAME_MAX);
+
+            /* rate */
+            rate = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);
+            connection_write(c, &rate, sizeof(int32_t));
+
+            /* left */
+            lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+            connection_write(c, &lvolume, sizeof(int32_t));
+
+            /*right*/
+            rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+            connection_write(c, &rvolume, sizeof(int32_t));
+
+            /*format*/
+            format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec));
+            connection_write(c, &format, sizeof(int32_t));
+
+            /*length*/
+            len = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);
+            connection_write(c, &len, sizeof(int32_t));
+
+            t -= s;
+        }
+    }
+
+    pa_assert(t == s);
+
+    connection_write(c, terminator, s);
+
+    return 0;
+}
+
+static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+    int32_t ok;
+    uint32_t idx, lvolume, rvolume;
+    connection *conn;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == sizeof(int32_t)*3);
+
+    memcpy(&idx, data, sizeof(uint32_t));
+    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+    data = (const char*)data + sizeof(uint32_t);
+
+    memcpy(&lvolume, data, sizeof(uint32_t));
+    lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
+    data = (const char*)data + sizeof(uint32_t);
+
+    memcpy(&rvolume, data, sizeof(uint32_t));
+    rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
+    data = (const char*)data + sizeof(uint32_t);
+
+    if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) {
+        pa_cvolume volume;
+        volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+        volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+        volume.channels = 2;
+        pa_sink_input_set_volume(conn->sink_input, &volume);
+        ok = 1;
+    } else
+        ok = 0;
+
+    connection_write(c, &ok, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+    pa_sample_spec ss;
+    int32_t format, rate, sc_length;
+    uint32_t idx;
+    char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == (ESD_NAME_MAX+3*sizeof(int32_t)));
+
+    memcpy(&format, data, sizeof(int32_t));
+    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
+    data = (const char*)data + sizeof(int32_t);
+
+    memcpy(&rate, data, sizeof(int32_t));
+    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+    data = (const char*)data + sizeof(int32_t);
+
+    ss.rate = rate;
+    format_esd2native(format, c->swap_byte_order, &ss);
+
+    CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
+
+    memcpy(&sc_length, data, sizeof(int32_t));
+    sc_length = PA_MAYBE_INT32_SWAP(c->swap_byte_order, sc_length);
+    data = (const char*)data + sizeof(int32_t);
+
+    CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length);
+
+    strcpy(name, SCACHE_PREFIX);
+    pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
+
+    CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
+
+    pa_assert(!c->scache.memchunk.memblock);
+    c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, sc_length);
+    c->scache.memchunk.index = 0;
+    c->scache.memchunk.length = sc_length;
+    c->scache.sample_spec = ss;
+    pa_assert(!c->scache.name);
+    c->scache.name = pa_xstrdup(name);
+
+    c->state = ESD_CACHING_SAMPLE;
+
+    pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, c->client->proplist, &idx);
+
+    idx += 1;
+    connection_write(c, &idx, sizeof(uint32_t));
+
+    return 0;
+}
+
+static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+    int32_t ok;
+    uint32_t idx;
+    char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == ESD_NAME_MAX);
+
+    strcpy(name, SCACHE_PREFIX);
+    pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
+
+    CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
+
+    ok = -1;
+    if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID)
+        ok = idx + 1;
+
+    connection_write(c, &ok, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t ok;
+    const char *name;
+    uint32_t idx;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == sizeof(int32_t));
+
+    memcpy(&idx, data, sizeof(uint32_t));
+    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+
+    ok = 0;
+
+    if ((name = pa_scache_get_name_by_id(c->protocol->core, idx))) {
+        if (request == ESD_PROTO_SAMPLE_PLAY) {
+            pa_sink *sink;
+
+            if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
+                if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
+                    ok = idx + 1;
+        } else {
+            pa_assert(request == ESD_PROTO_SAMPLE_FREE);
+
+            if (pa_scache_remove_item(c->protocol->core, name) >= 0)
+                ok = idx + 1;
+        }
+    }
+
+    connection_write(c, &ok, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_standby_or_resume(connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) {
+    int32_t ok;
+
+    connection_assert_ref(c);
+
+    connection_write_prepare(c, sizeof(int32_t) * 2);
+
+    ok = 1;
+    connection_write(c, &ok, sizeof(int32_t));
+    connection_write(c, &ok, sizeof(int32_t));
+
+    return 0;
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(pa_client *c) {
+    pa_assert(c);
+
+    connection_unlink(CONNECTION(c->userdata));
+}
+
+/*** pa_iochannel callbacks ***/
+
+static int do_read(connection *c) {
+    connection_assert_ref(c);
+
+/*     pa_log("READ"); */
+
+    if (c->state == ESD_NEXT_REQUEST) {
+        ssize_t r;
+        pa_assert(c->read_data_length < sizeof(c->request));
+
+        if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
+            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            return -1;
+        }
+
+        if ((c->read_data_length+= r) >= sizeof(c->request)) {
+            struct proto_handler *handler;
+
+            c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
+
+            if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
+                pa_log("recieved invalid request.");
+                return -1;
+            }
+
+            handler = proto_map+c->request;
+
+/*             pa_log("executing request #%u", c->request); */
+
+            if (!handler->proc) {
+                pa_log("recieved unimplemented request #%u.", c->request);
+                return -1;
+            }
+
+            if (handler->data_length == 0) {
+                c->read_data_length = 0;
+
+                if (handler->proc(c, c->request, NULL, 0) < 0)
+                    return -1;
+
+            } else {
+                if (c->read_data_alloc < handler->data_length)
+                    c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length);
+                pa_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;
+
+        pa_assert(handler->proc);
+
+        pa_assert(c->read_data && c->read_data_length < handler->data_length);
+
+        if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
+            if (r < 0 && (errno == EINTR || errno == EAGAIN))
+                return 0;
+
+            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            return -1;
+        }
+
+        if ((c->read_data_length += r) >= handler->data_length) {
+            size_t l = c->read_data_length;
+            pa_assert(handler->proc);
+
+            c->state = ESD_NEXT_REQUEST;
+            c->read_data_length = 0;
+
+            if (handler->proc(c, c->request, c->read_data, l) < 0)
+                return -1;
+        }
+    } else if (c->state == ESD_CACHING_SAMPLE) {
+        ssize_t r;
+        void *p;
+
+        pa_assert(c->scache.memchunk.memblock);
+        pa_assert(c->scache.name);
+        pa_assert(c->scache.memchunk.index < c->scache.memchunk.length);
+
+        p = pa_memblock_acquire(c->scache.memchunk.memblock);
+        r = pa_iochannel_read(c->io, (uint8_t*) p+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index);
+        pa_memblock_release(c->scache.memchunk.memblock);
+
+        if (r <= 0) {
+            if (r < 0 && (errno == EINTR || errno == EAGAIN))
+                return 0;
+
+            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            return -1;
+        }
+
+        c->scache.memchunk.index += r;
+        pa_assert(c->scache.memchunk.index <= c->scache.memchunk.length);
+
+        if (c->scache.memchunk.index == c->scache.memchunk.length) {
+            uint32_t idx;
+
+            c->scache.memchunk.index = 0;
+            pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx);
+
+            pa_memblock_unref(c->scache.memchunk.memblock);
+            c->scache.memchunk.memblock = NULL;
+            c->scache.memchunk.index = c->scache.memchunk.length = 0;
+
+            pa_xfree(c->scache.name);
+            c->scache.name = NULL;
+
+            c->state = ESD_NEXT_REQUEST;
+
+            idx += 1;
+            connection_write(c, &idx, sizeof(uint32_t));
+        }
+
+    } else if (c->state == ESD_STREAMING_DATA && c->sink_input) {
+        pa_memchunk chunk;
+        ssize_t r;
+        size_t l;
+        void *p;
+        size_t space;
+
+        pa_assert(c->input_memblockq);
+
+/*         pa_log("STREAMING_DATA"); */
+
+        if (!(l = pa_atomic_load(&c->playback.missing)))
+            return 0;
+
+        if (c->playback.current_memblock) {
+
+            space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
+
+            if (space <= 0) {
+                pa_memblock_unref(c->playback.current_memblock);
+                c->playback.current_memblock = NULL;
+            }
+        }
+
+        if (!c->playback.current_memblock) {
+            pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1));
+            c->playback.memblock_index = 0;
+
+            space = pa_memblock_get_length(c->playback.current_memblock);
+        }
+
+        if (l > space)
+            l = space;
+
+        p = pa_memblock_acquire(c->playback.current_memblock);
+        r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l);
+        pa_memblock_release(c->playback.current_memblock);
+
+        if (r <= 0) {
+
+            if (r < 0 && (errno == EINTR || errno == EAGAIN))
+                return 0;
+
+            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            return -1;
+        }
+
+        chunk.memblock = c->playback.current_memblock;
+        chunk.index = c->playback.memblock_index;
+        chunk.length = r;
+
+        c->playback.memblock_index += r;
+
+        pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
+        pa_atomic_sub(&c->playback.missing, r);
+    }
+
+    return 0;
+}
+
+static int do_write(connection *c) {
+    connection_assert_ref(c);
+
+/*     pa_log("WRITE"); */
+
+    if (c->write_data_length) {
+        ssize_t r;
+
+        pa_assert(c->write_data_index < c->write_data_length);
+        if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
+
+            if (r < 0 && (errno == EINTR || errno == EAGAIN))
+                return 0;
+
+            pa_log("write(): %s", pa_cstrerror(errno));
+            return -1;
+        }
+
+        if ((c->write_data_index +=r) >= c->write_data_length)
+            c->write_data_length = c->write_data_index = 0;
+
+    } else if (c->state == ESD_STREAMING_DATA && c->source_output) {
+        pa_memchunk chunk;
+        ssize_t r;
+        void *p;
+
+        if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
+            return 0;
+
+        pa_assert(chunk.memblock);
+        pa_assert(chunk.length);
+
+        p = pa_memblock_acquire(chunk.memblock);
+        r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
+        pa_memblock_release(chunk.memblock);
+
+        pa_memblock_unref(chunk.memblock);
+
+        if (r < 0) {
+
+            if (r < 0 && (errno == EINTR || errno == EAGAIN))
+                return 0;
+
+            pa_log("write(): %s", pa_cstrerror(errno));
+            return -1;
+        }
+
+        pa_memblockq_drop(c->output_memblockq, r);
+    }
+
+    return 0;
+}
+
+static void do_work(connection *c) {
+    connection_assert_ref(c);
+
+    c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
+
+    if (c->dead)
+        return;
+
+    if (pa_iochannel_is_readable(c->io))
+        if (do_read(c) < 0)
+            goto fail;
+
+    if (c->state == ESD_STREAMING_DATA && !c->sink_input && pa_iochannel_is_hungup(c->io))
+        /* In case we are in capture mode we will never call read()
+         * on the socket, hence we need to detect the hangup manually
+         * here, instead of simply waiting for read() to return 0. */
+        goto fail;
+
+    if (pa_iochannel_is_writable(c->io))
+        if (do_write(c) < 0)
+            goto fail;
+
+    return;
+
+fail:
+
+    if (c->state == ESD_STREAMING_DATA && c->sink_input) {
+        c->dead = TRUE;
+
+        pa_iochannel_free(c->io);
+        c->io = NULL;
+
+        pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);
+    } else
+        connection_unlink(c);
+}
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    connection_assert_ref(c);
+    pa_assert(io);
+
+    do_work(c);
+}
+
+static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    connection_assert_ref(c);
+    pa_assert(e);
+
+    do_work(c);
+}
+
+static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    connection *c = CONNECTION(o);
+    connection_assert_ref(c);
+
+    switch (code) {
+        case CONNECTION_MESSAGE_REQUEST_DATA:
+            do_work(c);
+            break;
+
+        case CONNECTION_MESSAGE_POST_DATA:
+/*             pa_log("got data %u", chunk->length); */
+            pa_memblockq_push_align(c->output_memblockq, chunk);
+            do_work(c);
+            break;
+
+        case CONNECTION_MESSAGE_UNLINK_CONNECTION:
+            connection_unlink(c);
+            break;
+    }
+
+    return 0;
+}
+
+/*** sink_input callbacks ***/
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_sink_input *i = PA_SINK_INPUT(o);
+    connection*c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    switch (code) {
+
+        case SINK_INPUT_MESSAGE_POST_DATA: {
+            pa_assert(chunk);
+
+            /* New data from the main loop */
+            pa_memblockq_push_align(c->input_memblockq, chunk);
+
+            if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+                pa_log_debug("Requesting rewind due to end of underrun.");
+                pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+            }
+
+/*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
+
+            return 0;
+        }
+
+        case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
+            pa_memblockq_prebuf_disable(c->input_memblockq);
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+        }
+
+        default:
+            return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+    }
+}
+
+/* Called from thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+    connection*c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+    pa_assert(chunk);
+
+    if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
+
+        c->playback.underrun = TRUE;
+
+        if (c->dead && pa_sink_input_safe_to_remove(i))
+            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+
+        return -1;
+    } else {
+        size_t m;
+
+        chunk->length = PA_MIN(length, chunk->length);
+
+        c->playback.underrun = FALSE;
+
+        pa_memblockq_drop(c->input_memblockq, chunk->length);
+        m = pa_memblockq_pop_missing(c->input_memblockq);
+
+        if (m > 0)
+            if (pa_atomic_add(&c->playback.missing, m) <= 0)
+                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+
+        return 0;
+    }
+}
+
+/* Called from thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    /* If we are in an underrun, then we don't rewind */
+    if (i->thread_info.underrun_for > 0)
+        return;
+
+    pa_memblockq_rewind(c->input_memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
+}
+
+static void sink_input_kill_cb(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+
+    connection_unlink(CONNECTION(i->userdata));
+}
+
+/*** source_output callbacks ***/
+
+/* Called from thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    connection *c;
+
+    pa_source_output_assert_ref(o);
+    c = CONNECTION(o->userdata);
+    pa_assert(c);
+    pa_assert(chunk);
+
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+}
+
+static void source_output_kill_cb(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+
+    connection_unlink(CONNECTION(o->userdata));
+}
+
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+    connection*c;
+
+    pa_source_output_assert_ref(o);
+    c = CONNECTION(o->userdata);
+    pa_assert(c);
+
+    return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
+}
+
+/*** socket server callback ***/
+
+static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    pa_assert(m);
+    pa_assert(tv);
+    connection_assert_ref(c);
+    pa_assert(c->auth_timeout_event == e);
+
+    if (!c->authorized)
+        connection_unlink(c);
+}
+
+static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+    connection *c;
+    pa_protocol_esound *p = userdata;
+    char cname[256], pname[128];
+
+    pa_assert(s);
+    pa_assert(io);
+    pa_assert(p);
+
+    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+
+    c = pa_msgobject_new(connection);
+    c->parent.parent.free = connection_free;
+    c->parent.process_msg = connection_process_msg;
+    c->protocol = p;
+    c->io = io;
+    pa_iochannel_set_callback(c->io, io_callback, c);
+
+    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+    pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
+    c->client = pa_client_new(p->core, __FILE__, cname);
+    pa_proplist_sets(c->client->proplist, "esound-protocol.peer", pname);
+    c->client->module = p->module;
+    c->client->kill = client_kill_cb;
+    c->client->userdata = c;
+
+    c->authorized = !!p->public;
+    c->swap_byte_order = FALSE;
+    c->dead = FALSE;
+
+    c->read_data_length = 0;
+    c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
+
+    c->write_data_length = c->write_data_index = c->write_data_alloc = 0;
+    c->write_data = NULL;
+
+    c->state = ESD_NEEDS_REQDATA;
+    c->request = ESD_PROTO_CONNECT;
+
+    c->sink_input = NULL;
+    c->input_memblockq = NULL;
+
+    c->source_output = NULL;
+    c->output_memblockq = NULL;
+
+    c->playback.current_memblock = NULL;
+    c->playback.memblock_index = 0;
+    c->playback.underrun = TRUE;
+    pa_atomic_store(&c->playback.missing, 0);
+
+    pa_memchunk_reset(&c->scache.memchunk);
+    c->scache.name = NULL;
+
+    c->original_name = NULL;
+
+    if (!c->authorized && p->auth_ip_acl && pa_ip_acl_check(p->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
+        pa_log_info("Client authenticated by IP ACL.");
+        c->authorized = TRUE;
+    }
+
+    if (!c->authorized) {
+        struct timeval tv;
+        pa_gettimeofday(&tv);
+        tv.tv_sec += AUTH_TIMEOUT;
+        c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
+    } else
+        c->auth_timeout_event = NULL;
+
+    c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
+    p->core->mainloop->defer_enable(c->defer_event, 0);
+
+    pa_idxset_put(p->connections, c, &c->index);
+}
+
+/*** entry points ***/
+
+pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
+    pa_protocol_esound *p = NULL;
+    pa_bool_t public = FALSE;
+    const char *acl;
+
+    pa_assert(core);
+    pa_assert(server);
+    pa_assert(m);
+    pa_assert(ma);
+
+    if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {
+        pa_log("auth-anonymous= expects a boolean argument.");
+        goto fail;
+    }
+
+    p = pa_xnew(pa_protocol_esound, 1);
+
+    if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0)
+        goto fail;
+
+    if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
+
+        if (!(p->auth_ip_acl = pa_ip_acl_new(acl))) {
+            pa_log("Failed to parse IP ACL '%s'", acl);
+            goto fail;
+        }
+    } else
+        p->auth_ip_acl = NULL;
+
+    p->core = core;
+    p->module = m;
+    p->public = public;
+    p->server = pa_socket_server_ref(server);
+    pa_socket_server_set_callback(p->server, on_connection, p);
+    p->connections = pa_idxset_new(NULL, NULL);
+
+    p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+    p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
+    p->n_player = 0;
+
+    return p;
+
+fail:
+    pa_xfree(p);
+    return NULL;
+}
+
+void pa_protocol_esound_free(pa_protocol_esound *p) {
+    connection *c;
+    pa_assert(p);
+
+    while ((c = pa_idxset_first(p->connections, NULL)))
+        connection_unlink(c);
+    pa_idxset_free(p->connections, NULL, NULL);
+
+    if (p->server)
+        pa_socket_server_unref(p->server);
+
+    if (p->auth_ip_acl)
+        pa_ip_acl_free(p->auth_ip_acl);
+
+    pa_xfree(p->sink_name);
+    pa_xfree(p->source_name);
+
+    pa_xfree(p);
+}
diff --git a/src/pulsecore/protocol-esound.h b/src/pulsecore/protocol-esound.h
new file mode 100644 (file)
index 0000000..0c9447d
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef fooprotocolesoundhfoo
+#define fooprotocolesoundhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/socket-server.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+
+typedef struct pa_protocol_esound pa_protocol_esound;
+
+pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
+void pa_protocol_esound_free(pa_protocol_esound *p);
+
+#endif
similarity index 74%
rename from polyp/protocol-http.c
rename to src/pulsecore/protocol-http.c
index 64e6dadfb98d0ae00defaa349cd3aa777f255228..039904355a0528fbf0516aefec58ee80618a7927 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <config.h>
 #endif
 
-#include <assert.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/ioline.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/cli-text.h>
+
 #include "protocol-http.h"
-#include "ioline.h"
-#include "xmalloc.h"
-#include "log.h"
-#include "namereg.h"
-#include "cli-text.h"
 
 /* Don't allow more than this many concurrent connections */
 #define MAX_CONNECTIONS 10
 #define URL_STATUS "/status"
 
 struct connection {
-    struct pa_protocol_http *protocol;
-    struct pa_ioline *line;
+    pa_protocol_http *protocol;
+    pa_ioline *line;
     enum { REQUEST_LINE, MIME_HEADER, DATA } state;
     char *url;
 };
 
 struct pa_protocol_http {
-    struct pa_module *module;
-    struct pa_core *core;
-    struct pa_socket_server*server;
-    struct pa_idxset *connections;
+    pa_module *module;
+    pa_core *core;
+    pa_socket_server*server;
+    pa_idxset *connections;
 };
 
 static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
     char s[256];
-    assert(c);
-    assert(msg);
-    assert(mime);
 
-    snprintf(s, sizeof(s), 
+    pa_assert(c);
+    pa_assert(msg);
+    pa_assert(mime);
+
+    pa_snprintf(s, sizeof(s),
              "HTTP/1.0 %i %s\n"
              "Connection: close\n"
              "Content-Type: %s\n"
@@ -78,14 +82,14 @@ static void http_response(struct connection *c, int code, const char *msg, const
 
 static void http_message(struct connection *c, int code, const char *msg, const char *text) {
     char s[256];
-    assert(c);
+    pa_assert(c);
 
     http_response(c, code, msg, "text/html");
 
     if (!text)
         text = msg;
 
-    snprintf(s, sizeof(s),
+    pa_snprintf(s, sizeof(s),
              "<?xml version=\"1.0\"?>\n"
              "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
              "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>%s</title></head>\n"
@@ -98,21 +102,22 @@ static void http_message(struct connection *c, int code, const char *msg, const
 
 
 static void connection_free(struct connection *c, int del) {
-    assert(c);
+    pa_assert(c);
 
     if (c->url)
         pa_xfree(c->url);
 
     if (del)
         pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
+
     pa_ioline_unref(c->line);
     pa_xfree(c);
 }
 
-static void line_callback(struct pa_ioline *line, const char *s, void *userdata) {
+static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     struct connection *c = userdata;
-    assert(line);
-    assert(c);
+    pa_assert(line);
+    pa_assert(c);
 
     if (!s) {
         /* EOF */
@@ -134,16 +139,16 @@ static void line_callback(struct pa_ioline *line, const char *s, void *userdata)
         }
 
         case MIME_HEADER: {
-            
+
             /* Ignore MIME headers */
             if (strcspn(s, " \r\n") != 0)
                 break;
-            
+
             /* We're done */
             c->state = DATA;
 
-            pa_log_info(__FILE__": request for %s\n", c->url);
-            
+            pa_log_info("request for %s", c->url);
+
             if (!strcmp(c->url, URL_ROOT)) {
                 char txt[256];
                 http_response(c, 200, "OK", "text/html");
@@ -161,22 +166,22 @@ static void line_callback(struct pa_ioline *line, const char *s, void *userdata)
 #define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "<tr><td><b>%s</b></td><td>%s</td></tr>\n",(a),(b))
 
                 PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
-                PRINTF_FIELD("Fully Qualified Domain Name:", pa_get_fqdn(txt, sizeof(txt)));
+                PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt)));
                 PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
                 PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core));
                 PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core));
-                
+
                 pa_ioline_puts(c->line, "</table>");
 
                 pa_ioline_puts(c->line, "<p><a href=\"/status\">Click here</a> for an extensive server status report.</p>");
-                
+
                 pa_ioline_puts(c->line, "</body></html>\n");
-                
-                pa_ioline_defer_close(c->line); 
+
+                pa_ioline_defer_close(c->line);
             } else if (!strcmp(c->url, URL_CSS)) {
                 http_response(c, 200, "OK", "text/css");
 
-                pa_ioline_puts(c->line, 
+                pa_ioline_puts(c->line,
                                "body { color: black; background-color: white; margin: 0.5cm; }\n"
                                "a:link, a:visited { color: #900000; }\n"
                                "p { margin-left: 0.5cm; margin-right: 0.5cm; }\n"
@@ -191,12 +196,12 @@ static void line_callback(struct pa_ioline *line, const char *s, void *userdata)
 
                 pa_ioline_defer_close(c->line);
             } else if (!strcmp(c->url, URL_STATUS)) {
-                char *s;
+                char *r;
 
                 http_response(c, 200, "OK", "text/plain");
-                s = pa_full_status_string(c->protocol->core);
-                pa_ioline_puts(c->line, s);
-                pa_xfree(s);
+                r = pa_full_status_string(c->protocol->core);
+                pa_ioline_puts(c->line, r);
+                pa_xfree(r);
 
                 pa_ioline_defer_close(c->line);
             } else
@@ -204,29 +209,32 @@ static void line_callback(struct pa_ioline *line, const char *s, void *userdata)
 
             break;
         }
-            
+
         default:
             ;
     }
 
     return;
-            
+
 fail:
     internal_server_error(c);
 }
 
-static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
-    struct pa_protocol_http *p = userdata;
+static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+    pa_protocol_http *p = userdata;
     struct connection *c;
-    assert(s && io && p);
 
-    if (pa_idxset_ncontents(p->connections)+1 > MAX_CONNECTIONS) {
-        pa_log_warn(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
+    pa_assert(s);
+    pa_assert(io);
+    pa_assert(p);
+
+    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
         pa_iochannel_free(io);
         return;
     }
 
-    c = pa_xmalloc(sizeof(struct connection));
+    c = pa_xnew(struct connection, 1);
     c->protocol = p;
     c->line = pa_ioline_new(io);
     c->state = REQUEST_LINE;
@@ -236,14 +244,16 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo
     pa_idxset_put(p->connections, c, NULL);
 }
 
-struct pa_protocol_http* pa_protocol_http_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
-    struct pa_protocol_http* p;
-    assert(core && server);
+pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
+    pa_protocol_http* p;
+
+    pa_core_assert_ref(core);
+    pa_assert(server);
 
-    p = pa_xmalloc(sizeof(struct pa_protocol_http));
+    p = pa_xnew(pa_protocol_http, 1);
     p->module = m;
     p->core = core;
-    p->server = server;
+    p->server = pa_socket_server_ref(server);
     p->connections = pa_idxset_new(NULL, NULL);
 
     pa_socket_server_set_callback(p->server, on_connection, p);
@@ -251,13 +261,13 @@ struct pa_protocol_http* pa_protocol_http_new(struct pa_core *core, struct pa_so
     return p;
 }
 
-static void free_connection(void *p, void *userdata) {
-    assert(p);
+static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
+    pa_assert(p);
     connection_free(p, 0);
 }
 
-void pa_protocol_http_free(struct pa_protocol_http *p) {
-    assert(p);
+void pa_protocol_http_free(pa_protocol_http *p) {
+    pa_assert(p);
 
     pa_idxset_free(p->connections, free_connection, NULL);
     pa_socket_server_unref(p->server);
diff --git a/src/pulsecore/protocol-http.h b/src/pulsecore/protocol-http.h
new file mode 100644 (file)
index 0000000..e337233
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef fooprotocolhttphfoo
+#define fooprotocolhttphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2005-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/socket-server.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+
+typedef struct pa_protocol_http pa_protocol_http;
+
+pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
+void pa_protocol_http_free(pa_protocol_http *n);
+
+#endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
new file mode 100644 (file)
index 0000000..923a566
--- /dev/null
@@ -0,0 +1,4163 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+#include <unistd.h>
+
+#include <pulse/timeval.h>
+#include <pulse/version.h>
+#include <pulse/utf8.h>
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/native-common.h>
+#include <pulsecore/packet.h>
+#include <pulsecore/client.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/tagstruct.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream-util.h>
+#include <pulsecore/authkey.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/autoload.h>
+#include <pulsecore/authkey-prop.h>
+#include <pulsecore/strlist.h>
+#include <pulsecore/props.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/ipacl.h>
+#include <pulsecore/thread-mq.h>
+
+#include "protocol-native.h"
+
+/* Kick a client if it doesn't authenticate within this time */
+#define AUTH_TIMEOUT 60
+
+/* Don't accept more connection than this */
+#define MAX_CONNECTIONS 64
+
+#define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */
+#define DEFAULT_TLENGTH_MSEC 2000 /* 2s */
+#define DEFAULT_PROCESS_MSEC 20   /* 20ms */
+#define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC
+
+typedef struct connection connection;
+struct pa_protocol_native;
+
+typedef struct record_stream {
+    pa_msgobject parent;
+
+    connection *connection;
+    uint32_t index;
+
+    pa_source_output *source_output;
+    pa_memblockq *memblockq;
+    size_t fragment_size;
+    pa_usec_t source_latency;
+} record_stream;
+
+typedef struct output_stream {
+    pa_msgobject parent;
+} output_stream;
+
+typedef struct playback_stream {
+    output_stream parent;
+
+    connection *connection;
+    uint32_t index;
+
+    pa_sink_input *sink_input;
+    pa_memblockq *memblockq;
+    pa_bool_t drain_request;
+    uint32_t drain_tag;
+    uint32_t syncid;
+
+    pa_atomic_t missing;
+    size_t minreq;
+    pa_usec_t sink_latency;
+
+    /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
+    int64_t read_index, write_index;
+    size_t render_memblockq_length;
+} playback_stream;
+
+typedef struct upload_stream {
+    output_stream parent;
+
+    connection *connection;
+    uint32_t index;
+
+    pa_memchunk memchunk;
+    size_t length;
+    char *name;
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+    pa_proplist *proplist;
+} upload_stream;
+
+struct connection {
+    pa_msgobject parent;
+
+    pa_bool_t authorized:1;
+    pa_bool_t is_local:1;
+    uint32_t version;
+    pa_protocol_native *protocol;
+    pa_client *client;
+    pa_pstream *pstream;
+    pa_pdispatch *pdispatch;
+    pa_idxset *record_streams, *output_streams;
+    uint32_t rrobin_index;
+    pa_subscription *subscription;
+    pa_time_event *auth_timeout_event;
+};
+
+PA_DECLARE_CLASS(record_stream);
+#define RECORD_STREAM(o) (record_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(record_stream, pa_msgobject);
+
+PA_DECLARE_CLASS(output_stream);
+#define OUTPUT_STREAM(o) (output_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(output_stream, pa_msgobject);
+
+PA_DECLARE_CLASS(playback_stream);
+#define PLAYBACK_STREAM(o) (playback_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(playback_stream, output_stream);
+
+PA_DECLARE_CLASS(upload_stream);
+#define UPLOAD_STREAM(o) (upload_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(upload_stream, output_stream);
+
+PA_DECLARE_CLASS(connection);
+#define CONNECTION(o) (connection_cast(o))
+static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
+
+struct pa_protocol_native {
+    pa_module *module;
+    pa_core *core;
+    pa_bool_t public;
+    pa_socket_server *server;
+    pa_idxset *connections;
+    uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH];
+    pa_bool_t auth_cookie_in_property;
+#ifdef HAVE_CREDS
+    char *auth_group;
+#endif
+    pa_ip_acl *auth_ip_acl;
+};
+
+enum {
+    SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+    SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */
+    SINK_INPUT_MESSAGE_FLUSH,
+    SINK_INPUT_MESSAGE_TRIGGER,
+    SINK_INPUT_MESSAGE_SEEK,
+    SINK_INPUT_MESSAGE_PREBUF_FORCE,
+    SINK_INPUT_MESSAGE_UPDATE_LATENCY
+};
+
+enum {
+    PLAYBACK_STREAM_MESSAGE_REQUEST_DATA,      /* data requested from sink input from the main loop */
+    PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
+    PLAYBACK_STREAM_MESSAGE_OVERFLOW,
+    PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
+    PLAYBACK_STREAM_MESSAGE_STARTED
+};
+
+enum {
+    RECORD_STREAM_MESSAGE_POST_DATA         /* data from source output to main loop */
+};
+
+enum {
+    CONNECTION_MESSAGE_RELEASE,
+    CONNECTION_MESSAGE_REVOKE
+};
+
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
+static void sink_input_kill_cb(pa_sink_input *i);
+static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
+static void sink_input_moved_cb(pa_sink_input *i);
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
+
+
+static void send_memblock(connection *c);
+static void request_bytes(struct playback_stream*s);
+
+static void source_output_kill_cb(pa_source_output *o);
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
+static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
+static void source_output_moved_cb(pa_source_output *o);
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_remove_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_volume(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_mute(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_trigger_or_flush_or_prebuf_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
+static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
+    [PA_COMMAND_ERROR] = NULL,
+    [PA_COMMAND_TIMEOUT] = NULL,
+    [PA_COMMAND_REPLY] = NULL,
+    [PA_COMMAND_CREATE_PLAYBACK_STREAM] = command_create_playback_stream,
+    [PA_COMMAND_DELETE_PLAYBACK_STREAM] = command_delete_stream,
+    [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = command_drain_playback_stream,
+    [PA_COMMAND_CREATE_RECORD_STREAM] = command_create_record_stream,
+    [PA_COMMAND_DELETE_RECORD_STREAM] = command_delete_stream,
+    [PA_COMMAND_AUTH] = command_auth,
+    [PA_COMMAND_REQUEST] = NULL,
+    [PA_COMMAND_EXIT] = command_exit,
+    [PA_COMMAND_SET_CLIENT_NAME] = command_set_client_name,
+    [PA_COMMAND_LOOKUP_SINK] = command_lookup,
+    [PA_COMMAND_LOOKUP_SOURCE] = command_lookup,
+    [PA_COMMAND_STAT] = command_stat,
+    [PA_COMMAND_GET_PLAYBACK_LATENCY] = command_get_playback_latency,
+    [PA_COMMAND_GET_RECORD_LATENCY] = command_get_record_latency,
+    [PA_COMMAND_CREATE_UPLOAD_STREAM] = command_create_upload_stream,
+    [PA_COMMAND_DELETE_UPLOAD_STREAM] = command_delete_stream,
+    [PA_COMMAND_FINISH_UPLOAD_STREAM] = command_finish_upload_stream,
+    [PA_COMMAND_PLAY_SAMPLE] = command_play_sample,
+    [PA_COMMAND_REMOVE_SAMPLE] = command_remove_sample,
+    [PA_COMMAND_GET_SINK_INFO] = command_get_info,
+    [PA_COMMAND_GET_SOURCE_INFO] = command_get_info,
+    [PA_COMMAND_GET_CLIENT_INFO] = command_get_info,
+    [PA_COMMAND_GET_MODULE_INFO] = command_get_info,
+    [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info,
+    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info,
+    [PA_COMMAND_GET_SAMPLE_INFO] = command_get_info,
+    [PA_COMMAND_GET_SINK_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_SERVER_INFO] = command_get_server_info,
+    [PA_COMMAND_SUBSCRIBE] = command_subscribe,
+
+    [PA_COMMAND_SET_SINK_VOLUME] = command_set_volume,
+    [PA_COMMAND_SET_SINK_INPUT_VOLUME] = command_set_volume,
+    [PA_COMMAND_SET_SOURCE_VOLUME] = command_set_volume,
+
+    [PA_COMMAND_SET_SINK_MUTE] = command_set_mute,
+    [PA_COMMAND_SET_SINK_INPUT_MUTE] = command_set_mute,
+    [PA_COMMAND_SET_SOURCE_MUTE] = command_set_mute,
+
+    [PA_COMMAND_SUSPEND_SINK] = command_suspend,
+    [PA_COMMAND_SUSPEND_SOURCE] = command_suspend,
+
+    [PA_COMMAND_CORK_PLAYBACK_STREAM] = command_cork_playback_stream,
+    [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+    [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+    [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
+
+    [PA_COMMAND_CORK_RECORD_STREAM] = command_cork_record_stream,
+    [PA_COMMAND_FLUSH_RECORD_STREAM] = command_flush_record_stream,
+
+    [PA_COMMAND_SET_DEFAULT_SINK] = command_set_default_sink_or_source,
+    [PA_COMMAND_SET_DEFAULT_SOURCE] = command_set_default_sink_or_source,
+    [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = command_set_stream_name,
+    [PA_COMMAND_SET_RECORD_STREAM_NAME] = command_set_stream_name,
+    [PA_COMMAND_KILL_CLIENT] = command_kill,
+    [PA_COMMAND_KILL_SINK_INPUT] = command_kill,
+    [PA_COMMAND_KILL_SOURCE_OUTPUT] = command_kill,
+    [PA_COMMAND_LOAD_MODULE] = command_load_module,
+    [PA_COMMAND_UNLOAD_MODULE] = command_unload_module,
+    [PA_COMMAND_GET_AUTOLOAD_INFO] = command_get_autoload_info,
+    [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = command_get_autoload_info_list,
+    [PA_COMMAND_ADD_AUTOLOAD] = command_add_autoload,
+    [PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload,
+
+    [PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
+    [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
+
+    [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+    [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+
+    [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+    [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+
+    [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = command_update_proplist,
+    [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = command_update_proplist,
+    [PA_COMMAND_UPDATE_CLIENT_PROPLIST] = command_update_proplist,
+
+    [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = command_remove_proplist,
+    [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
+    [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
+};
+
+/* structure management */
+
+static void upload_stream_unlink(upload_stream *s) {
+    pa_assert(s);
+
+    if (!s->connection)
+        return;
+
+    pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
+    s->connection = NULL;
+    upload_stream_unref(s);
+}
+
+static void upload_stream_free(pa_object *o) {
+    upload_stream *s = UPLOAD_STREAM(o);
+    pa_assert(s);
+
+    upload_stream_unlink(s);
+
+    pa_xfree(s->name);
+
+    if (s->proplist)
+        pa_proplist_free(s->proplist);
+
+    if (s->memchunk.memblock)
+        pa_memblock_unref(s->memchunk.memblock);
+
+    pa_xfree(s);
+}
+
+static upload_stream* upload_stream_new(
+        connection *c,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        const char *name,
+        size_t length,
+        pa_proplist *p) {
+
+    upload_stream *s;
+
+    pa_assert(c);
+    pa_assert(ss);
+    pa_assert(name);
+    pa_assert(length > 0);
+    pa_assert(p);
+
+    s = pa_msgobject_new(upload_stream);
+    s->parent.parent.parent.free = upload_stream_free;
+    s->connection = c;
+    s->sample_spec = *ss;
+    s->channel_map = *map;
+    s->name = pa_xstrdup(name);
+    pa_memchunk_reset(&s->memchunk);
+    s->length = length;
+    s->proplist = pa_proplist_copy(p);
+    pa_proplist_update(s->proplist, PA_UPDATE_MERGE, c->client->proplist);
+
+    pa_idxset_put(c->output_streams, s, &s->index);
+
+    return s;
+}
+
+static void record_stream_unlink(record_stream *s) {
+    pa_assert(s);
+
+    if (!s->connection)
+        return;
+
+    if (s->source_output) {
+        pa_source_output_unlink(s->source_output);
+        pa_source_output_unref(s->source_output);
+        s->source_output = NULL;
+    }
+
+    pa_assert_se(pa_idxset_remove_by_data(s->connection->record_streams, s, NULL) == s);
+    s->connection = NULL;
+    record_stream_unref(s);
+}
+
+static void record_stream_free(pa_object *o) {
+    record_stream *s = RECORD_STREAM(o);
+    pa_assert(s);
+
+    record_stream_unlink(s);
+
+    pa_memblockq_free(s->memblockq);
+    pa_xfree(s);
+}
+
+static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    record_stream *s = RECORD_STREAM(o);
+    record_stream_assert_ref(s);
+
+    if (!s->connection)
+        return -1;
+
+    switch (code) {
+
+        case RECORD_STREAM_MESSAGE_POST_DATA:
+
+            if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
+/*                 pa_log_warn("Failed to push data into output queue."); */
+                return -1;
+            }
+
+            if (!pa_pstream_is_pending(s->connection->pstream))
+                send_memblock(s->connection);
+
+            break;
+    }
+
+    return 0;
+}
+
+static void fix_record_buffer_attr_pre(record_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *fragsize) {
+    pa_assert(s);
+    pa_assert(maxlength);
+    pa_assert(fragsize);
+
+    if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+        *maxlength = MAX_MEMBLOCKQ_LENGTH;
+
+    if (*fragsize <= 0)
+        *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
+
+    if (adjust_latency) {
+        pa_usec_t fragsize_usec;
+
+        /* So, the user asked us to adjust the latency according to
+         * what the source can provide. Half the latency will be
+         * spent on the hw buffer, half of it in the async buffer
+         * queue we maintain for each client. */
+
+        fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec);
+
+        s->source_latency = pa_source_output_set_requested_latency(s->source_output, fragsize_usec/2);
+
+        if (fragsize_usec >= s->source_latency*2)
+            fragsize_usec -= s->source_latency;
+        else
+            fragsize_usec = s->source_latency;
+
+        *fragsize = pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
+    } else
+        s->source_latency = 0;
+}
+
+static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, uint32_t *fragsize) {
+    size_t base;
+
+    pa_assert(s);
+    pa_assert(maxlength);
+    pa_assert(fragsize);
+
+    *maxlength = pa_memblockq_get_maxlength(s->memblockq);
+
+    base = pa_frame_size(&s->source_output->sample_spec);
+
+    s->fragment_size = (*fragsize/base)*base;
+    if (s->fragment_size <= 0)
+        s->fragment_size = base;
+
+    if (s->fragment_size > *maxlength)
+        s->fragment_size = *maxlength;
+
+    *fragsize = s->fragment_size;
+}
+
+static record_stream* record_stream_new(
+        connection *c,
+        pa_source *source,
+        pa_sample_spec *ss,
+        pa_channel_map *map,
+        pa_bool_t peak_detect,
+        uint32_t *maxlength,
+        uint32_t *fragsize,
+        pa_source_output_flags_t flags,
+        pa_proplist *p,
+        pa_bool_t adjust_latency,
+        pa_sink_input *direct_on_input) {
+
+    record_stream *s;
+    pa_source_output *source_output;
+    size_t base;
+    pa_source_output_new_data data;
+
+    pa_assert(c);
+    pa_assert(ss);
+    pa_assert(maxlength);
+    pa_assert(p);
+
+    pa_source_output_new_data_init(&data);
+
+    pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+    pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+    data.driver = __FILE__;
+    data.module = c->protocol->module;
+    data.client = c->client;
+    data.source = source;
+    data.direct_on_input = direct_on_input;
+    pa_source_output_new_data_set_sample_spec(&data, ss);
+    pa_source_output_new_data_set_channel_map(&data, map);
+    if (peak_detect)
+        data.resample_method = PA_RESAMPLER_PEAKS;
+
+    source_output = pa_source_output_new(c->protocol->core, &data, flags);
+
+    pa_source_output_new_data_done(&data);
+
+    if (!source_output)
+        return NULL;
+
+    s = pa_msgobject_new(record_stream);
+    s->parent.parent.free = record_stream_free;
+    s->parent.process_msg = record_stream_process_msg;
+    s->connection = c;
+    s->source_output = source_output;
+
+    s->source_output->push = source_output_push_cb;
+    s->source_output->kill = source_output_kill_cb;
+    s->source_output->get_latency = source_output_get_latency_cb;
+    s->source_output->moved = source_output_moved_cb;
+    s->source_output->suspend = source_output_suspend_cb;
+    s->source_output->userdata = s;
+
+    fix_record_buffer_attr_pre(s, adjust_latency, maxlength, fragsize);
+
+    s->memblockq = pa_memblockq_new(
+            0,
+            *maxlength,
+            0,
+            base = pa_frame_size(&source_output->sample_spec),
+            1,
+            0,
+            0,
+            NULL);
+
+    fix_record_buffer_attr_post(s, maxlength, fragsize);
+
+    *ss = s->source_output->sample_spec;
+    *map = s->source_output->channel_map;
+
+    pa_idxset_put(c->record_streams, s, &s->index);
+
+    pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
+                ((double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC,
+                (double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) / PA_USEC_PER_MSEC,
+                (double) s->source_latency / PA_USEC_PER_MSEC);
+
+    pa_source_output_put(s->source_output);
+    return s;
+}
+
+static void playback_stream_unlink(playback_stream *s) {
+    pa_assert(s);
+
+    if (!s->connection)
+        return;
+
+    if (s->sink_input) {
+        pa_sink_input_unlink(s->sink_input);
+        pa_sink_input_unref(s->sink_input);
+        s->sink_input = NULL;
+    }
+
+    if (s->drain_request)
+        pa_pstream_send_error(s->connection->pstream, s->drain_tag, PA_ERR_NOENTITY);
+
+    pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
+    s->connection = NULL;
+    playback_stream_unref(s);
+}
+
+static void playback_stream_free(pa_object* o) {
+    playback_stream *s = PLAYBACK_STREAM(o);
+    pa_assert(s);
+
+    playback_stream_unlink(s);
+
+    pa_memblockq_free(s->memblockq);
+    pa_xfree(s);
+}
+
+static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    playback_stream *s = PLAYBACK_STREAM(o);
+    playback_stream_assert_ref(s);
+
+    if (!s->connection)
+        return -1;
+
+    switch (code) {
+        case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
+            pa_tagstruct *t;
+            uint32_t l = 0;
+
+            for (;;) {
+                if ((l = pa_atomic_load(&s->missing)) <= 0)
+                    break;
+
+                if (pa_atomic_cmpxchg(&s->missing, l, 0))
+                    break;
+            }
+
+            if (l <= 0)
+                break;
+
+            t = pa_tagstruct_new(NULL, 0);
+            pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
+            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+            pa_tagstruct_putu32(t, s->index);
+            pa_tagstruct_putu32(t, l);
+            pa_pstream_send_tagstruct(s->connection->pstream, t);
+
+/*             pa_log("Requesting %lu bytes", (unsigned long) l); */
+            break;
+        }
+
+        case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: {
+            pa_tagstruct *t;
+
+            /* Report that we're empty */
+            t = pa_tagstruct_new(NULL, 0);
+            pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW);
+            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+            pa_tagstruct_putu32(t, s->index);
+            pa_pstream_send_tagstruct(s->connection->pstream, t);
+            break;
+        }
+
+        case PLAYBACK_STREAM_MESSAGE_OVERFLOW: {
+            pa_tagstruct *t;
+
+            /* Notify the user we're overflowed*/
+            t = pa_tagstruct_new(NULL, 0);
+            pa_tagstruct_putu32(t, PA_COMMAND_OVERFLOW);
+            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+            pa_tagstruct_putu32(t, s->index);
+            pa_pstream_send_tagstruct(s->connection->pstream, t);
+            break;
+        }
+
+        case PLAYBACK_STREAM_MESSAGE_STARTED:
+
+            if (s->connection->version >= 13) {
+                pa_tagstruct *t;
+
+                /* Notify the user we're overflowed*/
+                t = pa_tagstruct_new(NULL, 0);
+                pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
+                pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+                pa_tagstruct_putu32(t, s->index);
+                pa_pstream_send_tagstruct(s->connection->pstream, t);
+            }
+
+            break;
+
+        case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
+            pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
+            break;
+    }
+
+    return 0;
+}
+
+static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) {
+    size_t frame_size;
+    pa_usec_t tlength_usec, minreq_usec, sink_usec;
+
+    pa_assert(s);
+    pa_assert(maxlength);
+    pa_assert(tlength);
+    pa_assert(prebuf);
+    pa_assert(minreq);
+
+    if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+        *maxlength = MAX_MEMBLOCKQ_LENGTH;
+    if (*tlength <= 0)
+        *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+    if (*minreq <= 0)
+        *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+
+    frame_size = pa_frame_size(&s->sink_input->sample_spec);
+    if (*minreq <= 0)
+        *minreq = frame_size;
+    if (*tlength < *minreq+frame_size)
+        *tlength = *minreq+frame_size;
+
+    tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec);
+    minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec);
+
+    pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
+                (double) tlength_usec / PA_USEC_PER_MSEC,
+                (double) minreq_usec / PA_USEC_PER_MSEC);
+
+    if (adjust_latency) {
+
+        /* So, the user asked us to adjust the latency of the stream
+         * buffer according to the what the sink can provide. The
+         * tlength passed in shall be the overall latency. Roughly
+         * half the latency will be spent on the hw buffer, the other
+         * half of it in the async buffer queue we maintain for each
+         * client. In between we'll have a safety space of size
+         * 2*minreq. Why the 2*minreq? When the hw buffer is completey
+         * empty and needs to be filled, then our buffer must have
+         * enough data to fulfill this request immediatly and thus
+         * have at least the same tlength as the size of the hw
+         * buffer. It additionally needs space for 2 times minreq
+         * because if the buffer ran empty and a partial fillup
+         * happens immediately on the next iteration we need to be
+         * able to fulfill it and give the application also minreq
+         * time to fill it up again for the next request Makes 2 times
+         * minreq in plus.. */
+
+        if (tlength_usec > minreq_usec*2)
+            sink_usec = (tlength_usec - minreq_usec*2)/2;
+        else
+            sink_usec = 0;
+
+    } else {
+
+        /* Ok, the user didn't ask us to adjust the latency, but we
+         * still need to make sure that the parameters from the user
+         * do make sense. */
+
+        if (tlength_usec > minreq_usec*2)
+            sink_usec = (tlength_usec - minreq_usec*2);
+        else
+            sink_usec = 0;
+    }
+
+    s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+
+    if (adjust_latency) {
+        /* Ok, we didn't necessarily get what we were asking for, so
+         * let's subtract from what we asked for for the remaining
+         * buffer space */
+
+        if (tlength_usec >= s->sink_latency)
+            tlength_usec -= s->sink_latency;
+    }
+
+    if (tlength_usec < s->sink_latency + 2*minreq_usec)
+        tlength_usec = s->sink_latency + 2*minreq_usec;
+
+    *tlength = pa_usec_to_bytes(tlength_usec, &s->sink_input->sample_spec);
+    *minreq = pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
+
+    if (*minreq <= 0) {
+        *minreq += frame_size;
+        *tlength += frame_size*2;
+    }
+
+    if (*tlength <= *minreq)
+        *tlength =  *minreq*2 + frame_size;
+
+    if (*prebuf <= 0 || *prebuf > *tlength)
+        *prebuf = *tlength;
+}
+
+static void fix_playback_buffer_attr_post(playback_stream *s, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) {
+    pa_assert(s);
+    pa_assert(maxlength);
+    pa_assert(tlength);
+    pa_assert(prebuf);
+    pa_assert(minreq);
+
+    *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+    *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
+    *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
+    *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+
+    s->minreq = *minreq;
+}
+
+static playback_stream* playback_stream_new(
+        connection *c,
+        pa_sink *sink,
+        pa_sample_spec *ss,
+        pa_channel_map *map,
+        uint32_t *maxlength,
+        uint32_t *tlength,
+        uint32_t *prebuf,
+        uint32_t *minreq,
+        pa_cvolume *volume,
+        pa_bool_t muted,
+        uint32_t syncid,
+        uint32_t *missing,
+        pa_sink_input_flags_t flags,
+        pa_proplist *p,
+        pa_bool_t adjust_latency) {
+
+    playback_stream *s, *ssync;
+    pa_sink_input *sink_input;
+    pa_memchunk silence;
+    uint32_t idx;
+    int64_t start_index;
+    pa_sink_input_new_data data;
+
+    pa_assert(c);
+    pa_assert(ss);
+    pa_assert(maxlength);
+    pa_assert(tlength);
+    pa_assert(prebuf);
+    pa_assert(minreq);
+    pa_assert(volume);
+    pa_assert(missing);
+    pa_assert(p);
+
+    /* Find syncid group */
+    for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
+
+        if (!playback_stream_isinstance(ssync))
+            continue;
+
+        if (ssync->syncid == syncid)
+            break;
+    }
+
+    /* Synced streams must connect to the same sink */
+    if (ssync) {
+
+        if (!sink)
+            sink = ssync->sink_input->sink;
+        else if (sink != ssync->sink_input->sink)
+            return NULL;
+    }
+
+    pa_sink_input_new_data_init(&data);
+
+    pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
+    pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+    data.driver = __FILE__;
+    data.module = c->protocol->module;
+    data.client = c->client;
+    data.sink = sink;
+    pa_sink_input_new_data_set_sample_spec(&data, ss);
+    pa_sink_input_new_data_set_channel_map(&data, map);
+    pa_sink_input_new_data_set_volume(&data, volume);
+    pa_sink_input_new_data_set_muted(&data, muted);
+    data.sync_base = ssync ? ssync->sink_input : NULL;
+
+    sink_input = pa_sink_input_new(c->protocol->core, &data, flags);
+
+    pa_sink_input_new_data_done(&data);
+
+    if (!sink_input)
+        return NULL;
+
+    s = pa_msgobject_new(playback_stream);
+    s->parent.parent.parent.free = playback_stream_free;
+    s->parent.parent.process_msg = playback_stream_process_msg;
+    s->connection = c;
+    s->syncid = syncid;
+    s->sink_input = sink_input;
+
+    s->sink_input->parent.process_msg = sink_input_process_msg;
+    s->sink_input->pop = sink_input_pop_cb;
+    s->sink_input->process_rewind = sink_input_process_rewind_cb;
+    s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    s->sink_input->kill = sink_input_kill_cb;
+    s->sink_input->moved = sink_input_moved_cb;
+    s->sink_input->suspend = sink_input_suspend_cb;
+    s->sink_input->userdata = s;
+
+    start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
+
+    fix_playback_buffer_attr_pre(s, adjust_latency, maxlength, tlength, prebuf, minreq);
+    pa_sink_input_get_silence(sink_input, &silence);
+
+    s->memblockq = pa_memblockq_new(
+            start_index,
+            *maxlength,
+            *tlength,
+            pa_frame_size(&sink_input->sample_spec),
+            *prebuf,
+            *minreq,
+            0,
+            &silence);
+
+    pa_memblock_unref(silence.memblock);
+    fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq);
+
+    *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
+
+    *ss = s->sink_input->sample_spec;
+    *map = s->sink_input->channel_map;
+
+    pa_atomic_store(&s->missing, 0);
+    s->drain_request = FALSE;
+
+    pa_idxset_put(c->output_streams, s, &s->index);
+
+    pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
+                ((double) pa_bytes_to_usec(*tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC,
+                (double) pa_bytes_to_usec(*tlength-*minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+                (double) pa_bytes_to_usec(*minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+                (double) s->sink_latency / PA_USEC_PER_MSEC);
+
+    pa_sink_input_put(s->sink_input);
+    return s;
+}
+
+static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    connection *c = CONNECTION(o);
+    connection_assert_ref(c);
+
+    if (!c->protocol)
+        return -1;
+
+    switch (code) {
+
+        case CONNECTION_MESSAGE_REVOKE:
+            pa_pstream_send_revoke(c->pstream, PA_PTR_TO_UINT(userdata));
+            break;
+
+        case CONNECTION_MESSAGE_RELEASE:
+            pa_pstream_send_release(c->pstream, PA_PTR_TO_UINT(userdata));
+            break;
+    }
+
+    return 0;
+}
+
+static void connection_unlink(connection *c) {
+    record_stream *r;
+    output_stream *o;
+
+    pa_assert(c);
+
+    if (!c->protocol)
+        return;
+
+    while ((r = pa_idxset_first(c->record_streams, NULL)))
+        record_stream_unlink(r);
+
+    while ((o = pa_idxset_first(c->output_streams, NULL)))
+        if (playback_stream_isinstance(o))
+            playback_stream_unlink(PLAYBACK_STREAM(o));
+        else
+            upload_stream_unlink(UPLOAD_STREAM(o));
+
+    if (c->subscription)
+        pa_subscription_free(c->subscription);
+
+    if (c->pstream)
+        pa_pstream_unlink(c->pstream);
+
+    if (c->auth_timeout_event) {
+        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+        c->auth_timeout_event = NULL;
+    }
+
+    pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+    c->protocol = NULL;
+    connection_unref(c);
+}
+
+static void connection_free(pa_object *o) {
+    connection *c = CONNECTION(o);
+
+    pa_assert(c);
+
+    connection_unlink(c);
+
+    pa_idxset_free(c->record_streams, NULL, NULL);
+    pa_idxset_free(c->output_streams, NULL, NULL);
+
+    pa_pdispatch_unref(c->pdispatch);
+    pa_pstream_unref(c->pstream);
+    pa_client_free(c->client);
+
+    pa_xfree(c);
+}
+
+/* Called from thread context */
+static void request_bytes(playback_stream *s) {
+    size_t m, previous_missing;
+
+    playback_stream_assert_ref(s);
+
+    m = pa_memblockq_pop_missing(s->memblockq);
+
+    if (m <= 0)
+        return;
+
+/*     pa_log("request_bytes(%lu)", (unsigned long) m); */
+
+    previous_missing = pa_atomic_add(&s->missing, m);
+
+    if (pa_memblockq_prebuf_active(s->memblockq) ||
+        (previous_missing < s->minreq && previous_missing+m >= s->minreq))
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+}
+
+static void send_memblock(connection *c) {
+    uint32_t start;
+    record_stream *r;
+
+    start = PA_IDXSET_INVALID;
+    for (;;) {
+        pa_memchunk chunk;
+
+        if (!(r = RECORD_STREAM(pa_idxset_rrobin(c->record_streams, &c->rrobin_index))))
+            return;
+
+        if (start == PA_IDXSET_INVALID)
+            start = c->rrobin_index;
+        else if (start == c->rrobin_index)
+            return;
+
+        if (pa_memblockq_peek(r->memblockq,  &chunk) >= 0) {
+            pa_memchunk schunk = chunk;
+
+            if (schunk.length > r->fragment_size)
+                schunk.length = r->fragment_size;
+
+            pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk);
+
+            pa_memblockq_drop(r->memblockq, schunk.length);
+            pa_memblock_unref(schunk.memblock);
+
+            return;
+        }
+    }
+}
+
+static void send_playback_stream_killed(playback_stream *p) {
+    pa_tagstruct *t;
+    playback_stream_assert_ref(p);
+
+    t = pa_tagstruct_new(NULL, 0);
+    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(record_stream *r) {
+    pa_tagstruct *t;
+    record_stream_assert_ref(r);
+
+    t = pa_tagstruct_new(NULL, 0);
+    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);
+}
+
+/*** sink input callbacks ***/
+
+static void handle_seek(playback_stream *s, int64_t indexw) {
+    playback_stream_assert_ref(s);
+
+/*     pa_log("handle_seek: %llu -- %i", (unsigned long long) s->sink_input->thread_info.underrun_for, pa_memblockq_is_readable(s->memblockq)); */
+
+    if (s->sink_input->thread_info.underrun_for > 0) {
+
+/*         pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */
+
+        if (pa_memblockq_is_readable(s->memblockq)) {
+
+            /* We just ended an underrun, let's ask the sink
+             * for a complete rewind rewrite */
+
+            pa_log_debug("Requesting rewind due to end of underrun.");
+            pa_sink_input_request_rewind(s->sink_input,
+                                         s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for,
+                                         FALSE, TRUE);
+        }
+
+    } else {
+        int64_t indexr;
+
+        indexr = pa_memblockq_get_read_index(s->memblockq);
+
+        if (indexw < indexr) {
+            /* OK, the sink already asked for this data, so
+             * let's have it usk us again */
+
+            pa_log_debug("Requesting rewind due to rewrite.");
+            pa_sink_input_request_rewind(s->sink_input, indexr - indexw, TRUE, FALSE);
+        }
+    }
+
+    request_bytes(s);
+}
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_sink_input *i = PA_SINK_INPUT(o);
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    switch (code) {
+
+        case SINK_INPUT_MESSAGE_SEEK: {
+            int64_t windex;
+
+            windex = pa_memblockq_get_write_index(s->memblockq);
+            pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata));
+
+            handle_seek(s, windex);
+            return 0;
+        }
+
+        case SINK_INPUT_MESSAGE_POST_DATA: {
+            int64_t windex;
+
+            pa_assert(chunk);
+
+            windex = pa_memblockq_get_write_index(s->memblockq);
+
+/*             pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */
+
+            if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
+                pa_log_warn("Failed to push data into queue");
+                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
+                pa_memblockq_seek(s->memblockq, chunk->length, PA_SEEK_RELATIVE);
+            }
+
+            handle_seek(s, windex);
+
+/*             pa_log("sink input post2: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+
+            return 0;
+        }
+
+        case SINK_INPUT_MESSAGE_DRAIN:
+        case SINK_INPUT_MESSAGE_FLUSH:
+        case SINK_INPUT_MESSAGE_PREBUF_FORCE:
+        case SINK_INPUT_MESSAGE_TRIGGER: {
+
+            int64_t windex;
+            pa_sink_input *isync;
+            void (*func)(pa_memblockq *bq);
+
+            switch  (code) {
+                case SINK_INPUT_MESSAGE_FLUSH:
+                    func = pa_memblockq_flush;
+                    break;
+
+                case SINK_INPUT_MESSAGE_PREBUF_FORCE:
+                    func = pa_memblockq_prebuf_force;
+                    break;
+
+                case SINK_INPUT_MESSAGE_DRAIN:
+                case SINK_INPUT_MESSAGE_TRIGGER:
+                    func = pa_memblockq_prebuf_disable;
+                    break;
+
+                default:
+                    pa_assert_not_reached();
+            }
+
+            windex = pa_memblockq_get_write_index(s->memblockq);
+            func(s->memblockq);
+            handle_seek(s, windex);
+
+            /* Do the same for all other members in the sync group */
+            for (isync = i->sync_prev; isync; isync = isync->sync_prev) {
+                playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+                windex = pa_memblockq_get_write_index(ssync->memblockq);
+                func(ssync->memblockq);
+                handle_seek(ssync, windex);
+            }
+
+            for (isync = i->sync_next; isync; isync = isync->sync_next) {
+                playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+                windex = pa_memblockq_get_write_index(ssync->memblockq);
+                func(ssync->memblockq);
+                handle_seek(ssync, windex);
+            }
+
+            if (code == SINK_INPUT_MESSAGE_DRAIN) {
+                if (!pa_memblockq_is_readable(s->memblockq))
+                    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL);
+                else {
+                    s->drain_tag = PA_PTR_TO_UINT(userdata);
+                    s->drain_request = TRUE;
+                }
+            }
+
+            return 0;
+        }
+
+        case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
+
+            s->read_index = pa_memblockq_get_read_index(s->memblockq);
+            s->write_index = pa_memblockq_get_write_index(s->memblockq);
+            s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_SET_STATE: {
+            int64_t windex;
+
+            windex = pa_memblockq_get_write_index(s->memblockq);
+
+            pa_memblockq_prebuf_force(s->memblockq);
+
+            handle_seek(s, windex);
+
+            /* Fall through to the default handler */
+            break;
+        }
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            *r = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+            break;
+        }
+    }
+
+    return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+}
+
+/* Called from thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+    pa_assert(chunk);
+
+    if (pa_memblockq_peek(s->memblockq, chunk) < 0) {
+
+/*         pa_log("UNDERRUN: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+
+        if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
+            s->drain_request = FALSE;
+            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
+        } else if (i->thread_info.playing_for > 0)
+            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);
+
+/*         pa_log("adding %llu bytes", (unsigned long long) nbytes); */
+
+        request_bytes(s);
+
+        return -1;
+    }
+
+/*     pa_log("NOTUNDERRUN %lu", (unsigned long) chunk->length); */
+
+    chunk->length = PA_MIN(nbytes, chunk->length);
+
+    if (i->thread_info.underrun_for > 0)
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL);
+
+    pa_memblockq_drop(s->memblockq, chunk->length);
+    request_bytes(s);
+
+    return 0;
+}
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    /* If we are in an underrun, then we don't rewind */
+    if (i->thread_info.underrun_for > 0)
+        return;
+
+    pa_memblockq_rewind(s->memblockq, nbytes);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    pa_memblockq_set_maxrewind(s->memblockq, nbytes);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    playback_stream *s;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    send_playback_stream_killed(s);
+    playback_stream_unlink(s);
+}
+
+/* Called from main context */
+static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
+    playback_stream *s;
+    pa_tagstruct *t;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    if (s->connection->version < 12)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_SUSPENDED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void sink_input_moved_cb(pa_sink_input *i) {
+    playback_stream *s;
+    pa_tagstruct *t;
+    uint32_t maxlength, tlength, prebuf, minreq;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+    tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
+    prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
+    minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+
+    fix_playback_buffer_attr_pre(s, TRUE, &maxlength, &tlength, &prebuf, &minreq);
+    pa_memblockq_set_maxlength(s->memblockq, maxlength);
+    pa_memblockq_set_tlength(s->memblockq, tlength);
+    pa_memblockq_set_prebuf(s->memblockq, prebuf);
+    pa_memblockq_set_minreq(s->memblockq, minreq);
+    fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+
+    if (s->connection->version < 12)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_putu32(t, i->sink->index);
+    pa_tagstruct_puts(t, i->sink->name);
+    pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);
+
+    if (s->connection->version >= 13) {
+        pa_tagstruct_putu32(t, maxlength);
+        pa_tagstruct_putu32(t, tlength);
+        pa_tagstruct_putu32(t, prebuf);
+        pa_tagstruct_putu32(t, minreq);
+        pa_tagstruct_put_usec(t, s->sink_latency);
+    }
+
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/*** source_output callbacks ***/
+
+/* Called from thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    record_stream *s;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+    pa_assert(chunk);
+
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+}
+
+static void source_output_kill_cb(pa_source_output *o) {
+    record_stream *s;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    send_record_stream_killed(s);
+    record_stream_unlink(s);
+}
+
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+    record_stream *s;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    /*pa_log("get_latency: %u", pa_memblockq_get_length(s->memblockq));*/
+
+    return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
+}
+
+/* Called from main context */
+static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
+    record_stream *s;
+    pa_tagstruct *t;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    if (s->connection->version < 12)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_SUSPENDED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_put_boolean(t, suspend);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void source_output_moved_cb(pa_source_output *o) {
+    record_stream *s;
+    pa_tagstruct *t;
+    uint32_t maxlength, fragsize;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    fragsize = (uint32_t) s->fragment_size;
+    maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq);
+
+    fix_record_buffer_attr_pre(s, TRUE, &maxlength, &fragsize);
+    pa_memblockq_set_maxlength(s->memblockq, maxlength);
+    fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+
+    if (s->connection->version < 12)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_putu32(t, o->source->index);
+    pa_tagstruct_puts(t, o->source->name);
+    pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED);
+
+    if (s->connection->version >= 13) {
+        pa_tagstruct_putu32(t, maxlength);
+        pa_tagstruct_putu32(t, fragsize);
+        pa_tagstruct_put_usec(t, s->source_latency);
+    }
+
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/*** pdispatch callbacks ***/
+
+static void protocol_error(connection *c) {
+    pa_log("protocol error, kicking client");
+    connection_unlink(c);
+}
+
+#define CHECK_VALIDITY(pstream, expression, tag, error) do { \
+if (!(expression)) { \
+    pa_pstream_send_error((pstream), (tag), (error)); \
+    return; \
+} \
+} while(0);
+
+static pa_tagstruct *reply_new(uint32_t tag) {
+    pa_tagstruct *reply;
+
+    reply = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+    pa_tagstruct_putu32(reply, tag);
+    return reply;
+}
+
+static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    playback_stream *s;
+    uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;
+    const char *name = NULL, *sink_name;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_tagstruct *reply;
+    pa_sink *sink = NULL;
+    pa_cvolume volume;
+    pa_bool_t
+        corked = FALSE,
+        no_remap = FALSE,
+        no_remix = FALSE,
+        fix_format = FALSE,
+        fix_rate = FALSE,
+        fix_channels = FALSE,
+        no_move = FALSE,
+        variable_rate = FALSE,
+        muted = FALSE,
+        adjust_latency = FALSE;
+
+    pa_sink_input_flags_t flags = 0;
+    pa_proplist *p;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
+        pa_tagstruct_get(
+                t,
+                PA_TAG_SAMPLE_SPEC, &ss,
+                PA_TAG_CHANNEL_MAP, &map,
+                PA_TAG_U32, &sink_index,
+                PA_TAG_STRING, &sink_name,
+                PA_TAG_U32, &maxlength,
+                PA_TAG_BOOLEAN, &corked,
+                PA_TAG_U32, &tlength,
+                PA_TAG_U32, &prebuf,
+                PA_TAG_U32, &minreq,
+                PA_TAG_U32, &syncid,
+                PA_TAG_CVOLUME, &volume,
+                PA_TAG_INVALID) < 0) {
+
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(sink_name)), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
+
+    p = pa_proplist_new();
+
+    if (name)
+        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+
+    if (c->version >= 12)  {
+        /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+        if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+            pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+            pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+            pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
+    if (c->version >= 13) {
+
+        if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
+            pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+            pa_tagstruct_get_proplist(t, p) < 0) {
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        pa_proplist_free(p);
+        return;
+    }
+
+    if (sink_index != PA_INVALID_INDEX) {
+
+        if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+            pa_proplist_free(p);
+            return;
+        }
+
+    } else if (sink_name) {
+
+        if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1))) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
+    flags =
+        (corked ?  PA_SINK_INPUT_START_CORKED : 0) |
+        (no_remap ?  PA_SINK_INPUT_NO_REMAP : 0) |
+        (no_remix ?  PA_SINK_INPUT_NO_REMIX : 0) |
+        (fix_format ?  PA_SINK_INPUT_FIX_FORMAT : 0) |
+        (fix_rate ?  PA_SINK_INPUT_FIX_RATE : 0) |
+        (fix_channels ?  PA_SINK_INPUT_FIX_CHANNELS : 0) |
+        (no_move ?  PA_SINK_INPUT_DONT_MOVE : 0) |
+        (variable_rate ?  PA_SINK_INPUT_VARIABLE_RATE : 0);
+
+    s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, &volume, muted, syncid, &missing, flags, p, adjust_latency);
+    pa_proplist_free(p);
+
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, s->index);
+    pa_assert(s->sink_input);
+    pa_tagstruct_putu32(reply, s->sink_input->index);
+    pa_tagstruct_putu32(reply, missing);
+
+/*     pa_log("initial request is %u", missing); */
+
+    if (c->version >= 9) {
+        /* Since 0.9.0 we support sending the buffer metrics back to the client */
+
+        pa_tagstruct_putu32(reply, (uint32_t) maxlength);
+        pa_tagstruct_putu32(reply, (uint32_t) tlength);
+        pa_tagstruct_putu32(reply, (uint32_t) prebuf);
+        pa_tagstruct_putu32(reply, (uint32_t) minreq);
+    }
+
+    if (c->version >= 12) {
+        /* Since 0.9.8 we support sending the chosen sample
+         * spec/channel map/device/suspend status back to the
+         * client */
+
+        pa_tagstruct_put_sample_spec(reply, &ss);
+        pa_tagstruct_put_channel_map(reply, &map);
+
+        pa_tagstruct_putu32(reply, s->sink_input->sink->index);
+        pa_tagstruct_puts(reply, s->sink_input->sink->name);
+
+        pa_tagstruct_put_boolean(reply, pa_sink_get_state(s->sink_input->sink) == PA_SINK_SUSPENDED);
+    }
+
+    if (c->version >= 13)
+        pa_tagstruct_put_usec(reply, s->sink_latency);
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t channel;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    switch (command) {
+
+        case PA_COMMAND_DELETE_PLAYBACK_STREAM: {
+            playback_stream *s;
+            if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !playback_stream_isinstance(s)) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+                return;
+            }
+
+            playback_stream_unlink(s);
+            break;
+        }
+
+        case PA_COMMAND_DELETE_RECORD_STREAM: {
+            record_stream *s;
+            if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+                return;
+            }
+
+            record_stream_unlink(s);
+            break;
+        }
+
+        case PA_COMMAND_DELETE_UPLOAD_STREAM: {
+            upload_stream *s;
+
+            if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !upload_stream_isinstance(s)) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+                return;
+            }
+
+            upload_stream_unlink(s);
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    record_stream *s;
+    uint32_t maxlength, fragment_size;
+    uint32_t source_index;
+    const char *name = NULL, *source_name;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_tagstruct *reply;
+    pa_source *source = NULL;
+    pa_bool_t
+        corked = FALSE,
+        no_remap = FALSE,
+        no_remix = FALSE,
+        fix_format = FALSE,
+        fix_rate = FALSE,
+        fix_channels = FALSE,
+        no_move = FALSE,
+        variable_rate = FALSE,
+        adjust_latency = FALSE,
+        peak_detect = FALSE;
+    pa_source_output_flags_t flags = 0;
+    pa_proplist *p;
+    uint32_t direct_on_input_idx = PA_INVALID_INDEX;
+    pa_sink_input *direct_on_input = NULL;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_get_channel_map(t, &map) < 0 ||
+        pa_tagstruct_getu32(t, &source_index) < 0 ||
+        pa_tagstruct_gets(t, &source_name) < 0 ||
+        pa_tagstruct_getu32(t, &maxlength) < 0 ||
+        pa_tagstruct_get_boolean(t, &corked) < 0 ||
+        pa_tagstruct_getu32(t, &fragment_size) < 0) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, source_index != PA_INVALID_INDEX || !source_name || (*source_name && pa_utf8_valid(source_name)), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
+
+    p = pa_proplist_new();
+
+    if (name)
+        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+
+    if (c->version >= 12)  {
+        /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+        if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+            pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+            pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+            pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+            pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
+    if (c->version >= 13) {
+
+        if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 ||
+            pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+            pa_tagstruct_get_proplist(t, p) < 0 ||
+            pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) {
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        pa_proplist_free(p);
+        return;
+    }
+
+    if (source_index != PA_INVALID_INDEX) {
+
+        if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+            pa_proplist_free(p);
+            return;
+        }
+
+    } else if (source_name) {
+
+        if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1))) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
+    if (direct_on_input_idx != PA_INVALID_INDEX) {
+
+        if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
+    flags =
+        (corked ?  PA_SOURCE_OUTPUT_START_CORKED : 0) |
+        (no_remap ?  PA_SOURCE_OUTPUT_NO_REMAP : 0) |
+        (no_remix ?  PA_SOURCE_OUTPUT_NO_REMIX : 0) |
+        (fix_format ?  PA_SOURCE_OUTPUT_FIX_FORMAT : 0) |
+        (fix_rate ?  PA_SOURCE_OUTPUT_FIX_RATE : 0) |
+        (fix_channels ?  PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
+        (no_move ?  PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
+        (variable_rate ?  PA_SOURCE_OUTPUT_VARIABLE_RATE : 0);
+
+    s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input);
+    pa_proplist_free(p);
+
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, s->index);
+    pa_assert(s->source_output);
+    pa_tagstruct_putu32(reply, s->source_output->index);
+
+    if (c->version >= 9) {
+        /* Since 0.9 we support sending the buffer metrics back to the client */
+
+        pa_tagstruct_putu32(reply, (uint32_t) maxlength);
+        pa_tagstruct_putu32(reply, (uint32_t) fragment_size);
+    }
+
+    if (c->version >= 12) {
+        /* Since 0.9.8 we support sending the chosen sample
+         * spec/channel map/device/suspend status back to the
+         * client */
+
+        pa_tagstruct_put_sample_spec(reply, &ss);
+        pa_tagstruct_put_channel_map(reply, &map);
+
+        pa_tagstruct_putu32(reply, s->source_output->source->index);
+        pa_tagstruct_puts(reply, s->source_output->source->name);
+
+        pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_SUSPENDED);
+    }
+
+    if (c->version >= 13)
+        pa_tagstruct_put_usec(reply, s->source_latency);
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    c->protocol->core->mainloop->quit(c->protocol->core->mainloop, 0);
+    pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
+}
+
+static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    const void*cookie;
+    pa_tagstruct *reply;
+    pa_bool_t shm_on_remote, do_shm;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &c->version) < 0 ||
+        pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    /* Minimum supported version */
+    if (c->version < 8) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_VERSION);
+        return;
+    }
+
+    /* Starting with protocol version 13 the MSB of the version tag
+       reflects if shm is available for this connection or
+       not. */
+    if (c->version >= 13) {
+        shm_on_remote = !!(c->version & 0x80000000U);
+        c->version &= 0x7FFFFFFFU;
+    }
+
+    pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
+
+    pa_proplist_setf(c->client->proplist, "native-protocol.version", "%u", c->version);
+
+    if (!c->authorized) {
+        pa_bool_t success = FALSE;
+
+#ifdef HAVE_CREDS
+        const pa_creds *creds;
+
+        if ((creds = pa_pdispatch_creds(pd))) {
+            if (creds->uid == getuid())
+                success = TRUE;
+            else if (c->protocol->auth_group) {
+                int r;
+                gid_t gid;
+
+                if ((gid = pa_get_gid_of_group(c->protocol->auth_group)) == (gid_t) -1)
+                    pa_log_warn("Failed to get GID of group '%s'", c->protocol->auth_group);
+                else if (gid == creds->gid)
+                    success = TRUE;
+
+                if (!success) {
+                    if ((r = pa_uid_in_group(creds->uid, c->protocol->auth_group)) < 0)
+                        pa_log_warn("Failed to check group membership.");
+                    else if (r > 0)
+                        success = TRUE;
+                }
+            }
+
+            pa_log_info("Got credentials: uid=%lu gid=%lu success=%i",
+                        (unsigned long) creds->uid,
+                        (unsigned long) creds->gid,
+                        (int) success);
+        }
+#endif
+
+        if (!success && memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) == 0)
+            success = TRUE;
+
+        if (!success) {
+            pa_log_warn("Denied access to client with invalid authorization data.");
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_ACCESS);
+            return;
+        }
+
+        c->authorized = TRUE;
+        if (c->auth_timeout_event) {
+            c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+            c->auth_timeout_event = NULL;
+        }
+    }
+
+    /* Enable shared memory support if possible */
+    do_shm =
+        pa_mempool_is_shared(c->protocol->core->mempool) &&
+        c->is_local;
+
+    pa_log_debug("SHM possible: %s", pa_yes_no(do_shm));
+
+    if (do_shm)
+        if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
+            do_shm = FALSE;
+
+    if (do_shm) {
+        /* Only enable SHM if both sides are owned by the same
+         * user. This is a security measure because otherwise data
+         * private to the user might leak. */
+
+        const pa_creds *creds;
+        if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
+            do_shm = FALSE;
+    }
+
+    pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
+    pa_pstream_enable_shm(c->pstream, do_shm);
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0));
+
+#ifdef HAVE_CREDS
+{
+    /* SHM support is only enabled after both sides made sure they are the same user. */
+
+    pa_creds ucred;
+
+    ucred.uid = getuid();
+    ucred.gid = getgid();
+
+    pa_pstream_send_tagstruct_with_creds(c->pstream, reply, &ucred);
+}
+#else
+    pa_pstream_send_tagstruct(c->pstream, reply);
+#endif
+}
+
+static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    const char *name = NULL;
+    pa_proplist *p;
+    pa_tagstruct *reply;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    p = pa_proplist_new();
+
+    if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) ||
+        (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+        !pa_tagstruct_eof(t)) {
+
+        protocol_error(c);
+        pa_proplist_free(p);
+        return;
+    }
+
+    if (name)
+        if (pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name) < 0) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+            pa_proplist_free(p);
+            return;
+        }
+
+    pa_proplist_update(c->client->proplist, PA_UPDATE_REPLACE, p);
+    pa_proplist_free(p);
+
+    pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+
+    reply = reply_new(tag);
+
+    if (c->version >= 13)
+        pa_tagstruct_putu32(reply, c->client->index);
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    const char *name;
+    uint32_t idx = PA_IDXSET_INVALID;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &name) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_LOOKUP_SINK) {
+        pa_sink *sink;
+        if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1)))
+            idx = sink->index;
+    } else {
+        pa_source *source;
+        pa_assert(command == PA_COMMAND_LOOKUP_SOURCE);
+        if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1)))
+            idx = source->index;
+    }
+
+    if (idx == PA_IDXSET_INVALID)
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+    else {
+        pa_tagstruct *reply;
+        reply = reply_new(tag);
+        pa_tagstruct_putu32(reply, idx);
+        pa_pstream_send_tagstruct(c->pstream, reply);
+    }
+}
+
+static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    playback_stream *s;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    s = pa_idxset_get_by_index(c->output_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+    pa_asyncmsgq_post(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_DRAIN, PA_UINT_TO_PTR(tag), 0, NULL, NULL);
+}
+
+static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    pa_tagstruct *reply;
+    const pa_mempool_stat *stat;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    stat = pa_mempool_get_stat(c->protocol->core->mempool);
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_allocated));
+    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->allocated_size));
+    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_accumulated));
+    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->accumulated_size));
+    pa_tagstruct_putu32(reply, pa_scache_total_size(c->protocol->core));
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    pa_tagstruct *reply;
+    playback_stream *s;
+    struct timeval tv, now;
+    uint32_t idx;
+    pa_usec_t latency;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_get_timeval(t, &tv) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    s = pa_idxset_get_by_index(c->output_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0, tag, PA_ERR_NOENTITY)
+
+    reply = reply_new(tag);
+
+    latency = pa_sink_get_latency(s->sink_input->sink);
+    latency += pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec);
+
+    pa_tagstruct_put_usec(reply, latency);
+
+    pa_tagstruct_put_usec(reply, 0);
+    pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
+    pa_tagstruct_put_timeval(reply, &tv);
+    pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
+    pa_tagstruct_puts64(reply, s->write_index);
+    pa_tagstruct_puts64(reply, s->read_index);
+
+    if (c->version >= 13) {
+        pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for);
+        pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    pa_tagstruct *reply;
+    record_stream *s;
+    struct timeval tv, now;
+    uint32_t idx;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_get_timeval(t, &tv) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    s = pa_idxset_get_by_index(c->record_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+    reply = reply_new(tag);
+    pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
+    pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
+    pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING);
+    pa_tagstruct_put_timeval(reply, &tv);
+    pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
+    pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
+    pa_tagstruct_puts64(reply, pa_memblockq_get_read_index(s->memblockq));
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    upload_stream *s;
+    uint32_t length;
+    const char *name = NULL;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_tagstruct *reply;
+    pa_proplist *p;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_get_channel_map(t, &map) < 0 ||
+        pa_tagstruct_getu32(t, &length) < 0) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE);
+
+    p = pa_proplist_new();
+
+    if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) {
+        protocol_error(c);
+        pa_proplist_free(p);
+        return;
+    }
+
+    if (c->version < 13)
+        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+    else if (!name)
+        if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
+            name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
+
+    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+
+    s = upload_stream_new(c, &ss, &map, name, length, p);
+    pa_proplist_free(p);
+
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, s->index);
+    pa_tagstruct_putu32(reply, length);
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t channel;
+    upload_stream *s;
+    uint32_t idx;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    s = pa_idxset_get_by_index(c->output_streams, channel);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+    if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
+    else
+        pa_pstream_send_simple_ack(c->pstream, tag);
+
+    upload_stream_unlink(s);
+}
+
+static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t sink_index;
+    pa_volume_t volume;
+    pa_sink *sink;
+    const char *name, *sink_name;
+    uint32_t idx;
+    pa_proplist *p;
+    pa_tagstruct *reply;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
+        pa_tagstruct_gets(t, &sink_name) < 0 ||
+        pa_tagstruct_getu32(t, &volume) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+
+    if (sink_index != PA_INVALID_INDEX)
+        sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
+    else
+        sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1);
+
+    CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+    p = pa_proplist_new();
+
+    if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        pa_proplist_free(p);
+        return;
+    }
+
+    if (pa_scache_play_item(c->protocol->core, name, sink, volume, p, &idx) < 0) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+        pa_proplist_free(p);
+        return;
+    }
+
+    pa_proplist_free(p);
+
+    reply = reply_new(tag);
+
+    if (c->version >= 13)
+        pa_tagstruct_putu32(reply, idx);
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    const char *name;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &name) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+
+    if (pa_scache_remove_item(c->protocol->core, name) < 0) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+        return;
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void fixup_sample_spec(connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
+    pa_assert(c);
+    pa_assert(fixed);
+    pa_assert(original);
+
+    *fixed = *original;
+
+    if (c->version < 12) {
+        /* Before protocol version 12 we didn't support S32 samples,
+         * so we need to lie about this to the client */
+
+        if (fixed->format == PA_SAMPLE_S32LE)
+            fixed->format = PA_SAMPLE_FLOAT32LE;
+        if (fixed->format == PA_SAMPLE_S32BE)
+            fixed->format = PA_SAMPLE_FLOAT32BE;
+    }
+}
+
+static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) {
+    pa_sample_spec fixed_ss;
+
+    pa_assert(t);
+    pa_sink_assert_ref(sink);
+
+    fixup_sample_spec(c, &fixed_ss, &sink->sample_spec);
+
+    pa_tagstruct_put(
+        t,
+        PA_TAG_U32, sink->index,
+        PA_TAG_STRING, sink->name,
+        PA_TAG_STRING, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)),
+        PA_TAG_SAMPLE_SPEC, &fixed_ss,
+        PA_TAG_CHANNEL_MAP, &sink->channel_map,
+        PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX,
+        PA_TAG_CVOLUME, pa_sink_get_volume(sink),
+        PA_TAG_BOOLEAN, pa_sink_get_mute(sink),
+        PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
+        PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL,
+        PA_TAG_USEC, pa_sink_get_latency(sink),
+        PA_TAG_STRING, sink->driver,
+        PA_TAG_U32, sink->flags,
+        PA_TAG_INVALID);
+
+    if (c->version >= 13) {
+        pa_tagstruct_put_proplist(t, sink->proplist);
+        pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
+    }
+}
+
+static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *source) {
+    pa_sample_spec fixed_ss;
+
+    pa_assert(t);
+    pa_source_assert_ref(source);
+
+    fixup_sample_spec(c, &fixed_ss, &source->sample_spec);
+
+    pa_tagstruct_put(
+        t,
+        PA_TAG_U32, source->index,
+        PA_TAG_STRING, source->name,
+        PA_TAG_STRING, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)),
+        PA_TAG_SAMPLE_SPEC, &fixed_ss,
+        PA_TAG_CHANNEL_MAP, &source->channel_map,
+        PA_TAG_U32, source->module ? source->module->index : PA_INVALID_INDEX,
+        PA_TAG_CVOLUME, pa_source_get_volume(source),
+        PA_TAG_BOOLEAN, pa_source_get_mute(source),
+        PA_TAG_U32, source->monitor_of ? source->monitor_of->index : PA_INVALID_INDEX,
+        PA_TAG_STRING, source->monitor_of ? source->monitor_of->name : NULL,
+        PA_TAG_USEC, pa_source_get_latency(source),
+        PA_TAG_STRING, source->driver,
+        PA_TAG_U32, source->flags,
+        PA_TAG_INVALID);
+
+    if (c->version >= 13) {
+        pa_tagstruct_put_proplist(t, source->proplist);
+        pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
+    }
+}
+
+
+static void client_fill_tagstruct(connection *c, pa_tagstruct *t, pa_client *client) {
+    pa_assert(t);
+    pa_assert(client);
+
+    pa_tagstruct_putu32(t, client->index);
+    pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)));
+    pa_tagstruct_putu32(t, client->module ? client->module->index : PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, client->driver);
+
+    if (c->version >= 13)
+        pa_tagstruct_put_proplist(t, client->proplist);
+
+}
+
+static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
+    pa_assert(t);
+    pa_assert(module);
+
+    pa_tagstruct_putu32(t, module->index);
+    pa_tagstruct_puts(t, module->name);
+    pa_tagstruct_puts(t, module->argument);
+    pa_tagstruct_putu32(t, module->n_used);
+    pa_tagstruct_put_boolean(t, module->auto_unload);
+}
+
+static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_input *s) {
+    pa_sample_spec fixed_ss;
+    pa_usec_t sink_latency;
+
+    pa_assert(t);
+    pa_sink_input_assert_ref(s);
+
+    fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
+
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
+    pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
+    pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
+    pa_tagstruct_putu32(t, s->sink->index);
+    pa_tagstruct_put_sample_spec(t, &fixed_ss);
+    pa_tagstruct_put_channel_map(t, &s->channel_map);
+    pa_tagstruct_put_cvolume(t, &s->volume);
+    pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
+    pa_tagstruct_put_usec(t, sink_latency);
+    pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
+    pa_tagstruct_puts(t, s->driver);
+    if (c->version >= 11)
+        pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s));
+    if (c->version >= 13)
+        pa_tagstruct_put_proplist(t, s->proplist);
+}
+
+static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source_output *s) {
+    pa_sample_spec fixed_ss;
+    pa_usec_t source_latency;
+
+    pa_assert(t);
+    pa_source_output_assert_ref(s);
+
+    fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
+
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
+    pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
+    pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
+    pa_tagstruct_putu32(t, s->source->index);
+    pa_tagstruct_put_sample_spec(t, &fixed_ss);
+    pa_tagstruct_put_channel_map(t, &s->channel_map);
+    pa_tagstruct_put_usec(t, pa_source_output_get_latency(s, &source_latency));
+    pa_tagstruct_put_usec(t, source_latency);
+    pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s)));
+    pa_tagstruct_puts(t, s->driver);
+
+    if (c->version >= 13)
+        pa_tagstruct_put_proplist(t, s->proplist);
+}
+
+static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entry *e) {
+    pa_sample_spec fixed_ss;
+
+    pa_assert(t);
+    pa_assert(e);
+
+    if (e->memchunk.memblock)
+        fixup_sample_spec(c, &fixed_ss, &e->sample_spec);
+    else
+        memset(&fixed_ss, 0, sizeof(fixed_ss));
+
+    pa_tagstruct_putu32(t, e->index);
+    pa_tagstruct_puts(t, e->name);
+    pa_tagstruct_put_cvolume(t, &e->volume);
+    pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
+    pa_tagstruct_put_sample_spec(t, &fixed_ss);
+    pa_tagstruct_put_channel_map(t, &e->channel_map);
+    pa_tagstruct_putu32(t, e->memchunk.length);
+    pa_tagstruct_put_boolean(t, e->lazy);
+    pa_tagstruct_puts(t, e->filename);
+
+    if (c->version >= 13)
+        pa_tagstruct_put_proplist(t, e->proplist);
+}
+
+static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    pa_sink *sink = NULL;
+    pa_source *source = NULL;
+    pa_client *client = NULL;
+    pa_module *module = NULL;
+    pa_sink_input *si = NULL;
+    pa_source_output *so = NULL;
+    pa_scache_entry *sce = NULL;
+    const char *name;
+    pa_tagstruct *reply;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        (command != PA_COMMAND_GET_CLIENT_INFO &&
+         command != PA_COMMAND_GET_MODULE_INFO &&
+         command != PA_COMMAND_GET_SINK_INPUT_INFO &&
+         command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO &&
+         pa_tagstruct_gets(t, &name) < 0) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_GET_SINK_INFO) {
+        if (idx != PA_INVALID_INDEX)
+            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+        else
+            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+    } else if (command == PA_COMMAND_GET_SOURCE_INFO) {
+        if (idx != PA_INVALID_INDEX)
+            source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+        else
+            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+    } else if (command == PA_COMMAND_GET_CLIENT_INFO)
+        client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
+    else if (command == PA_COMMAND_GET_MODULE_INFO)
+        module = pa_idxset_get_by_index(c->protocol->core->modules, idx);
+    else if (command == PA_COMMAND_GET_SINK_INPUT_INFO)
+        si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+    else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO)
+        so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+    else {
+        pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO);
+        if (idx != PA_INVALID_INDEX)
+            sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);
+        else
+            sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE, 0);
+    }
+
+    if (!sink && !source && !client && !module && !si && !so && !sce) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+        return;
+    }
+
+    reply = reply_new(tag);
+    if (sink)
+        sink_fill_tagstruct(c, reply, sink);
+    else if (source)
+        source_fill_tagstruct(c, reply, source);
+    else if (client)
+        client_fill_tagstruct(c, reply, client);
+    else if (module)
+        module_fill_tagstruct(reply, module);
+    else if (si)
+        sink_input_fill_tagstruct(c, reply, si);
+    else if (so)
+        source_output_fill_tagstruct(c, reply, so);
+    else
+        scache_fill_tagstruct(c, reply, sce);
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    pa_idxset *i;
+    uint32_t idx;
+    void *p;
+    pa_tagstruct *reply;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    reply = reply_new(tag);
+
+    if (command == PA_COMMAND_GET_SINK_INFO_LIST)
+        i = c->protocol->core->sinks;
+    else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
+        i = c->protocol->core->sources;
+    else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
+        i = c->protocol->core->clients;
+    else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
+        i = c->protocol->core->modules;
+    else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
+        i = c->protocol->core->sink_inputs;
+    else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
+        i = c->protocol->core->source_outputs;
+    else {
+        pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+        i = c->protocol->core->scache;
+    }
+
+    if (i) {
+        for (p = pa_idxset_first(i, &idx); p; p = pa_idxset_next(i, &idx)) {
+            if (command == PA_COMMAND_GET_SINK_INFO_LIST)
+                sink_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
+                source_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
+                client_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
+                module_fill_tagstruct(reply, p);
+            else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
+                sink_input_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
+                source_output_fill_tagstruct(c, reply, p);
+            else {
+                pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+                scache_fill_tagstruct(c, reply, p);
+            }
+        }
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    pa_tagstruct *reply;
+    char txt[256];
+    const char *n;
+    pa_sample_spec fixed_ss;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    reply = reply_new(tag);
+    pa_tagstruct_puts(reply, PACKAGE_NAME);
+    pa_tagstruct_puts(reply, PACKAGE_VERSION);
+    pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
+    pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt)));
+
+    fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
+    pa_tagstruct_put_sample_spec(reply, &fixed_ss);
+
+    n = pa_namereg_get_default_sink_name(c->protocol->core);
+    pa_tagstruct_puts(reply, n);
+    n = pa_namereg_get_default_source_name(c->protocol->core);
+    pa_tagstruct_puts(reply, n);
+
+    pa_tagstruct_putu32(reply, c->protocol->core->cookie);
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) {
+    pa_tagstruct *t;
+    connection *c = CONNECTION(userdata);
+
+    connection_assert_ref(c);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1);
+    pa_tagstruct_putu32(t, e);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+}
+
+static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    pa_subscription_mask_t m;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &m) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, (m & ~PA_SUBSCRIPTION_MASK_ALL) == 0, tag, PA_ERR_INVALID);
+
+    if (c->subscription)
+        pa_subscription_free(c->subscription);
+
+    if (m != 0) {
+        c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c);
+        pa_assert(c->subscription);
+    } else
+        c->subscription = NULL;
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_volume(
+        PA_GCC_UNUSED pa_pdispatch *pd,
+        uint32_t command,
+        uint32_t tag,
+        pa_tagstruct *t,
+        void *userdata) {
+
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    pa_cvolume volume;
+    pa_sink *sink = NULL;
+    pa_source *source = NULL;
+    pa_sink_input *si = NULL;
+    const char *name = NULL;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        (command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
+        (command == PA_COMMAND_SET_SOURCE_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
+        pa_tagstruct_get_cvolume(t, &volume) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
+
+    switch (command) {
+
+        case PA_COMMAND_SET_SINK_VOLUME:
+            if (idx != PA_INVALID_INDEX)
+                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+            else
+                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+            break;
+
+        case PA_COMMAND_SET_SOURCE_VOLUME:
+            if (idx != PA_INVALID_INDEX)
+                source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+            else
+                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+            break;
+
+        case PA_COMMAND_SET_SINK_INPUT_VOLUME:
+            si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
+
+    if (sink)
+        pa_sink_set_volume(sink, &volume);
+    else if (source)
+        pa_source_set_volume(source, &volume);
+    else if (si)
+        pa_sink_input_set_volume(si, &volume);
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_mute(
+        PA_GCC_UNUSED pa_pdispatch *pd,
+        uint32_t command,
+        uint32_t tag,
+        pa_tagstruct *t,
+        void *userdata) {
+
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    pa_bool_t mute;
+    pa_sink *sink = NULL;
+    pa_source *source = NULL;
+    pa_sink_input *si = NULL;
+    const char *name = NULL;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        (command == PA_COMMAND_SET_SINK_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
+        (command == PA_COMMAND_SET_SOURCE_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
+        pa_tagstruct_get_boolean(t, &mute) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+
+    switch (command) {
+
+        case PA_COMMAND_SET_SINK_MUTE:
+
+            if (idx != PA_INVALID_INDEX)
+                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+            else
+                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+
+            break;
+
+        case PA_COMMAND_SET_SOURCE_MUTE:
+            if (idx != PA_INVALID_INDEX)
+                source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+            else
+                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+
+            break;
+
+        case PA_COMMAND_SET_SINK_INPUT_MUTE:
+            si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
+
+    if (sink)
+        pa_sink_set_mute(sink, mute);
+    else if (source)
+        pa_source_set_mute(source, mute);
+    else if (si)
+        pa_sink_input_set_mute(si, mute);
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    pa_bool_t b;
+    playback_stream *s;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_get_boolean(t, &b) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+    s = pa_idxset_get_by_index(c->output_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+    pa_sink_input_cork(s->sink_input, b);
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_trigger_or_flush_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    playback_stream *s;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+    s = pa_idxset_get_by_index(c->output_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+    switch (command) {
+        case PA_COMMAND_FLUSH_PLAYBACK_STREAM:
+            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_FLUSH, NULL, 0, NULL);
+            break;
+
+        case PA_COMMAND_PREBUF_PLAYBACK_STREAM:
+            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_PREBUF_FORCE, NULL, 0, NULL);
+            break;
+
+        case PA_COMMAND_TRIGGER_PLAYBACK_STREAM:
+            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_TRIGGER, NULL, 0, NULL);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    record_stream *s;
+    pa_bool_t b;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_get_boolean(t, &b) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    s = pa_idxset_get_by_index(c->record_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+    pa_source_output_cork(s->source_output, b);
+    pa_memblockq_prebuf_force(s->memblockq);
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    record_stream *s;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    s = pa_idxset_get_by_index(c->record_streams, idx);
+    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+    pa_memblockq_flush(s->memblockq);
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    uint32_t maxlength, tlength, prebuf, minreq, fragsize;
+    pa_tagstruct *reply;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
+        playback_stream *s;
+        pa_bool_t adjust_latency = FALSE;
+
+        s = pa_idxset_get_by_index(c->output_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+        if (pa_tagstruct_get(
+                    t,
+                    PA_TAG_U32, &maxlength,
+                    PA_TAG_U32, &tlength,
+                    PA_TAG_U32, &prebuf,
+                    PA_TAG_U32, &minreq,
+                    PA_TAG_INVALID) < 0 ||
+            (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+            !pa_tagstruct_eof(t)) {
+            protocol_error(c);
+            return;
+        }
+
+        fix_playback_buffer_attr_pre(s, adjust_latency, &maxlength, &tlength, &prebuf, &minreq);
+        pa_memblockq_set_maxlength(s->memblockq, maxlength);
+        pa_memblockq_set_tlength(s->memblockq, tlength);
+        pa_memblockq_set_prebuf(s->memblockq, prebuf);
+        pa_memblockq_set_minreq(s->memblockq, minreq);
+        fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+
+        reply = reply_new(tag);
+        pa_tagstruct_putu32(reply, maxlength);
+        pa_tagstruct_putu32(reply, tlength);
+        pa_tagstruct_putu32(reply, prebuf);
+        pa_tagstruct_putu32(reply, minreq);
+
+        if (c->version >= 13)
+            pa_tagstruct_put_usec(reply, s->sink_latency);
+
+    } else {
+        record_stream *s;
+        pa_bool_t adjust_latency = FALSE;
+        pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
+
+        s = pa_idxset_get_by_index(c->record_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        if (pa_tagstruct_get(
+                    t,
+                    PA_TAG_U32, &maxlength,
+                    PA_TAG_U32, &fragsize,
+                    PA_TAG_INVALID) < 0 ||
+            (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
+            !pa_tagstruct_eof(t)) {
+            protocol_error(c);
+            return;
+        }
+
+        fix_record_buffer_attr_pre(s, adjust_latency, &maxlength, &fragsize);
+        pa_memblockq_set_maxlength(s->memblockq, maxlength);
+        fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+
+        reply = reply_new(tag);
+        pa_tagstruct_putu32(reply, maxlength);
+        pa_tagstruct_putu32(reply, fragsize);
+
+        if (c->version >= 13)
+            pa_tagstruct_put_usec(reply, s->source_latency);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    uint32_t rate;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_getu32(t, &rate) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, rate > 0 && rate <= PA_RATE_MAX, tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE) {
+        playback_stream *s;
+
+        s = pa_idxset_get_by_index(c->output_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+        pa_sink_input_set_rate(s->sink_input, rate);
+
+    } else {
+        record_stream *s;
+        pa_assert(command == PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE);
+
+        s = pa_idxset_get_by_index(c->record_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        pa_source_output_set_rate(s->source_output, rate);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    uint32_t mode;
+    pa_proplist *p;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    p = pa_proplist_new();
+
+    if (command == PA_COMMAND_UPDATE_CLIENT_PROPLIST) {
+
+        if (pa_tagstruct_getu32(t, &mode) < 0 ||
+            pa_tagstruct_get_proplist(t, p) < 0 ||
+            !pa_tagstruct_eof(t)) {
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+
+    } else {
+
+        if (pa_tagstruct_getu32(t, &idx) < 0 ||
+            pa_tagstruct_getu32(t, &mode) < 0 ||
+            pa_tagstruct_get_proplist(t, p) < 0 ||
+            !pa_tagstruct_eof(t)) {
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
+    CHECK_VALIDITY(c->pstream, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
+        playback_stream *s;
+
+        s = pa_idxset_get_by_index(c->output_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+        pa_proplist_update(s->sink_input->proplist, mode, p);
+        pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+
+    } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
+        record_stream *s;
+
+        s = pa_idxset_get_by_index(c->record_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        pa_proplist_update(s->source_output->proplist, mode, p);
+        pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
+    } else {
+        pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
+
+        pa_proplist_update(c->client->proplist, mode, p);
+        pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    unsigned changed = 0;
+    pa_proplist *p;
+    pa_strlist *l = NULL;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    if (command != PA_COMMAND_REMOVE_CLIENT_PROPLIST) {
+
+        if (pa_tagstruct_getu32(t, &idx) < 0) {
+            protocol_error(c);
+            return;
+        }
+    }
+
+    if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
+        playback_stream *s;
+
+        s = pa_idxset_get_by_index(c->output_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+        p = s->sink_input->proplist;
+
+    } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
+        record_stream *s;
+
+        s = pa_idxset_get_by_index(c->record_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        p = s->source_output->proplist;
+    } else {
+        pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
+
+        p = c->client->proplist;
+    }
+
+    for (;;) {
+        const char *k;
+
+        if (pa_tagstruct_gets(t, &k) < 0) {
+            protocol_error(c);
+            pa_strlist_free(l);
+            return;
+        }
+
+        if (!k)
+            break;
+
+        l = pa_strlist_prepend(l, k);
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        pa_strlist_free(l);
+        return;
+    }
+
+    for (;;) {
+        char *z;
+
+        l = pa_strlist_pop(l, &z);
+
+        if (!z)
+            break;
+
+        changed += pa_proplist_unset(p, z) >= 0;
+        pa_xfree(z);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+
+    if (changed) {
+        if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
+            playback_stream *s;
+
+            s = pa_idxset_get_by_index(c->output_streams, idx);
+            pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+
+        } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
+            record_stream *s;
+
+            s = pa_idxset_get_by_index(c->record_streams, idx);
+            pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
+
+        } else {
+            pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
+            pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+        }
+    }
+}
+
+static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    const char *s;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &s) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !s || (*s && pa_utf8_valid(s)), tag, PA_ERR_INVALID);
+
+    pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK);
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    const char *name;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) {
+        playback_stream *s;
+
+        s = pa_idxset_get_by_index(c->output_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+        pa_sink_input_set_name(s->sink_input, name);
+
+    } else {
+        record_stream *s;
+        pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_NAME);
+
+        s = pa_idxset_get_by_index(c->record_streams, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        pa_source_output_set_name(s->source_output, name);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    if (command == PA_COMMAND_KILL_CLIENT) {
+        pa_client *client;
+
+        client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
+        CHECK_VALIDITY(c->pstream, client, tag, PA_ERR_NOENTITY);
+
+        connection_ref(c);
+        pa_client_kill(client);
+
+    } else if (command == PA_COMMAND_KILL_SINK_INPUT) {
+        pa_sink_input *s;
+
+        s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        connection_ref(c);
+        pa_sink_input_kill(s);
+    } else {
+        pa_source_output *s;
+
+        pa_assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);
+
+        s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+        connection_ref(c);
+        pa_source_output_kill(s);
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+    connection_unref(c);
+}
+
+static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    pa_module *m;
+    const char *name, *argument;
+    pa_tagstruct *reply;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &argument) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name) && !strchr(name, '/'), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID);
+
+    if (!(m = pa_module_load(c->protocol->core, name, argument))) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_MODINITFAILED);
+        return;
+    }
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, m->index);
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx;
+    pa_module *m;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
+    CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOENTITY);
+
+    pa_module_unload_request(m);
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    const char *name, *module, *argument;
+    uint32_t type;
+    uint32_t idx;
+    pa_tagstruct *reply;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_getu32(t, &type) < 0 ||
+        pa_tagstruct_gets(t, &module) < 0 ||
+        pa_tagstruct_gets(t, &argument) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, type == 0 || type == 1, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, module && *module && pa_utf8_valid(module), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID);
+
+    if (pa_autoload_add(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, module, argument, &idx) < 0) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
+        return;
+    }
+
+    reply = reply_new(tag);
+    pa_tagstruct_putu32(reply, idx);
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    const char *name = NULL;
+    uint32_t type, idx = PA_IDXSET_INVALID;
+    int r;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if ((pa_tagstruct_getu32(t, &idx) < 0 &&
+        (pa_tagstruct_gets(t, &name) < 0 ||
+         pa_tagstruct_getu32(t, &type) < 0)) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name || idx != PA_IDXSET_INVALID, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, !name || (*name && pa_utf8_valid(name) && (type == 0 || type == 1)), tag, PA_ERR_INVALID);
+
+    if (name)
+        r = pa_autoload_remove_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
+    else
+        r = pa_autoload_remove_by_index(c->protocol->core, idx);
+
+    CHECK_VALIDITY(c->pstream, r >= 0, tag, PA_ERR_NOENTITY);
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e) {
+    pa_assert(t && e);
+
+    pa_tagstruct_putu32(t, e->index);
+    pa_tagstruct_puts(t, e->name);
+    pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0 : 1);
+    pa_tagstruct_puts(t, e->module);
+    pa_tagstruct_puts(t, e->argument);
+}
+
+static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    const pa_autoload_entry *a = NULL;
+    uint32_t type, idx;
+    const char *name;
+    pa_tagstruct *reply;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if ((pa_tagstruct_getu32(t, &idx) < 0 &&
+        (pa_tagstruct_gets(t, &name) < 0 ||
+         pa_tagstruct_getu32(t, &type) < 0)) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, name || idx != PA_IDXSET_INVALID, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, !name || (*name && (type == 0 || type == 1) && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+
+    if (name)
+        a = pa_autoload_get_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
+    else
+        a = pa_autoload_get_by_index(c->protocol->core, idx);
+
+    CHECK_VALIDITY(c->pstream, a, tag, PA_ERR_NOENTITY);
+
+    reply = reply_new(tag);
+    autoload_fill_tagstruct(reply, a);
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    pa_tagstruct *reply;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    reply = reply_new(tag);
+
+    if (c->protocol->core->autoload_hashmap) {
+        pa_autoload_entry *a;
+        void *state = NULL;
+
+        while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state, NULL)))
+            autoload_fill_tagstruct(reply, a);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;
+    const char *name = NULL;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_getu32(t, &idx_device) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, idx_device != PA_INVALID_INDEX || !name || (*name && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_MOVE_SINK_INPUT) {
+        pa_sink_input *si = NULL;
+        pa_sink *sink = NULL;
+
+        si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
+
+        if (idx_device != PA_INVALID_INDEX)
+            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device);
+        else
+            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+
+        CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
+
+        if (pa_sink_input_move_to(si, sink) < 0) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+            return;
+        }
+    } else {
+        pa_source_output *so = NULL;
+        pa_source *source;
+
+        pa_assert(command == PA_COMMAND_MOVE_SOURCE_OUTPUT);
+
+        so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+
+        if (idx_device != PA_INVALID_INDEX)
+            source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device);
+        else
+            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+
+        CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
+
+        if (pa_source_output_move_to(so, source) < 0) {
+            pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+            return;
+        }
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    uint32_t idx = PA_INVALID_INDEX;
+    const char *name = NULL;
+    pa_bool_t b;
+
+    connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_get_boolean(t, &b) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || !name || !*name || pa_utf8_valid(name), tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_SUSPEND_SINK) {
+
+        if (idx == PA_INVALID_INDEX && name && !*name) {
+
+            if (pa_sink_suspend_all(c->protocol->core, b) < 0) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+                return;
+            }
+        } else {
+            pa_sink *sink = NULL;
+
+            if (idx != PA_INVALID_INDEX)
+                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+            else
+                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+
+            CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+            if (pa_sink_suspend(sink, b) < 0) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+                return;
+            }
+        }
+    } else {
+
+        pa_assert(command == PA_COMMAND_SUSPEND_SOURCE);
+
+        if (idx == PA_INVALID_INDEX && name && !*name) {
+
+            if (pa_source_suspend_all(c->protocol->core, b) < 0) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+                return;
+            }
+
+        } else {
+            pa_source *source;
+
+            if (idx != PA_INVALID_INDEX)
+                source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+            else
+                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+
+            CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+            if (pa_source_suspend(source, b) < 0) {
+                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+                return;
+            }
+        }
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+/*** pstream callbacks ***/
+
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    pa_assert(p);
+    pa_assert(packet);
+    connection_assert_ref(c);
+
+    if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) {
+        pa_log("invalid packet.");
+        connection_unlink(c);
+    }
+}
+
+static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
+    connection *c = CONNECTION(userdata);
+    output_stream *stream;
+
+    pa_assert(p);
+    pa_assert(chunk);
+    connection_assert_ref(c);
+
+    if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
+        pa_log("client sent block for invalid stream.");
+        /* Ignoring */
+        return;
+    }
+
+    if (playback_stream_isinstance(stream)) {
+        playback_stream *ps = PLAYBACK_STREAM(stream);
+
+        if (seek != PA_SEEK_RELATIVE || offset != 0)
+            pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, NULL, NULL);
+
+        pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+
+    } else {
+        upload_stream *u = UPLOAD_STREAM(stream);
+        size_t l;
+
+        if (!u->memchunk.memblock) {
+            if (u->length == chunk->length) {
+                u->memchunk = *chunk;
+                pa_memblock_ref(u->memchunk.memblock);
+                u->length = 0;
+            } else {
+                u->memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, u->length);
+                u->memchunk.index = u->memchunk.length = 0;
+            }
+        }
+
+        pa_assert(u->memchunk.memblock);
+
+        l = u->length;
+        if (l > chunk->length)
+            l = chunk->length;
+
+
+        if (l > 0) {
+            void *src, *dst;
+            dst = pa_memblock_acquire(u->memchunk.memblock);
+            src = pa_memblock_acquire(chunk->memblock);
+
+            memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
+                   (uint8_t*) src+chunk->index, l);
+
+            pa_memblock_release(u->memchunk.memblock);
+            pa_memblock_release(chunk->memblock);
+
+            u->memchunk.length += l;
+            u->length -= l;
+        }
+    }
+}
+
+static void pstream_die_callback(pa_pstream *p, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    pa_assert(p);
+    connection_assert_ref(c);
+
+    connection_unlink(c);
+    pa_log_info("connection died.");
+}
+
+static void pstream_drain_callback(pa_pstream *p, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    pa_assert(p);
+    connection_assert_ref(c);
+
+    send_memblock(c);
+}
+
+static void pstream_revoke_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
+    pa_thread_mq *q;
+
+    if (!(q = pa_thread_mq_get()))
+        pa_pstream_send_revoke(p, block_id);
+    else
+        pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_REVOKE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
+}
+
+static void pstream_release_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
+    pa_thread_mq *q;
+
+    if (!(q = pa_thread_mq_get()))
+        pa_pstream_send_release(p, block_id);
+    else
+        pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_RELEASE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(pa_client *c) {
+    pa_assert(c);
+
+    connection_unlink(CONNECTION(c->userdata));
+}
+
+/*** socket server callbacks ***/
+
+static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    pa_assert(m);
+    pa_assert(tv);
+    connection_assert_ref(c);
+    pa_assert(c->auth_timeout_event == e);
+
+    if (!c->authorized)
+        connection_unlink(c);
+}
+
+static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, void *userdata) {
+    pa_protocol_native *p = userdata;
+    connection *c;
+    char cname[256], pname[128];
+
+    pa_assert(io);
+    pa_assert(p);
+
+    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+
+    c = pa_msgobject_new(connection);
+    c->parent.parent.free = connection_free;
+    c->parent.process_msg = connection_process_msg;
+
+    c->authorized = p->public;
+
+    if (!c->authorized && p->auth_ip_acl && pa_ip_acl_check(p->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
+        pa_log_info("Client authenticated by IP ACL.");
+        c->authorized = TRUE;
+    }
+
+    if (!c->authorized) {
+        struct timeval tv;
+        pa_gettimeofday(&tv);
+        tv.tv_sec += AUTH_TIMEOUT;
+        c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
+    } else
+        c->auth_timeout_event = NULL;
+
+    c->is_local = pa_iochannel_socket_is_local(io);
+    c->version = 8;
+    c->protocol = p;
+    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+    pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname);
+    c->client = pa_client_new(p->core, __FILE__, cname);
+    pa_proplist_sets(c->client->proplist, "native-protocol.peer", pname);
+    c->client->kill = client_kill_cb;
+    c->client->userdata = c;
+    c->client->module = p->module;
+
+    c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
+
+    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);
+    pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c);
+    pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);
+
+    c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX);
+
+    c->record_streams = pa_idxset_new(NULL, NULL);
+    c->output_streams = pa_idxset_new(NULL, NULL);
+
+    c->rrobin_index = PA_IDXSET_INVALID;
+    c->subscription = NULL;
+
+    pa_idxset_put(p->connections, c, NULL);
+
+#ifdef HAVE_CREDS
+    if (pa_iochannel_creds_supported(io))
+        pa_iochannel_creds_enable(io);
+#endif
+}
+
+/*** module entry points ***/
+
+static int load_key(pa_protocol_native*p, const char*fn) {
+    pa_assert(p);
+
+    p->auth_cookie_in_property = FALSE;
+
+    if (!fn && pa_authkey_prop_get(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) {
+        pa_log_info("using already loaded auth cookie.");
+        pa_authkey_prop_ref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+        p->auth_cookie_in_property = TRUE;
+        return 0;
+    }
+
+    if (!fn)
+        fn = PA_NATIVE_COOKIE_FILE;
+
+    if (pa_authkey_load_auto(fn, p->auth_cookie, sizeof(p->auth_cookie)) < 0)
+        return -1;
+
+    pa_log_info("loading cookie from disk.");
+
+    if (pa_authkey_prop_put(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0)
+        p->auth_cookie_in_property = TRUE;
+
+    return 0;
+}
+
+static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_modargs *ma) {
+    pa_protocol_native *p;
+    pa_bool_t public = FALSE;
+    const char *acl;
+
+    pa_assert(c);
+    pa_assert(ma);
+
+    if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {
+        pa_log("auth-anonymous= expects a boolean argument.");
+        return NULL;
+    }
+
+    p = pa_xnew(pa_protocol_native, 1);
+    p->core = c;
+    p->module = m;
+    p->public = public;
+    p->server = NULL;
+    p->auth_ip_acl = NULL;
+
+#ifdef HAVE_CREDS
+    {
+        pa_bool_t a = TRUE;
+        if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &a) < 0) {
+            pa_log("auth-group-enabled= expects a boolean argument.");
+            return NULL;
+        }
+        p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", pa_in_system_mode() ? PA_ACCESS_GROUP : NULL)) : NULL;
+
+        if (p->auth_group)
+            pa_log_info("Allowing access to group '%s'.", p->auth_group);
+    }
+#endif
+
+
+    if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
+
+        if (!(p->auth_ip_acl = pa_ip_acl_new(acl))) {
+            pa_log("Failed to parse IP ACL '%s'", acl);
+            goto fail;
+        }
+    }
+
+    if (load_key(p, pa_modargs_get_value(ma, "cookie", NULL)) < 0)
+        goto fail;
+
+    p->connections = pa_idxset_new(NULL, NULL);
+
+    return p;
+
+fail:
+#ifdef HAVE_CREDS
+    pa_xfree(p->auth_group);
+#endif
+    if (p->auth_ip_acl)
+        pa_ip_acl_free(p->auth_ip_acl);
+    pa_xfree(p);
+    return NULL;
+}
+
+pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
+    char t[256];
+    pa_protocol_native *p;
+
+    if (!(p = protocol_new_internal(core, m, ma)))
+        return NULL;
+
+    p->server = pa_socket_server_ref(server);
+    pa_socket_server_set_callback(p->server, on_connection, p);
+
+    if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
+        pa_strlist *l;
+        l = pa_property_get(core, PA_NATIVE_SERVER_PROPERTY_NAME);
+        l = pa_strlist_prepend(l, t);
+        pa_property_replace(core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
+    }
+
+    return p;
+}
+
+void pa_protocol_native_free(pa_protocol_native *p) {
+    connection *c;
+    pa_assert(p);
+
+    while ((c = pa_idxset_first(p->connections, NULL)))
+        connection_unlink(c);
+    pa_idxset_free(p->connections, NULL, NULL);
+
+    if (p->server) {
+        char t[256];
+
+        if (pa_socket_server_get_address(p->server, t, sizeof(t))) {
+            pa_strlist *l;
+            l = pa_property_get(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
+            l = pa_strlist_remove(l, t);
+
+            if (l)
+                pa_property_replace(p->core, PA_NATIVE_SERVER_PROPERTY_NAME, l);
+            else
+                pa_property_remove(p->core, PA_NATIVE_SERVER_PROPERTY_NAME);
+        }
+
+        pa_socket_server_unref(p->server);
+    }
+
+    if (p->auth_cookie_in_property)
+        pa_authkey_prop_unref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
+
+    if (p->auth_ip_acl)
+        pa_ip_acl_free(p->auth_ip_acl);
+
+#ifdef HAVE_CREDS
+    pa_xfree(p->auth_group);
+#endif
+    pa_xfree(p);
+}
+
+pa_protocol_native* pa_protocol_native_new_iochannel(
+        pa_core*core,
+        pa_iochannel *io,
+        pa_module *m,
+        pa_modargs *ma) {
+
+    pa_protocol_native *p;
+
+    if (!(p = protocol_new_internal(core, m, ma)))
+        return NULL;
+
+    on_connection(NULL, io, p);
+
+    return p;
+}
diff --git a/src/pulsecore/protocol-native.h b/src/pulsecore/protocol-native.h
new file mode 100644 (file)
index 0000000..a52fa8c
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef fooprotocolnativehfoo
+#define fooprotocolnativehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/core.h>
+#include <pulsecore/socket-server.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+
+typedef struct pa_protocol_native pa_protocol_native;
+
+pa_protocol_native* pa_protocol_native_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
+void pa_protocol_native_free(pa_protocol_native *n);
+
+pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma);
+
+#endif
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
new file mode 100644 (file)
index 0000000..020a281
--- /dev/null
@@ -0,0 +1,696 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <limits.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/core-util.h>
+
+#include "protocol-simple.h"
+
+/* Don't allow more than this many concurrent connections */
+#define MAX_CONNECTIONS 10
+
+typedef struct connection {
+    pa_msgobject parent;
+    pa_protocol_simple *protocol;
+    pa_iochannel *io;
+    pa_sink_input *sink_input;
+    pa_source_output *source_output;
+    pa_client *client;
+    pa_memblockq *input_memblockq, *output_memblockq;
+
+    pa_bool_t dead;
+
+    struct {
+        pa_memblock *current_memblock;
+        size_t memblock_index;
+        pa_atomic_t missing;
+        pa_bool_t underrun;
+    } playback;
+} connection;
+
+PA_DECLARE_CLASS(connection);
+#define CONNECTION(o) (connection_cast(o))
+static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
+
+struct pa_protocol_simple {
+    pa_module *module;
+    pa_core *core;
+    pa_socket_server*server;
+    pa_idxset *connections;
+
+    enum {
+        RECORD = 1,
+        PLAYBACK = 2,
+        DUPLEX = 3
+    } mode;
+
+    pa_sample_spec sample_spec;
+    char *source_name, *sink_name;
+};
+
+enum {
+    SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
+    SINK_INPUT_MESSAGE_DISABLE_PREBUF /* disabled prebuf, get playback started. */
+};
+
+enum {
+    CONNECTION_MESSAGE_REQUEST_DATA,      /* data requested from sink input from the main loop */
+    CONNECTION_MESSAGE_POST_DATA,         /* data from source output to main loop */
+    CONNECTION_MESSAGE_UNLINK_CONNECTION    /* Please drop a aconnection now */
+};
+
+
+#define PLAYBACK_BUFFER_SECONDS (.5)
+#define PLAYBACK_BUFFER_FRAGMENTS (10)
+#define RECORD_BUFFER_SECONDS (5)
+#define DEFAULT_SINK_LATENCY (300*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
+
+static void connection_unlink(connection *c) {
+    pa_assert(c);
+
+    if (!c->protocol)
+        return;
+
+    if (c->sink_input) {
+        pa_sink_input_unlink(c->sink_input);
+        pa_sink_input_unref(c->sink_input);
+        c->sink_input = NULL;
+    }
+
+    if (c->source_output) {
+        pa_source_output_unlink(c->source_output);
+        pa_source_output_unref(c->source_output);
+        c->source_output = NULL;
+    }
+
+    if (c->client) {
+        pa_client_free(c->client);
+        c->client = NULL;
+    }
+
+    if (c->io) {
+        pa_iochannel_free(c->io);
+        c->io = NULL;
+    }
+
+    pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
+    c->protocol = NULL;
+    connection_unref(c);
+}
+
+static void connection_free(pa_object *o) {
+    connection *c = CONNECTION(o);
+    pa_assert(c);
+
+    if (c->playback.current_memblock)
+        pa_memblock_unref(c->playback.current_memblock);
+
+    if (c->input_memblockq)
+        pa_memblockq_free(c->input_memblockq);
+    if (c->output_memblockq)
+        pa_memblockq_free(c->output_memblockq);
+
+    pa_xfree(c);
+}
+
+static int do_read(connection *c) {
+    pa_memchunk chunk;
+    ssize_t r;
+    size_t l;
+    void *p;
+    size_t space;
+
+    connection_assert_ref(c);
+
+    if (!c->sink_input || (l = pa_atomic_load(&c->playback.missing)) <= 0)
+        return 0;
+
+    if (c->playback.current_memblock) {
+
+        space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
+
+        if (space <= 0) {
+            pa_memblock_unref(c->playback.current_memblock);
+            c->playback.current_memblock = NULL;
+        }
+    }
+
+    if (!c->playback.current_memblock) {
+        pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, 0));
+        c->playback.memblock_index = 0;
+
+        space = pa_memblock_get_length(c->playback.current_memblock);
+    }
+
+    if (l > space)
+        l = space;
+
+    p = pa_memblock_acquire(c->playback.current_memblock);
+    r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l);
+    pa_memblock_release(c->playback.current_memblock);
+
+    if (r <= 0) {
+
+        if (r < 0 && (errno == EINTR || errno == EAGAIN))
+            return 0;
+
+        pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno));
+        return -1;
+    }
+
+    chunk.memblock = c->playback.current_memblock;
+    chunk.index = c->playback.memblock_index;
+    chunk.length = r;
+
+    c->playback.memblock_index += r;
+
+    pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
+    pa_atomic_sub(&c->playback.missing, r);
+
+    return 0;
+}
+
+static int do_write(connection *c) {
+    pa_memchunk chunk;
+    ssize_t r;
+    void *p;
+
+    connection_assert_ref(c);
+
+    if (!c->source_output)
+        return 0;
+
+    if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) {
+/*         pa_log("peek failed"); */
+        return 0;
+    }
+
+    pa_assert(chunk.memblock);
+    pa_assert(chunk.length);
+
+    p = pa_memblock_acquire(chunk.memblock);
+    r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
+    pa_memblock_release(chunk.memblock);
+
+    pa_memblock_unref(chunk.memblock);
+
+    if (r < 0) {
+
+        if (errno == EINTR || errno == EAGAIN)
+            return 0;
+
+        pa_log("write(): %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_memblockq_drop(c->output_memblockq, r);
+
+    return 0;
+}
+
+static void do_work(connection *c) {
+    connection_assert_ref(c);
+
+    if (c->dead)
+        return;
+
+    if (pa_iochannel_is_readable(c->io))
+        if (do_read(c) < 0)
+            goto fail;
+
+    if (!c->sink_input && pa_iochannel_is_hungup(c->io))
+        goto fail;
+
+    if (pa_iochannel_is_writable(c->io))
+        if (do_write(c) < 0)
+            goto fail;
+
+    return;
+
+fail:
+
+    if (c->sink_input) {
+
+        /* If there is a sink input, we first drain what we already have read before shutting down the connection */
+        c->dead = TRUE;
+
+        pa_iochannel_free(c->io);
+        c->io = NULL;
+
+        pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);
+    } else
+        connection_unlink(c);
+}
+
+static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    connection *c = CONNECTION(o);
+    connection_assert_ref(c);
+
+    switch (code) {
+        case CONNECTION_MESSAGE_REQUEST_DATA:
+            do_work(c);
+            break;
+
+        case CONNECTION_MESSAGE_POST_DATA:
+/*             pa_log("got data %u", chunk->length); */
+            pa_memblockq_push_align(c->output_memblockq, chunk);
+            do_work(c);
+            break;
+
+        case CONNECTION_MESSAGE_UNLINK_CONNECTION:
+            connection_unlink(c);
+            break;
+    }
+
+    return 0;
+}
+
+/*** sink_input callbacks ***/
+
+/* Called from thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_sink_input *i = PA_SINK_INPUT(o);
+    connection*c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    switch (code) {
+
+        case SINK_INPUT_MESSAGE_POST_DATA: {
+            pa_assert(chunk);
+
+            /* New data from the main loop */
+            pa_memblockq_push_align(c->input_memblockq, chunk);
+
+            if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+                pa_log_debug("Requesting rewind due to end of underrun.");
+                pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+            }
+
+/*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
+
+            return 0;
+        }
+
+        case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
+            pa_memblockq_prebuf_disable(c->input_memblockq);
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+        }
+
+        default:
+            return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
+    }
+}
+
+/* Called from thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+    pa_assert(chunk);
+
+    if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
+
+        c->playback.underrun = TRUE;
+
+        if (c->dead && pa_sink_input_safe_to_remove(i))
+            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+
+        return -1;
+    } else {
+        size_t m;
+
+        chunk->length = PA_MIN(length, chunk->length);
+
+        c->playback.underrun = FALSE;
+
+        pa_memblockq_drop(c->input_memblockq, chunk->length);
+        m = pa_memblockq_pop_missing(c->input_memblockq);
+
+        if (m > 0)
+            if (pa_atomic_add(&c->playback.missing, m) <= 0)
+                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+
+        return 0;
+    }
+}
+
+/* Called from thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    /* If we are in an underrun, then we don't rewind */
+    if (i->thread_info.underrun_for > 0)
+        return;
+
+    pa_memblockq_rewind(c->input_memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+
+    connection_unlink(CONNECTION(i->userdata));
+}
+
+/*** source_output callbacks ***/
+
+/* Called from thread context */
+static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
+    connection *c;
+
+    pa_source_output_assert_ref(o);
+    c = CONNECTION(o->userdata);
+    pa_assert(c);
+    pa_assert(chunk);
+
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+}
+
+/* Called from main context */
+static void source_output_kill_cb(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+
+    connection_unlink(CONNECTION(o->userdata));
+}
+
+/* Called from main context */
+static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
+    connection*c;
+
+    pa_source_output_assert_ref(o);
+    c = CONNECTION(o->userdata);
+    pa_assert(c);
+
+    return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(pa_client *client) {
+    connection*c;
+
+    pa_assert(client);
+    c = CONNECTION(client->userdata);
+    pa_assert(c);
+
+    connection_unlink(c);
+}
+
+/*** pa_iochannel callbacks ***/
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+    connection *c = CONNECTION(userdata);
+
+    connection_assert_ref(c);
+    pa_assert(io);
+
+    do_work(c);
+}
+
+/*** socket_server callbacks ***/
+
+static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+    pa_protocol_simple *p = userdata;
+    connection *c = NULL;
+    char cname[256], pname[128];
+
+    pa_assert(s);
+    pa_assert(io);
+    pa_assert(p);
+
+    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+
+    c = pa_msgobject_new(connection);
+    c->parent.parent.free = connection_free;
+    c->parent.process_msg = connection_process_msg;
+    c->io = io;
+    c->sink_input = NULL;
+    c->source_output = NULL;
+    c->input_memblockq = c->output_memblockq = NULL;
+    c->protocol = p;
+    c->playback.current_memblock = NULL;
+    c->playback.memblock_index = 0;
+    c->dead = FALSE;
+    c->playback.underrun = TRUE;
+    pa_atomic_store(&c->playback.missing, 0);
+
+    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+    pa_snprintf(cname, sizeof(cname), "Simple client (%s)", pname);
+    pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname));
+    pa_proplist_sets(c->client->proplist, "simple-protocol.peer", pname);
+    c->client->module = p->module;
+    c->client->kill = client_kill_cb;
+    c->client->userdata = c;
+
+    if (p->mode & PLAYBACK) {
+        pa_sink_input_new_data data;
+        size_t l;
+        pa_sink *sink;
+
+        if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, TRUE))) {
+            pa_log("Failed to get sink.");
+            goto fail;
+        }
+
+        pa_sink_input_new_data_init(&data);
+        data.driver = __FILE__;
+        data.module = p->module;
+        data.client = c->client;
+        data.sink = sink;
+        pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+        pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec);
+
+        c->sink_input = pa_sink_input_new(p->core, &data, 0);
+        pa_sink_input_new_data_done(&data);
+
+        if (!c->sink_input) {
+            pa_log("Failed to create sink input.");
+            goto fail;
+        }
+
+        c->sink_input->parent.process_msg = sink_input_process_msg;
+        c->sink_input->pop = sink_input_pop_cb;
+        c->sink_input->process_rewind = sink_input_process_rewind_cb;
+        c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+        c->sink_input->kill = sink_input_kill_cb;
+        c->sink_input->userdata = c;
+
+        pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
+        l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
+        c->input_memblockq = pa_memblockq_new(
+                0,
+                l,
+                l,
+                pa_frame_size(&p->sample_spec),
+                (size_t) -1,
+                l/PLAYBACK_BUFFER_FRAGMENTS,
+                0,
+                NULL);
+        pa_iochannel_socket_set_rcvbuf(io, l);
+
+        pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
+
+        pa_sink_input_put(c->sink_input);
+    }
+
+    if (p->mode & RECORD) {
+        pa_source_output_new_data data;
+        size_t l;
+        pa_source *source;
+
+        if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, TRUE))) {
+            pa_log("Failed to get source.");
+            goto fail;
+        }
+
+        pa_source_output_new_data_init(&data);
+        data.driver = __FILE__;
+        data.module = p->module;
+        data.client = c->client;
+        data.source = source;
+        pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
+        pa_source_output_new_data_set_sample_spec(&data, &p->sample_spec);
+
+        c->source_output = pa_source_output_new(p->core, &data, 0);
+        pa_source_output_new_data_done(&data);
+
+        if (!c->source_output) {
+            pa_log("Failed to create source output.");
+            goto fail;
+        }
+        c->source_output->push = source_output_push_cb;
+        c->source_output->kill = source_output_kill_cb;
+        c->source_output->get_latency = source_output_get_latency_cb;
+        c->source_output->userdata = c;
+
+        pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
+        l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
+        c->output_memblockq = pa_memblockq_new(
+                0,
+                l,
+                0,
+                pa_frame_size(&p->sample_spec),
+                1,
+                0,
+                0,
+                NULL);
+        pa_iochannel_socket_set_sndbuf(io, l);
+
+        pa_source_output_put(c->source_output);
+    }
+
+    pa_iochannel_set_callback(c->io, io_callback, c);
+    pa_idxset_put(p->connections, c, NULL);
+
+    return;
+
+fail:
+    if (c)
+        connection_unlink(c);
+}
+
+pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
+    pa_protocol_simple* p = NULL;
+    pa_bool_t enable;
+
+    pa_assert(core);
+    pa_assert(server);
+    pa_assert(m);
+    pa_assert(ma);
+
+    p = pa_xnew0(pa_protocol_simple, 1);
+    p->module = m;
+    p->core = core;
+    p->server = pa_socket_server_ref(server);
+    p->connections = pa_idxset_new(NULL, NULL);
+
+    p->sample_spec = core->default_sample_spec;
+    if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) {
+        pa_log("Failed to parse sample type specification.");
+        goto fail;
+    }
+
+    p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
+    p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+
+    enable = FALSE;
+    if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) {
+        pa_log("record= expects a numeric argument.");
+        goto fail;
+    }
+    p->mode = enable ? RECORD : 0;
+
+    enable = TRUE;
+    if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
+        pa_log("playback= expects a numeric argument.");
+        goto fail;
+    }
+    p->mode |= enable ? PLAYBACK : 0;
+
+    if ((p->mode & (RECORD|PLAYBACK)) == 0) {
+        pa_log("neither playback nor recording enabled for protocol.");
+        goto fail;
+    }
+
+    pa_socket_server_set_callback(p->server, on_connection, p);
+
+    return p;
+
+fail:
+    if (p)
+        pa_protocol_simple_free(p);
+
+    return NULL;
+}
+
+
+void pa_protocol_simple_free(pa_protocol_simple *p) {
+    connection *c;
+    pa_assert(p);
+
+    if (p->connections) {
+        while((c = pa_idxset_first(p->connections, NULL)))
+            connection_unlink(c);
+
+        pa_idxset_free(p->connections, NULL, NULL);
+    }
+
+    if (p->server)
+        pa_socket_server_unref(p->server);
+
+    pa_xfree(p);
+}
diff --git a/src/pulsecore/protocol-simple.h b/src/pulsecore/protocol-simple.h
new file mode 100644 (file)
index 0000000..e1b3143
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef fooprotocolsimplehfoo
+#define fooprotocolsimplehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/socket-server.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core.h>
+#include <pulsecore/modargs.h>
+
+typedef struct pa_protocol_simple pa_protocol_simple;
+
+pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
+void pa_protocol_simple_free(pa_protocol_simple *n);
+
+#endif
similarity index 50%
rename from polyp/pstream-util.c
rename to src/pulsecore/pstream-util.c
index 8526f29ce0ad48062e9c72d3cde1a2c8740edd4c..f84f486a222cb6222ff646e8c711fb77d81f1b0f 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <config.h>
 #endif
 
-#include <assert.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/macro.h>
 
-#include "native-common.h"
 #include "pstream-util.h"
 
-void pa_pstream_send_tagstruct(struct pa_pstream *p, struct pa_tagstruct *t) {
+void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const pa_creds *creds) {
     size_t length;
     uint8_t *data;
-    struct pa_packet *packet;
-    assert(p);
-    assert(t);
-
-    data = pa_tagstruct_free_data(t, &length);
-    assert(data && length);
-    packet = pa_packet_new_dynamic(data, length);
-    assert(packet);
-    pa_pstream_send_packet(p, packet);
+    pa_packet *packet;
+
+    pa_assert(p);
+    pa_assert(t);
+
+    pa_assert_se(data = pa_tagstruct_free_data(t, &length));
+    pa_assert_se(packet = pa_packet_new_dynamic(data, length));
+    pa_pstream_send_packet(p, packet, creds);
     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);
+void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
+    pa_tagstruct *t;
+
+    pa_assert_se(t = pa_tagstruct_new(NULL, 0));
     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);
+void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) {
+    pa_tagstruct *t;
+
+    pa_assert_se(t = pa_tagstruct_new(NULL, 0));
     pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
     pa_tagstruct_putu32(t, tag);
     pa_pstream_send_tagstruct(p, t);
diff --git a/src/pulsecore/pstream-util.h b/src/pulsecore/pstream-util.h
new file mode 100644 (file)
index 0000000..ae0d79c
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef foopstreamutilhfoo
+#define foopstreamutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/tagstruct.h>
+#include <pulsecore/creds.h>
+
+/* The tagstruct is freed!*/
+void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const pa_creds *creds);
+
+#define pa_pstream_send_tagstruct(p, t) pa_pstream_send_tagstruct_with_creds((p), (t), NULL)
+
+void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error);
+void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag);
+
+#endif
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
new file mode 100644 (file)
index 0000000..e26ca47
--- /dev/null
@@ -0,0 +1,1025 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <unistd.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/winsock.h>
+#include <pulsecore/queue.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/macro.h>
+
+#include "pstream.h"
+
+/* We piggyback information if audio data blocks are stored in SHM on the seek mode */
+#define PA_FLAG_SHMDATA    0x80000000LU
+#define PA_FLAG_SHMRELEASE 0x40000000LU
+#define PA_FLAG_SHMREVOKE  0xC0000000LU
+#define PA_FLAG_SHMMASK    0xFF000000LU
+#define PA_FLAG_SEEKMASK   0x000000FFLU
+
+/* The sequence descriptor header consists of 5 32bit integers: */
+enum {
+    PA_PSTREAM_DESCRIPTOR_LENGTH,
+    PA_PSTREAM_DESCRIPTOR_CHANNEL,
+    PA_PSTREAM_DESCRIPTOR_OFFSET_HI,
+    PA_PSTREAM_DESCRIPTOR_OFFSET_LO,
+    PA_PSTREAM_DESCRIPTOR_FLAGS,
+    PA_PSTREAM_DESCRIPTOR_MAX
+};
+
+/* If we have an SHM block, this info follows the descriptor */
+enum {
+    PA_PSTREAM_SHM_BLOCKID,
+    PA_PSTREAM_SHM_SHMID,
+    PA_PSTREAM_SHM_INDEX,
+    PA_PSTREAM_SHM_LENGTH,
+    PA_PSTREAM_SHM_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_ALLOW PA_SCACHE_ENTRY_SIZE_MAX /* allow uploading a single sample in one frame at max */
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+struct item_info {
+    enum {
+        PA_PSTREAM_ITEM_PACKET,
+        PA_PSTREAM_ITEM_MEMBLOCK,
+        PA_PSTREAM_ITEM_SHMRELEASE,
+        PA_PSTREAM_ITEM_SHMREVOKE
+    } type;
+
+    /* packet info */
+    pa_packet *packet;
+#ifdef HAVE_CREDS
+    pa_bool_t with_creds;
+    pa_creds creds;
+#endif
+
+    /* memblock info */
+    pa_memchunk chunk;
+    uint32_t channel;
+    int64_t offset;
+    pa_seek_mode_t seek_mode;
+
+    /* release/revoke info */
+    uint32_t block_id;
+};
+
+struct pa_pstream {
+    PA_REFCNT_DECLARE;
+
+    pa_mainloop_api *mainloop;
+    pa_defer_event *defer_event;
+    pa_iochannel *io;
+
+    pa_queue *send_queue;
+
+    pa_bool_t dead;
+
+    struct {
+        pa_pstream_descriptor descriptor;
+        struct item_info* current;
+        uint32_t shm_info[PA_PSTREAM_SHM_MAX];
+        void *data;
+        size_t index;
+        pa_memchunk memchunk;
+    } write;
+
+    struct {
+        pa_pstream_descriptor descriptor;
+        pa_memblock *memblock;
+        pa_packet *packet;
+        uint32_t shm_info[PA_PSTREAM_SHM_MAX];
+        void *data;
+        size_t index;
+    } read;
+
+    pa_bool_t use_shm;
+    pa_memimport *import;
+    pa_memexport *export;
+
+    pa_pstream_packet_cb_t recieve_packet_callback;
+    void *recieve_packet_callback_userdata;
+
+    pa_pstream_memblock_cb_t recieve_memblock_callback;
+    void *recieve_memblock_callback_userdata;
+
+    pa_pstream_notify_cb_t drain_callback;
+    void *drain_callback_userdata;
+
+    pa_pstream_notify_cb_t die_callback;
+    void *die_callback_userdata;
+
+    pa_pstream_block_id_cb_t revoke_callback;
+    void *revoke_callback_userdata;
+
+    pa_pstream_block_id_cb_t release_callback;
+    void *release_callback_userdata;
+
+    pa_mempool *mempool;
+
+#ifdef HAVE_CREDS
+    pa_creds read_creds, write_creds;
+    pa_bool_t read_creds_valid, send_creds_now;
+#endif
+};
+
+static int do_write(pa_pstream *p);
+static int do_read(pa_pstream *p);
+
+static void do_something(pa_pstream *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    pa_pstream_ref(p);
+
+    p->mainloop->defer_enable(p->defer_event, 0);
+
+    if (!p->dead && pa_iochannel_is_readable(p->io)) {
+        if (do_read(p) < 0)
+            goto fail;
+    } else if (!p->dead && pa_iochannel_is_hungup(p->io))
+        goto fail;
+
+    if (!p->dead && pa_iochannel_is_writable(p->io)) {
+        if (do_write(p) < 0)
+            goto fail;
+    }
+
+    pa_pstream_unref(p);
+    return;
+
+fail:
+
+    if (p->die_callback)
+        p->die_callback(p, p->die_callback_userdata);
+
+    pa_pstream_unlink(p);
+    pa_pstream_unref(p);
+}
+
+static void io_callback(pa_iochannel*io, void *userdata) {
+    pa_pstream *p = userdata;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+    pa_assert(p->io == io);
+
+    do_something(p);
+}
+
+static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) {
+    pa_pstream *p = userdata;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+    pa_assert(p->defer_event == e);
+    pa_assert(p->mainloop == m);
+
+    do_something(p);
+}
+
+static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata);
+
+pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *pool) {
+    pa_pstream *p;
+
+    pa_assert(m);
+    pa_assert(io);
+    pa_assert(pool);
+
+    p = pa_xnew(pa_pstream, 1);
+    PA_REFCNT_INIT(p);
+    p->io = io;
+    pa_iochannel_set_callback(io, io_callback, p);
+    p->dead = FALSE;
+
+    p->mainloop = m;
+    p->defer_event = m->defer_new(m, defer_callback, p);
+    m->defer_enable(p->defer_event, 0);
+
+    p->send_queue = pa_queue_new();
+
+    p->write.current = NULL;
+    p->write.index = 0;
+    pa_memchunk_reset(&p->write.memchunk);
+    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_callback_userdata = NULL;
+    p->die_callback = NULL;
+    p->die_callback_userdata = NULL;
+    p->revoke_callback = NULL;
+    p->revoke_callback_userdata = NULL;
+    p->release_callback = NULL;
+    p->release_callback_userdata = NULL;
+
+    p->mempool = pool;
+
+    p->use_shm = FALSE;
+    p->export = NULL;
+
+    /* We do importing unconditionally */
+    p->import = pa_memimport_new(p->mempool, memimport_release_cb, p);
+
+    pa_iochannel_socket_set_rcvbuf(io, pa_mempool_block_size_max(p->mempool));
+    pa_iochannel_socket_set_sndbuf(io, pa_mempool_block_size_max(p->mempool));
+
+#ifdef HAVE_CREDS
+    p->send_creds_now = FALSE;
+    p->read_creds_valid = FALSE;
+#endif
+    return p;
+}
+
+static void item_free(void *item, PA_GCC_UNUSED void *q) {
+    struct item_info *i = item;
+    pa_assert(i);
+
+    if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) {
+        pa_assert(i->chunk.memblock);
+        pa_memblock_unref(i->chunk.memblock);
+    } else if (i->type == PA_PSTREAM_ITEM_PACKET) {
+        pa_assert(i->packet);
+        pa_packet_unref(i->packet);
+    }
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+        pa_xfree(i);
+}
+
+static void pstream_free(pa_pstream *p) {
+    pa_assert(p);
+
+    pa_pstream_unlink(p);
+
+    pa_queue_free(p->send_queue, item_free, NULL);
+
+    if (p->write.current)
+        item_free(p->write.current, NULL);
+
+    if (p->write.memchunk.memblock)
+        pa_memblock_unref(p->write.memchunk.memblock);
+
+    if (p->read.memblock)
+        pa_memblock_unref(p->read.memblock);
+
+    if (p->read.packet)
+        pa_packet_unref(p->read.packet);
+
+    pa_xfree(p);
+}
+
+void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *creds) {
+    struct item_info *i;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+    pa_assert(packet);
+
+    if (p->dead)
+        return;
+
+    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+        i = pa_xnew(struct item_info, 1);
+
+    i->type = PA_PSTREAM_ITEM_PACKET;
+    i->packet = pa_packet_ref(packet);
+
+#ifdef HAVE_CREDS
+    if ((i->with_creds = !!creds))
+        i->creds = *creds;
+#endif
+
+    pa_queue_push(p->send_queue, i);
+
+    p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek_mode, const pa_memchunk *chunk) {
+    size_t length, idx;
+    size_t bsm;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+    pa_assert(channel != (uint32_t) -1);
+    pa_assert(chunk);
+
+    if (p->dead)
+        return;
+
+    idx = 0;
+    length = chunk->length;
+
+    bsm = pa_mempool_block_size_max(p->mempool);
+
+    while (length > 0) {
+        struct item_info *i;
+        size_t n;
+
+        if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+            i = pa_xnew(struct item_info, 1);
+        i->type = PA_PSTREAM_ITEM_MEMBLOCK;
+
+        n = PA_MIN(length, bsm);
+        i->chunk.index = chunk->index + idx;
+        i->chunk.length = n;
+        i->chunk.memblock = pa_memblock_ref(chunk->memblock);
+
+        i->channel = channel;
+        i->offset = offset;
+        i->seek_mode = seek_mode;
+#ifdef HAVE_CREDS
+        i->with_creds = FALSE;
+#endif
+
+        pa_queue_push(p->send_queue, i);
+
+        idx += n;
+        length -= n;
+    }
+
+    p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+void pa_pstream_send_release(pa_pstream *p, uint32_t block_id) {
+    struct item_info *item;
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->dead)
+        return;
+
+/*     pa_log("Releasing block %u", block_id); */
+
+    if (!(item = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+        item = pa_xnew(struct item_info, 1);
+    item->type = PA_PSTREAM_ITEM_SHMRELEASE;
+    item->block_id = block_id;
+#ifdef HAVE_CREDS
+    item->with_creds = FALSE;
+#endif
+
+    pa_queue_push(p->send_queue, item);
+    p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+/* might be called from thread context */
+static void memimport_release_cb(pa_memimport *i, uint32_t block_id, void *userdata) {
+    pa_pstream *p = userdata;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->dead)
+        return;
+
+    if (p->release_callback)
+        p->release_callback(p, block_id, p->release_callback_userdata);
+    else
+        pa_pstream_send_release(p, block_id);
+}
+
+void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id) {
+    struct item_info *item;
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->dead)
+        return;
+/*     pa_log("Revoking block %u", block_id); */
+
+    if (!(item = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+        item = pa_xnew(struct item_info, 1);
+    item->type = PA_PSTREAM_ITEM_SHMREVOKE;
+    item->block_id = block_id;
+#ifdef HAVE_CREDS
+    item->with_creds = FALSE;
+#endif
+
+    pa_queue_push(p->send_queue, item);
+    p->mainloop->defer_enable(p->defer_event, 1);
+}
+
+/* might be called from thread context */
+static void memexport_revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) {
+    pa_pstream *p = userdata;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->revoke_callback)
+        p->revoke_callback(p, block_id, p->revoke_callback_userdata);
+    else
+        pa_pstream_send_revoke(p, block_id);
+}
+
+static void prepare_next_write_item(pa_pstream *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->write.current = pa_queue_pop(p->send_queue);
+
+    if (!p->write.current)
+        return;
+
+    p->write.index = 0;
+    p->write.data = NULL;
+    pa_memchunk_reset(&p->write.memchunk);
+
+    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = 0;
+    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1);
+    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = 0;
+    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = 0;
+    p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = 0;
+
+    if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) {
+
+        pa_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);
+
+    } else if (p->write.current->type == PA_PSTREAM_ITEM_SHMRELEASE) {
+
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(PA_FLAG_SHMRELEASE);
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl(p->write.current->block_id);
+
+    } else if (p->write.current->type == PA_PSTREAM_ITEM_SHMREVOKE) {
+
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(PA_FLAG_SHMREVOKE);
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl(p->write.current->block_id);
+
+    } else {
+        uint32_t flags;
+        pa_bool_t send_payload = TRUE;
+
+        pa_assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK);
+        pa_assert(p->write.current->chunk.memblock);
+
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel);
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl((uint32_t) (((uint64_t) p->write.current->offset) >> 32));
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = htonl((uint32_t) ((uint64_t) p->write.current->offset));
+
+        flags = p->write.current->seek_mode & PA_FLAG_SEEKMASK;
+
+        if (p->use_shm) {
+            uint32_t block_id, shm_id;
+            size_t offset, length;
+
+            pa_assert(p->export);
+
+            if (pa_memexport_put(p->export,
+                                 p->write.current->chunk.memblock,
+                                 &block_id,
+                                 &shm_id,
+                                 &offset,
+                                 &length) >= 0) {
+
+                flags |= PA_FLAG_SHMDATA;
+                send_payload = FALSE;
+
+                p->write.shm_info[PA_PSTREAM_SHM_BLOCKID] = htonl(block_id);
+                p->write.shm_info[PA_PSTREAM_SHM_SHMID] = htonl(shm_id);
+                p->write.shm_info[PA_PSTREAM_SHM_INDEX] = htonl((uint32_t) (offset + p->write.current->chunk.index));
+                p->write.shm_info[PA_PSTREAM_SHM_LENGTH] = htonl((uint32_t) p->write.current->chunk.length);
+
+                p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(sizeof(p->write.shm_info));
+                p->write.data = p->write.shm_info;
+            }
+/*             else */
+/*                 pa_log_warn("Failed to export memory block."); */
+        }
+
+        if (send_payload) {
+            p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length);
+            p->write.memchunk = p->write.current->chunk;
+            pa_memblock_ref(p->write.memchunk.memblock);
+            p->write.data = NULL;
+        }
+
+        p->write.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] = htonl(flags);
+    }
+
+#ifdef HAVE_CREDS
+    if ((p->send_creds_now = p->write.current->with_creds))
+        p->write_creds = p->write.current->creds;
+#endif
+}
+
+static int do_write(pa_pstream *p) {
+    void *d;
+    size_t l;
+    ssize_t r;
+    pa_memblock *release_memblock = NULL;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (!p->write.current)
+        prepare_next_write_item(p);
+
+    if (!p->write.current)
+        return 0;
+
+    if (p->write.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
+        d = (uint8_t*) p->write.descriptor + p->write.index;
+        l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;
+    } else {
+        pa_assert(p->write.data || p->write.memchunk.memblock);
+
+        if (p->write.data)
+            d = p->write.data;
+        else {
+            d = (uint8_t*) pa_memblock_acquire(p->write.memchunk.memblock) + p->write.memchunk.index;
+            release_memblock = p->write.memchunk.memblock;
+        }
+
+        d = (uint8_t*) d + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
+        l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
+    }
+
+    pa_assert(l > 0);
+
+#ifdef HAVE_CREDS
+    if (p->send_creds_now) {
+
+        if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_creds)) < 0)
+            goto fail;
+
+        p->send_creds_now = FALSE;
+    } else
+#endif
+
+    if ((r = pa_iochannel_write(p->io, d, l)) < 0)
+        goto fail;
+
+    if (release_memblock)
+        pa_memblock_release(release_memblock);
+
+    p->write.index += r;
+
+    if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE + ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {
+        pa_assert(p->write.current);
+        item_free(p->write.current, NULL);
+        p->write.current = NULL;
+
+        if (p->write.memchunk.memblock)
+            pa_memblock_unref(p->write.memchunk.memblock);
+
+        pa_memchunk_reset(&p->write.memchunk);
+
+        if (p->drain_callback && !pa_pstream_is_pending(p))
+            p->drain_callback(p, p->drain_callback_userdata);
+    }
+
+    return 0;
+
+fail:
+
+    if (release_memblock)
+        pa_memblock_release(release_memblock);
+
+    return -1;
+}
+
+static int do_read(pa_pstream *p) {
+    void *d;
+    size_t l;
+    ssize_t r;
+    pa_memblock *release_memblock = NULL;
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
+        d = (uint8_t*) p->read.descriptor + p->read.index;
+        l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index;
+    } else {
+        pa_assert(p->read.data || p->read.memblock);
+
+        if (p->read.data)
+            d = p->read.data;
+        else {
+            d = pa_memblock_acquire(p->read.memblock);
+            release_memblock = p->read.memblock;
+        }
+
+        d = (uint8_t*) d + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE;
+        l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE);
+    }
+
+#ifdef HAVE_CREDS
+    {
+        pa_bool_t b = 0;
+
+        if ((r = pa_iochannel_read_with_creds(p->io, d, l, &p->read_creds, &b)) <= 0)
+            goto fail;
+
+        p->read_creds_valid = p->read_creds_valid || b;
+    }
+#else
+    if ((r = pa_iochannel_read(p->io, d, l)) <= 0)
+        goto fail;
+#endif
+
+    if (release_memblock)
+        pa_memblock_release(release_memblock);
+
+    p->read.index += r;
+
+    if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) {
+        uint32_t flags, length, channel;
+        /* Reading of frame descriptor complete */
+
+        flags = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]);
+
+        if (!p->use_shm && (flags & PA_FLAG_SHMMASK) != 0) {
+            pa_log_warn("Recieved SHM frame on a socket where SHM is disabled.");
+            return -1;
+        }
+
+        if (flags == PA_FLAG_SHMRELEASE) {
+
+            /* This is a SHM memblock release frame with no payload */
+
+/*             pa_log("Got release frame for %u", ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])); */
+
+            pa_assert(p->export);
+            pa_memexport_process_release(p->export, ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI]));
+
+            goto frame_done;
+
+        } else if (flags == PA_FLAG_SHMREVOKE) {
+
+            /* This is a SHM memblock revoke frame with no payload */
+
+/*             pa_log("Got revoke frame for %u", ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])); */
+
+            pa_assert(p->import);
+            pa_memimport_process_revoke(p->import, ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI]));
+
+            goto frame_done;
+        }
+
+        length = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]);
+
+        if (length > FRAME_SIZE_MAX_ALLOW || length <= 0) {
+            pa_log_warn("Recieved invalid frame size: %lu", (unsigned long) length);
+            return -1;
+        }
+
+        pa_assert(!p->read.packet && !p->read.memblock);
+
+        channel = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]);
+
+        if (channel == (uint32_t) -1) {
+
+            if (flags != 0) {
+                pa_log_warn("Received packet frame with invalid flags value.");
+                return -1;
+            }
+
+            /* Frame is a packet frame */
+            p->read.packet = pa_packet_new(length);
+            p->read.data = p->read.packet->data;
+
+        } else {
+
+            if ((flags & PA_FLAG_SEEKMASK) > PA_SEEK_RELATIVE_END) {
+                pa_log_warn("Received memblock frame with invalid seek mode.");
+                return -1;
+            }
+
+            if ((flags & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA) {
+
+                if (length != sizeof(p->read.shm_info)) {
+                    pa_log_warn("Recieved SHM memblock frame with Invalid frame length.");
+                    return -1;
+                }
+
+                /* Frame is a memblock frame referencing an SHM memblock */
+                p->read.data = p->read.shm_info;
+
+            } else if ((flags & PA_FLAG_SHMMASK) == 0) {
+
+                /* Frame is a memblock frame */
+
+                p->read.memblock = pa_memblock_new(p->mempool, length);
+                p->read.data = NULL;
+            } else {
+
+                pa_log_warn("Recieved memblock frame with invalid flags value.");
+                return -1;
+            }
+        }
+
+    } else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) {
+        /* Frame payload available */
+
+        if (p->read.memblock && p->recieve_memblock_callback) {
+
+            /* Is this memblock data? Than pass it to the user */
+            l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r;
+
+            if (l > 0) {
+                pa_memchunk chunk;
+
+                chunk.memblock = p->read.memblock;
+                chunk.index = p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE - l;
+                chunk.length = l;
+
+                if (p->recieve_memblock_callback) {
+                    int64_t offset;
+
+                    offset = (int64_t) (
+                            (((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) |
+                            (((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO]))));
+
+                    p->recieve_memblock_callback(
+                        p,
+                        ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]),
+                        offset,
+                        ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SEEKMASK,
+                        &chunk,
+                        p->recieve_memblock_callback_userdata);
+                }
+
+                /* Drop seek info for following callbacks */
+                p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS] =
+                    p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] =
+                    p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = 0;
+            }
+        }
+
+        /* Frame complete */
+        if (p->read.index >= ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) {
+
+            if (p->read.memblock) {
+
+                /* This was a memblock frame. We can unref the memblock now */
+                pa_memblock_unref(p->read.memblock);
+
+            } else if (p->read.packet) {
+
+                if (p->recieve_packet_callback)
+#ifdef HAVE_CREDS
+                    p->recieve_packet_callback(p, p->read.packet, p->read_creds_valid ? &p->read_creds : NULL, p->recieve_packet_callback_userdata);
+#else
+                    p->recieve_packet_callback(p, p->read.packet, NULL, p->recieve_packet_callback_userdata);
+#endif
+
+                pa_packet_unref(p->read.packet);
+            } else {
+                pa_memblock *b;
+
+                pa_assert((ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA);
+
+                pa_assert(p->import);
+
+                if (!(b = pa_memimport_get(p->import,
+                                          ntohl(p->read.shm_info[PA_PSTREAM_SHM_BLOCKID]),
+                                          ntohl(p->read.shm_info[PA_PSTREAM_SHM_SHMID]),
+                                          ntohl(p->read.shm_info[PA_PSTREAM_SHM_INDEX]),
+                                          ntohl(p->read.shm_info[PA_PSTREAM_SHM_LENGTH])))) {
+
+                    pa_log_warn("Failed to import memory block.");
+                    return -1;
+                }
+
+                if (p->recieve_memblock_callback) {
+                    int64_t offset;
+                    pa_memchunk chunk;
+
+                    chunk.memblock = b;
+                    chunk.index = 0;
+                    chunk.length = pa_memblock_get_length(b);
+
+                    offset = (int64_t) (
+                            (((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) |
+                            (((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO]))));
+
+                    p->recieve_memblock_callback(
+                            p,
+                            ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]),
+                            offset,
+                            ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]) & PA_FLAG_SEEKMASK,
+                            &chunk,
+                            p->recieve_memblock_callback_userdata);
+                }
+
+                pa_memblock_unref(b);
+            }
+
+            goto frame_done;
+        }
+    }
+
+    return 0;
+
+frame_done:
+    p->read.memblock = NULL;
+    p->read.packet = NULL;
+    p->read.index = 0;
+    p->read.data = NULL;
+
+#ifdef HAVE_CREDS
+    p->read_creds_valid = FALSE;
+#endif
+
+    return 0;
+
+fail:
+    if (release_memblock)
+        pa_memblock_release(release_memblock);
+
+    return -1;
+}
+
+void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->die_callback = cb;
+    p->die_callback_userdata = userdata;
+}
+
+void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->drain_callback = cb;
+    p->drain_callback_userdata = userdata;
+}
+
+void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->recieve_packet_callback = cb;
+    p->recieve_packet_callback_userdata = userdata;
+}
+
+void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->recieve_memblock_callback = cb;
+    p->recieve_memblock_callback_userdata = userdata;
+}
+
+void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->release_callback = cb;
+    p->release_callback_userdata = userdata;
+}
+
+void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->release_callback = cb;
+    p->release_callback_userdata = userdata;
+}
+
+pa_bool_t pa_pstream_is_pending(pa_pstream *p) {
+    pa_bool_t b;
+
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (p->dead)
+        b = FALSE;
+    else
+        b = p->write.current || !pa_queue_is_empty(p->send_queue);
+
+    return b;
+}
+
+void pa_pstream_unref(pa_pstream*p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    if (PA_REFCNT_DEC(p) <= 0)
+        pstream_free(p);
+}
+
+pa_pstream* pa_pstream_ref(pa_pstream*p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    PA_REFCNT_INC(p);
+    return p;
+}
+
+void pa_pstream_unlink(pa_pstream *p) {
+    pa_assert(p);
+
+    if (p->dead)
+        return;
+
+    p->dead = TRUE;
+
+    if (p->import) {
+        pa_memimport_free(p->import);
+        p->import = NULL;
+    }
+
+    if (p->export) {
+        pa_memexport_free(p->export);
+        p->export = NULL;
+    }
+
+    if (p->io) {
+        pa_iochannel_free(p->io);
+        p->io = NULL;
+    }
+
+    if (p->defer_event) {
+        p->mainloop->defer_free(p->defer_event);
+        p->defer_event = NULL;
+    }
+
+    p->die_callback = NULL;
+    p->drain_callback = NULL;
+    p->recieve_packet_callback = NULL;
+    p->recieve_memblock_callback = NULL;
+}
+
+void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    p->use_shm = enable;
+
+    if (enable) {
+
+        if (!p->export)
+            p->export = pa_memexport_new(p->mempool, memexport_revoke_cb, p);
+
+    } else {
+
+        if (p->export) {
+            pa_memexport_free(p->export);
+            p->export = NULL;
+        }
+    }
+}
+
+pa_bool_t pa_pstream_get_shm(pa_pstream *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+    return p->use_shm;
+}
diff --git a/src/pulsecore/pstream.h b/src/pulsecore/pstream.h
new file mode 100644 (file)
index 0000000..a528b25
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef foopstreamhfoo
+#define foopstreamhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/def.h>
+
+#include <pulsecore/packet.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_pstream pa_pstream;
+
+typedef void (*pa_pstream_packet_cb_t)(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata);
+typedef void (*pa_pstream_memblock_cb_t)(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata);
+typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata);
+typedef void (*pa_pstream_block_id_cb_t)(pa_pstream *p, uint32_t block_id, void *userdata);
+
+pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *p);
+
+pa_pstream* pa_pstream_ref(pa_pstream*p);
+void pa_pstream_unref(pa_pstream*p);
+
+void pa_pstream_unlink(pa_pstream *p);
+
+void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *creds);
+void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk);
+void pa_pstream_send_release(pa_pstream *p, uint32_t block_id);
+void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id);
+
+void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata);
+void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata);
+void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
+void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
+void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);
+void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);
+
+pa_bool_t pa_pstream_is_pending(pa_pstream *p);
+
+void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable);
+pa_bool_t pa_pstream_get_shm(pa_pstream *p);
+
+#endif
similarity index 50%
rename from polyp/queue.c
rename to src/pulsecore/queue.c
index e0a06ae112aff82b2f5dcd57a721bf7d3ab6fb81..08fd14263f611642d32dbf1fe5bb9dddb522aec6 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <config.h>
 #endif
 
-#include <assert.h>
 #include <stdlib.h>
 
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+
 #include "queue.h"
-#include "xmalloc.h"
+
+PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
 
 struct queue_entry {
     struct queue_entry *next;
@@ -39,42 +43,47 @@ struct pa_queue {
     unsigned length;
 };
 
-struct pa_queue* pa_queue_new(void) {
-    struct pa_queue *q = pa_xmalloc(sizeof(struct pa_queue));
+pa_queue* pa_queue_new(void) {
+    pa_queue *q = pa_xnew(pa_queue, 1);
+
     q->front = q->back = NULL;
     q->length = 0;
+
     return q;
 }
 
-void pa_queue_free(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;
+void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) {
+    void *data;
+    pa_assert(q);
 
+    while ((data = pa_queue_pop(q)))
         if (destroy)
-            destroy(e->data, userdata);
+            destroy(data, userdata);
 
-        pa_xfree(e);
-        e = n;
-    }
+    pa_assert(!q->front);
+    pa_assert(!q->back);
+    pa_assert(q->length == 0);
 
     pa_xfree(q);
 }
 
-void pa_queue_push(struct pa_queue *q, void *p) {
+void pa_queue_push(pa_queue *q, void *p) {
     struct queue_entry *e;
 
-    e = pa_xmalloc(sizeof(struct queue_entry));
+    pa_assert(q);
+    pa_assert(p);
+
+    if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
+        e = pa_xnew(struct queue_entry, 1);
+
     e->data = p;
     e->next = NULL;
 
-    if (q->back)
+    if (q->back) {
+        pa_assert(q->front);
         q->back->next = e;
-    else {
-        assert(!q->front);
+    else {
+        pa_assert(!q->front);
         q->front = e;
     }
 
@@ -82,27 +91,33 @@ void pa_queue_push(struct pa_queue *q, void *p) {
     q->length++;
 }
 
-void* pa_queue_pop(struct pa_queue *q) {
+void* pa_queue_pop(pa_queue *q) {
     void *p;
     struct queue_entry *e;
-    assert(q);
+    pa_assert(q);
 
     if (!(e = q->front))
         return NULL;
 
     q->front = e->next;
-    if (q->back == e)
+
+    if (q->back == e) {
+        pa_assert(!e->next);
         q->back = NULL;
+    }
 
     p = e->data;
-    pa_xfree(e);
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
+        pa_xfree(e);
 
     q->length--;
-    
+
     return p;
 }
 
-int pa_queue_is_empty(struct pa_queue *q) {
-    assert(q);
+int pa_queue_is_empty(pa_queue *q) {
+    pa_assert(q);
+
     return q->length == 0;
 }
similarity index 61%
rename from polyp/queue.h
rename to src/pulsecore/queue.h
index a739ab74c82cf3f46aeac890bef5dfbf59148640..b09216cf58f381c9daf6dc21ea49b13dc5206546 100644 (file)
@@ -1,40 +1,40 @@
 #ifndef fooqueuehfoo
 #define fooqueuehfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-struct pa_queue;
+typedef struct pa_queue pa_queue;
 
 /* A simple implementation of the abstract data type queue. Stores
  * pointers as members. The memory has to be managed by the caller. */
 
-struct pa_queue* pa_queue_new(void);
+pa_queue* pa_queue_new(void);
 
 /* Free the queue and run the specified callback function for every remaining entry. The callback function may be NULL. */
-void pa_queue_free(struct pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata);
+void pa_queue_free(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);
+void pa_queue_push(pa_queue *q, void *p);
+void* pa_queue_pop(pa_queue *q);
 
-int pa_queue_is_empty(struct pa_queue *q);
+int pa_queue_is_empty(pa_queue *q);
 
 #endif
diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c
new file mode 100644 (file)
index 0000000..5deac37
--- /dev/null
@@ -0,0 +1,116 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "random.h"
+
+static int has_whined = 0;
+
+static const char * const devices[] = { "/dev/urandom", "/dev/random", NULL };
+
+static int random_proper(void *ret_data, size_t length) {
+#ifdef OS_IS_WIN32
+    pa_assert(ret_data);
+    pa_assert(length > 0);
+
+    return -1;
+
+#else /* OS_IS_WIN32 */
+
+    int fd, ret = -1;
+    ssize_t r = 0;
+    const char *const * device;
+
+    pa_assert(ret_data);
+    pa_assert(length > 0);
+
+    device = devices;
+
+    while (*device) {
+        ret = 0;
+
+        if ((fd = open(*device, O_RDONLY
+#ifdef O_NOCTTY
+                       | O_NOCTTY
+#endif
+             )) >= 0) {
+
+            if ((r = pa_loop_read(fd, ret_data, length, NULL)) < 0 || (size_t) r != length)
+                ret = -1;
+
+            pa_close(fd);
+        } else
+            ret = -1;
+
+        if (ret == 0)
+            break;
+    }
+
+    return ret;
+#endif /* OS_IS_WIN32 */
+}
+
+void pa_random_seed(void) {
+    unsigned int seed;
+
+    if (random_proper(&seed, sizeof(unsigned int)) < 0) {
+        if (!has_whined)
+            pa_log_warn("Failed to get proper entropy. Falling back to seeding with current time.");
+        has_whined = 1;
+
+        seed = (unsigned int) time(NULL);
+    }
+
+    srand(seed);
+}
+
+void pa_random(void *ret_data, size_t length) {
+    uint8_t *p;
+    size_t l;
+
+    pa_assert(ret_data);
+    pa_assert(length > 0);
+
+    if (random_proper(ret_data, length) >= 0)
+        return;
+
+    if (!has_whined)
+        pa_log_warn("Failed to get proper entropy. Falling back to unsecure pseudo RNG.");
+    has_whined = 1;
+
+    for (p = ret_data, l = length; l > 0; p++, l--)
+        *p = (uint8_t) rand();
+}
similarity index 62%
rename from polyp/random.h
rename to src/pulsecore/random.h
index bfb3df084e9de90ae99fe2fbd63b01726c77d799..36d7f9df90009ed064e1f20f7387e7db1aca7597 100644 (file)
@@ -1,27 +1,31 @@
 #ifndef foorandomhfoo
 #define foorandomhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; 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_random_seed(void);
 void pa_random(void *ret_data, size_t length);
-    
+
 #endif
diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h
new file mode 100644 (file)
index 0000000..291f450
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef foopulserefcnthfoo
+#define foopulserefcnthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/atomic.h>
+
+#define PA_REFCNT_DECLARE \
+    pa_atomic_t _ref
+
+#define PA_REFCNT_INIT(p) \
+    pa_atomic_store(&(p)->_ref, 1)
+
+#define PA_REFCNT_INIT_ZERO(p) \
+    pa_atomic_store(&(p)->_ref, 0)
+
+#define PA_REFCNT_INC(p) \
+    pa_atomic_inc(&(p)->_ref)
+
+#define PA_REFCNT_DEC(p) \
+    (pa_atomic_dec(&(p)->_ref)-1)
+
+#define PA_REFCNT_VALUE(p) \
+    pa_atomic_load(&(p)->_ref)
+
+#endif
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
new file mode 100644 (file)
index 0000000..00dc794
--- /dev/null
@@ -0,0 +1,1647 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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>
+
+#if HAVE_LIBSAMPLERATE
+#include <samplerate.h>
+#endif
+
+#include <liboil/liboilfuncs.h>
+#include <liboil/liboil.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/sconv.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
+
+#include "speexwrap.h"
+
+#include "ffmpeg/avcodec.h"
+
+#include "resampler.h"
+
+/* Number of samples of extra space we allow the resamplers to return */
+#define EXTRA_FRAMES 128
+
+struct pa_resampler {
+    pa_resample_method_t method;
+    pa_resample_flags_t flags;
+
+    pa_sample_spec i_ss, o_ss;
+    pa_channel_map i_cm, o_cm;
+    size_t i_fz, o_fz, w_sz;
+    pa_mempool *mempool;
+
+    pa_memchunk buf1, buf2, buf3, buf4;
+    unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples;
+
+    pa_sample_format_t work_format;
+
+    pa_convert_func_t to_work_format_func;
+    pa_convert_func_t from_work_format_func;
+
+    float map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
+    pa_bool_t map_required;
+
+    void (*impl_free)(pa_resampler *r);
+    void (*impl_update_rates)(pa_resampler *r);
+    void (*impl_resample)(pa_resampler *r, const pa_memchunk *in, unsigned in_samples, pa_memchunk *out, unsigned *out_samples);
+    void (*impl_reset)(pa_resampler *r);
+
+    struct { /* data specific to the trivial resampler */
+        unsigned o_counter;
+        unsigned i_counter;
+    } trivial;
+
+    struct { /* data specific to the peak finder pseudo resampler */
+        unsigned o_counter;
+        unsigned i_counter;
+
+        float max_f[PA_CHANNELS_MAX];
+        int16_t max_i[PA_CHANNELS_MAX];
+
+    } peaks;
+
+#ifdef HAVE_LIBSAMPLERATE
+    struct { /* data specific to libsamplerate */
+        SRC_STATE *state;
+    } src;
+#endif
+
+    struct { /* data specific to speex */
+        SpeexResamplerState* state;
+    } speex;
+
+    struct { /* data specific to ffmpeg */
+        struct AVResampleContext *state;
+        pa_memchunk buf[PA_CHANNELS_MAX];
+    } ffmpeg;
+};
+
+static int copy_init(pa_resampler *r);
+static int trivial_init(pa_resampler*r);
+static int speex_init(pa_resampler*r);
+static int ffmpeg_init(pa_resampler*r);
+static int peaks_init(pa_resampler*r);
+#ifdef HAVE_LIBSAMPLERATE
+static int libsamplerate_init(pa_resampler*r);
+#endif
+
+static void calc_map_table(pa_resampler *r);
+
+static int (* const init_table[])(pa_resampler*r) = {
+#ifdef HAVE_LIBSAMPLERATE
+    [PA_RESAMPLER_SRC_SINC_BEST_QUALITY]   = libsamplerate_init,
+    [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = libsamplerate_init,
+    [PA_RESAMPLER_SRC_SINC_FASTEST]        = libsamplerate_init,
+    [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD]     = libsamplerate_init,
+    [PA_RESAMPLER_SRC_LINEAR]              = libsamplerate_init,
+#else
+    [PA_RESAMPLER_SRC_SINC_BEST_QUALITY]   = NULL,
+    [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = NULL,
+    [PA_RESAMPLER_SRC_SINC_FASTEST]        = NULL,
+    [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD]     = NULL,
+    [PA_RESAMPLER_SRC_LINEAR]              = NULL,
+#endif
+    [PA_RESAMPLER_TRIVIAL]                 = trivial_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+0]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+1]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+2]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+3]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+4]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+5]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+6]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+7]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+8]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+9]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+10]     = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+0]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+1]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+2]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+3]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+4]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+5]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+6]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+7]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+8]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+9]      = speex_init,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+10]     = speex_init,
+    [PA_RESAMPLER_FFMPEG]                  = ffmpeg_init,
+    [PA_RESAMPLER_AUTO]                    = NULL,
+    [PA_RESAMPLER_COPY]                    = copy_init,
+    [PA_RESAMPLER_PEAKS]                   = peaks_init,
+};
+
+static inline size_t sample_size(pa_sample_format_t f) {
+    pa_sample_spec ss = {
+        .format = f,
+        .rate = 0,
+        .channels = 1
+    };
+
+    return pa_sample_size(&ss);
+}
+
+pa_resampler* pa_resampler_new(
+        pa_mempool *pool,
+        const pa_sample_spec *a,
+        const pa_channel_map *am,
+        const pa_sample_spec *b,
+        const pa_channel_map *bm,
+        pa_resample_method_t method,
+        pa_resample_flags_t flags) {
+
+    pa_resampler *r = NULL;
+
+    pa_assert(pool);
+    pa_assert(a);
+    pa_assert(b);
+    pa_assert(pa_sample_spec_valid(a));
+    pa_assert(pa_sample_spec_valid(b));
+    pa_assert(method >= 0);
+    pa_assert(method < PA_RESAMPLER_MAX);
+
+    /* Fix method */
+
+    if (!(flags & PA_RESAMPLER_VARIABLE_RATE) && a->rate == b->rate) {
+        pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates.");
+        method = PA_RESAMPLER_COPY;
+    }
+
+    if (!pa_resample_method_supported(method)) {
+        pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(method));
+        method = PA_RESAMPLER_AUTO;
+    }
+
+    if (method == PA_RESAMPLER_FFMPEG && (flags & PA_RESAMPLER_VARIABLE_RATE)) {
+        pa_log_info("Resampler 'ffmpeg' cannot do variable rate, reverting to resampler 'auto'.");
+        method = PA_RESAMPLER_AUTO;
+    }
+
+    if (method == PA_RESAMPLER_COPY && ((flags & PA_RESAMPLER_VARIABLE_RATE) || a->rate != b->rate)) {
+        pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'.");
+        method = PA_RESAMPLER_AUTO;
+    }
+
+    if (method == PA_RESAMPLER_AUTO)
+        method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
+
+    r = pa_xnew(pa_resampler, 1);
+    r->mempool = pool;
+    r->method = method;
+    r->flags = flags;
+
+    r->impl_free = NULL;
+    r->impl_update_rates = NULL;
+    r->impl_resample = NULL;
+    r->impl_reset = NULL;
+
+    /* Fill sample specs */
+    r->i_ss = *a;
+    r->o_ss = *b;
+
+    if (am)
+        r->i_cm = *am;
+    else if (!pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT))
+        goto fail;
+
+    if (bm)
+        r->o_cm = *bm;
+    else if (!pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels, PA_CHANNEL_MAP_DEFAULT))
+        goto fail;
+
+    r->i_fz = pa_frame_size(a);
+    r->o_fz = pa_frame_size(b);
+
+    pa_memchunk_reset(&r->buf1);
+    pa_memchunk_reset(&r->buf2);
+    pa_memchunk_reset(&r->buf3);
+    pa_memchunk_reset(&r->buf4);
+
+    r->buf1_samples = r->buf2_samples = r->buf3_samples = r->buf4_samples = 0;
+
+    calc_map_table(r);
+
+    pa_log_info("Using resampler '%s'", pa_resample_method_to_string(method));
+
+    if ((method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX) ||
+        (method == PA_RESAMPLER_FFMPEG))
+        r->work_format = PA_SAMPLE_S16NE;
+    else if (method == PA_RESAMPLER_TRIVIAL || method == PA_RESAMPLER_COPY || method == PA_RESAMPLER_PEAKS) {
+
+        if (r->map_required || a->format != b->format || method == PA_RESAMPLER_PEAKS) {
+
+            if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE ||
+                a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
+                b->format == PA_SAMPLE_S32NE || b->format == PA_SAMPLE_S32RE ||
+                b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE)
+                r->work_format = PA_SAMPLE_FLOAT32NE;
+            else
+                r->work_format = PA_SAMPLE_S16NE;
+
+        } else
+            r->work_format = a->format;
+
+    } else
+        r->work_format = PA_SAMPLE_FLOAT32NE;
+
+    pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format));
+
+    r->w_sz = sample_size(r->work_format);
+
+    if (r->i_ss.format == r->work_format)
+        r->to_work_format_func = NULL;
+    else if (r->work_format == PA_SAMPLE_FLOAT32NE) {
+        if (!(r->to_work_format_func = pa_get_convert_to_float32ne_function(r->i_ss.format)))
+            goto fail;
+    } else {
+        pa_assert(r->work_format == PA_SAMPLE_S16NE);
+        if (!(r->to_work_format_func = pa_get_convert_to_s16ne_function(r->i_ss.format)))
+            goto fail;
+    }
+
+    if (r->o_ss.format == r->work_format)
+        r->from_work_format_func = NULL;
+    else if (r->work_format == PA_SAMPLE_FLOAT32NE) {
+        if (!(r->from_work_format_func = pa_get_convert_from_float32ne_function(r->o_ss.format)))
+            goto fail;
+    } else {
+        pa_assert(r->work_format == PA_SAMPLE_S16NE);
+        if (!(r->from_work_format_func = pa_get_convert_from_s16ne_function(r->o_ss.format)))
+            goto fail;
+    }
+
+    /* initialize implementation */
+    if (init_table[method](r) < 0)
+        goto fail;
+
+    return r;
+
+fail:
+    if (r)
+        pa_xfree(r);
+
+    return NULL;
+}
+
+void pa_resampler_free(pa_resampler *r) {
+    pa_assert(r);
+
+    if (r->impl_free)
+        r->impl_free(r);
+
+    if (r->buf1.memblock)
+        pa_memblock_unref(r->buf1.memblock);
+    if (r->buf2.memblock)
+        pa_memblock_unref(r->buf2.memblock);
+    if (r->buf3.memblock)
+        pa_memblock_unref(r->buf3.memblock);
+    if (r->buf4.memblock)
+        pa_memblock_unref(r->buf4.memblock);
+
+    pa_xfree(r);
+}
+
+void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) {
+    pa_assert(r);
+    pa_assert(rate > 0);
+
+    if (r->i_ss.rate == rate)
+        return;
+
+    r->i_ss.rate = rate;
+
+    r->impl_update_rates(r);
+}
+
+void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) {
+    pa_assert(r);
+    pa_assert(rate > 0);
+
+    if (r->o_ss.rate == rate)
+        return;
+
+    r->o_ss.rate = rate;
+
+    r->impl_update_rates(r);
+}
+
+size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
+    pa_assert(r);
+
+    return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
+}
+
+size_t pa_resampler_result(pa_resampler *r, size_t in_length) {
+    pa_assert(r);
+
+    return (((in_length / r->i_fz)*r->o_ss.rate)/r->i_ss.rate) * r->o_fz;
+}
+
+size_t pa_resampler_max_block_size(pa_resampler *r) {
+    size_t block_size_max;
+    pa_sample_spec ss;
+    size_t fs;
+
+    pa_assert(r);
+
+    block_size_max = pa_mempool_block_size_max(r->mempool);
+
+    /* We deduce the "largest" sample spec we're using during the
+     * conversion */
+    ss.channels = PA_MAX(r->i_ss.channels, r->o_ss.channels);
+
+    /* We silently assume that the format enum is ordered by size */
+    ss.format = PA_MAX(r->i_ss.format, r->o_ss.format);
+    ss.format = PA_MAX(ss.format, r->work_format);
+
+    ss.rate = PA_MAX(r->i_ss.rate, r->o_ss.rate);
+
+    fs = pa_frame_size(&ss);
+
+    return (((block_size_max/fs - EXTRA_FRAMES)*r->i_ss.rate)/ss.rate)*r->i_fz;
+}
+
+void pa_resampler_reset(pa_resampler *r) {
+    pa_assert(r);
+
+    if (r->impl_reset)
+        r->impl_reset(r);
+}
+
+pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
+    pa_assert(r);
+
+    return r->method;
+}
+
+static const char * const resample_methods[] = {
+    "src-sinc-best-quality",
+    "src-sinc-medium-quality",
+    "src-sinc-fastest",
+    "src-zero-order-hold",
+    "src-linear",
+    "trivial",
+    "speex-float-0",
+    "speex-float-1",
+    "speex-float-2",
+    "speex-float-3",
+    "speex-float-4",
+    "speex-float-5",
+    "speex-float-6",
+    "speex-float-7",
+    "speex-float-8",
+    "speex-float-9",
+    "speex-float-10",
+    "speex-fixed-0",
+    "speex-fixed-1",
+    "speex-fixed-2",
+    "speex-fixed-3",
+    "speex-fixed-4",
+    "speex-fixed-5",
+    "speex-fixed-6",
+    "speex-fixed-7",
+    "speex-fixed-8",
+    "speex-fixed-9",
+    "speex-fixed-10",
+    "ffmpeg",
+    "auto",
+    "copy",
+    "peaks"
+};
+
+const char *pa_resample_method_to_string(pa_resample_method_t m) {
+
+    if (m < 0 || m >= PA_RESAMPLER_MAX)
+        return NULL;
+
+    return resample_methods[m];
+}
+
+int pa_resample_method_supported(pa_resample_method_t m) {
+
+    if (m < 0 || m >= PA_RESAMPLER_MAX)
+        return 0;
+
+#ifndef HAVE_LIBSAMPLERATE
+    if (m <= PA_RESAMPLER_SRC_LINEAR)
+        return 0;
+#endif
+
+    return 1;
+}
+
+pa_resample_method_t pa_parse_resample_method(const char *string) {
+    pa_resample_method_t m;
+
+    pa_assert(string);
+
+    for (m = 0; m < PA_RESAMPLER_MAX; m++)
+        if (!strcmp(string, resample_methods[m]))
+            return m;
+
+    if (!strcmp(string, "speex-fixed"))
+        return PA_RESAMPLER_SPEEX_FIXED_BASE + 3;
+
+    if (!strcmp(string, "speex-float"))
+        return PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
+
+    return PA_RESAMPLER_INVALID;
+}
+
+static pa_bool_t on_left(pa_channel_position_t p) {
+
+    return
+        p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_REAR_LEFT ||
+        p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+}
+
+static pa_bool_t on_right(pa_channel_position_t p) {
+
+    return
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_REAR_RIGHT ||
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+}
+
+static pa_bool_t on_center(pa_channel_position_t p) {
+
+    return
+        p == PA_CHANNEL_POSITION_FRONT_CENTER ||
+        p == PA_CHANNEL_POSITION_REAR_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+}
+
+static pa_bool_t on_lfe(pa_channel_position_t p) {
+    return
+        p == PA_CHANNEL_POSITION_LFE;
+}
+
+static void calc_map_table(pa_resampler *r) {
+    unsigned oc, ic;
+    pa_bool_t ic_connected[PA_CHANNELS_MAX];
+    pa_bool_t remix;
+    pa_strbuf *s;
+    char *t;
+
+    pa_assert(r);
+
+    if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) && !pa_channel_map_equal(&r->i_cm, &r->o_cm)))))
+        return;
+
+    memset(r->map_table, 0, sizeof(r->map_table));
+    memset(ic_connected, 0, sizeof(ic_connected));
+    remix = (r->flags & (PA_RESAMPLER_NO_REMAP|PA_RESAMPLER_NO_REMIX)) == 0;
+
+    for (oc = 0; oc < r->o_ss.channels; oc++) {
+        pa_bool_t oc_connected = FALSE;
+        pa_channel_position_t b = r->o_cm.map[oc];
+
+        for (ic = 0; ic < r->i_ss.channels; ic++) {
+            pa_channel_position_t a = r->i_cm.map[ic];
+
+            if (r->flags & PA_RESAMPLER_NO_REMAP) {
+                /* We shall not do any remapping. Hence, just check by index */
+
+                if (ic == oc)
+                    r->map_table[oc][ic] = 1.0;
+
+                continue;
+            }
+
+            if (r->flags & PA_RESAMPLER_NO_REMIX) {
+                /* We shall not do any remixing. Hence, just check by name */
+
+                if (a == b)
+                    r->map_table[oc][ic] = 1.0;
+
+                continue;
+            }
+
+            pa_assert(remix);
+
+            /* OK, we shall do the full monty: upmixing and
+             * downmixing. Our algorithm is relatively simple, does
+             * not do spacialization, delay elements or apply lowpass
+             * filters for LFE. Patches are always welcome,
+             * though. Oh, and it doesn't do any matrix
+             * decoding. (Which probably wouldn't make any sense
+             * anyway.)
+             *
+             * This code is not idempotent: downmixing an upmixed
+             * stereo stream is not identical to the original. The
+             * volume will not match, and the two channels will be a
+             * linear combination of both.
+             *
+             * This is losely based on random suggestions found on the
+             * Internet, such as this:
+             * http://www.halfgaar.net/surround-sound-in-linux and the
+             * alsa upmix plugin.
+             *
+             * The algorithm works basically like this:
+             *
+             * 1) Connect all channels with matching names.
+             *
+             * 2) Mono Handling:
+             *    S:Mono: Copy into all D:channels
+             *    D:Mono: Copy in all S:channels
+             *
+             * 3) Mix D:Left, D:Right:
+             *    D:Left: If not connected, avg all S:Left
+             *    D:Right: If not connected, avg all S:Right
+             *
+             * 4) Mix D:Center
+             *       If not connected, avg all S:Center
+             *       If still not connected, avg all S:Left, S:Right
+             *
+             * 5) Mix D:LFE
+             *       If not connected, avg all S:*
+             *
+             * 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If
+             *    not connected, mix into all D:left and all D:right
+             *    channels. Gain is 0.1, the current left and right
+             *    should be multiplied by 0.9.
+             *
+             * 7) Make sure S:Center, S:LFE is used:
+             *
+             *    S:Center, S:LFE: If not connected, mix into all
+             *    D:left, all D:right, all D:center channels, gain is
+             *    0.375. The current (as result of 1..6) factors
+             *    should be multiplied by 0.75. (Alt. suggestion: 0.25
+             *    vs. 0.5)
+             *
+             * S: and D: shall relate to the source resp. destination channels.
+             *
+             * Rationale: 1, 2 are probably obvious. For 3: this
+             * copies front to rear if needed. For 4: we try to find
+             * some suitable C source for C, if we don't find any, we
+             * avg L and R. For 5: LFE is mixed from all channels. For
+             * 6: the rear channels should not be dropped entirely,
+             * however have only minimal impact. For 7: movies usually
+             * encode speech on the center channel. Thus we have to
+             * make sure this channel is distributed to L and R if not
+             * available in the output. Also, LFE is used to achieve a
+             * greater dynamic range, and thus we should try to do our
+             * best to pass it to L+R.
+             */
+
+            if (a == b || a == PA_CHANNEL_POSITION_MONO || b == PA_CHANNEL_POSITION_MONO) {
+                r->map_table[oc][ic] = 1.0;
+
+                oc_connected = TRUE;
+                ic_connected[ic] = TRUE;
+            }
+        }
+
+        if (!oc_connected && remix) {
+            /* OK, we shall remix */
+
+            if (on_left(b)) {
+                unsigned n = 0;
+
+                /* We are not connected and on the left side, let's
+                 * average all left side input channels. */
+
+                for (ic = 0; ic < r->i_ss.channels; ic++)
+                    if (on_left(r->i_cm.map[ic]))
+                        n++;
+
+                if (n > 0)
+                    for (ic = 0; ic < r->i_ss.channels; ic++)
+                        if (on_left(r->i_cm.map[ic])) {
+                            r->map_table[oc][ic] = 1.0 / n;
+                            ic_connected[ic] = TRUE;
+                        }
+
+                /* We ignore the case where there is no left input
+                 * channel. Something is really wrong in this case
+                 * anyway. */
+
+            } else if (on_right(b)) {
+                unsigned n = 0;
+
+                /* We are not connected and on the right side, let's
+                 * average all right side input channels. */
+
+                for (ic = 0; ic < r->i_ss.channels; ic++)
+                    if (on_right(r->i_cm.map[ic]))
+                        n++;
+
+                if (n > 0)
+                    for (ic = 0; ic < r->i_ss.channels; ic++)
+                        if (on_right(r->i_cm.map[ic])) {
+                            r->map_table[oc][ic] = 1.0 / n;
+                            ic_connected[ic] = TRUE;
+                        }
+
+                /* We ignore the case where there is no right input
+                 * channel. Something is really wrong in this case
+                 * anyway. */
+
+            } else if (on_center(b)) {
+                unsigned n = 0;
+
+                /* We are not connected and at the center. Let's
+                 * average all center input channels. */
+
+                for (ic = 0; ic < r->i_ss.channels; ic++)
+                    if (on_center(r->i_cm.map[ic]))
+                        n++;
+
+                if (n > 0) {
+                    for (ic = 0; ic < r->i_ss.channels; ic++)
+                        if (on_center(r->i_cm.map[ic])) {
+                            r->map_table[oc][ic] = 1.0 / n;
+                            ic_connected[ic] = TRUE;
+                        }
+                } else {
+
+                    /* Hmm, no center channel around, let's synthesize
+                     * it by mixing L and R.*/
+
+                    n = 0;
+
+                    for (ic = 0; ic < r->i_ss.channels; ic++)
+                        if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic]))
+                            n++;
+
+                    if (n > 0)
+                        for (ic = 0; ic < r->i_ss.channels; ic++)
+                            if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) {
+                                r->map_table[oc][ic] = 1.0 / n;
+                                ic_connected[ic] = TRUE;
+                            }
+
+                    /* We ignore the case where there is not even a
+                     * left or right input channel. Something is
+                     * really wrong in this case anyway. */
+                }
+
+            } else if (on_lfe(b)) {
+
+                /* We are not connected and an LFE. Let's average all
+                 * channels for LFE. */
+
+                for (ic = 0; ic < r->i_ss.channels; ic++) {
+                    r->map_table[oc][ic] = 1.0 / r->i_ss.channels;
+
+                    /* Please note that a channel connected to LFE
+                     * doesn't really count as connected. */
+                }
+            }
+        }
+    }
+
+    if (remix) {
+        unsigned
+            ic_unconnected_left = 0,
+            ic_unconnected_right = 0,
+            ic_unconnected_center = 0,
+            ic_unconnected_lfe = 0;
+
+        for (ic = 0; ic < r->i_ss.channels; ic++) {
+            pa_channel_position_t a = r->i_cm.map[ic];
+
+            if (ic_connected[ic])
+                continue;
+
+            if (on_left(a))
+                ic_unconnected_left++;
+            else if (on_right(a))
+                ic_unconnected_right++;
+            else if (on_center(a))
+                ic_unconnected_center++;
+            else if (on_lfe(a))
+                ic_unconnected_lfe++;
+        }
+
+        if (ic_unconnected_left > 0) {
+
+            /* OK, so there are unconnected input channels on the
+             * left. Let's multiply all already connected channels on
+             * the left side by .9 and add in our averaged unconnected
+             * channels multplied by .1 */
+
+            for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+                if (!on_left(r->o_cm.map[oc]))
+                    continue;
+
+                for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+                    if (ic_connected[ic]) {
+                        r->map_table[oc][ic] *= .9;
+                        continue;
+                    }
+
+                    if (on_left(r->i_cm.map[ic]))
+                        r->map_table[oc][ic] = .1 / ic_unconnected_left;
+                }
+            }
+        }
+
+        if (ic_unconnected_right > 0) {
+
+            /* OK, so there are unconnected input channels on the
+             * right. Let's multiply all already connected channels on
+             * the right side by .9 and add in our averaged unconnected
+             * channels multplied by .1 */
+
+            for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+                if (!on_right(r->o_cm.map[oc]))
+                    continue;
+
+                for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+                    if (ic_connected[ic]) {
+                        r->map_table[oc][ic] *= .9;
+                        continue;
+                    }
+
+                    if (on_right(r->i_cm.map[ic]))
+                        r->map_table[oc][ic] = .1 / ic_unconnected_right;
+                }
+            }
+        }
+
+        if (ic_unconnected_center > 0) {
+            pa_bool_t mixed_in = FALSE;
+
+            /* OK, so there are unconnected input channels on the
+             * center. Let's multiply all already connected channels on
+             * the center side by .9 and add in our averaged unconnected
+             * channels multplied by .1 */
+
+            for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+                if (!on_center(r->o_cm.map[oc]))
+                    continue;
+
+                for (ic = 0; ic < r->i_ss.channels; ic++)  {
+
+                    if (ic_connected[ic]) {
+                        r->map_table[oc][ic] *= .9;
+                        continue;
+                    }
+
+                    if (on_center(r->i_cm.map[ic])) {
+                        r->map_table[oc][ic] = .1 / ic_unconnected_center;
+                        mixed_in = TRUE;
+                    }
+                }
+            }
+
+            if (!mixed_in) {
+
+                /* Hmm, as it appears there was no center channel we
+                   could mix our center channel in. In this case, mix
+                   it into left and right. Using .375 and 0.75 as
+                   factors. */
+
+                for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+                    if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
+                        continue;
+
+                    for (ic = 0; ic < r->i_ss.channels; ic++)  {
+
+                        if (ic_connected[ic]) {
+                            r->map_table[oc][ic] *= .75;
+                            continue;
+                        }
+
+                        if (on_center(r->i_cm.map[ic]))
+                            r->map_table[oc][ic] = .375 / ic_unconnected_center;
+                    }
+                }
+            }
+        }
+
+        if (ic_unconnected_lfe > 0) {
+
+            /* OK, so there is an unconnected LFE channel. Let's mix
+             * it into all channels, with factor 0.375 */
+
+            for (ic = 0; ic < r->i_ss.channels; ic++)  {
+
+                if (!on_lfe(r->i_cm.map[ic]))
+                    continue;
+
+                for (oc = 0; oc < r->o_ss.channels; oc++)
+                    r->map_table[oc][ic] = 0.375 / ic_unconnected_lfe;
+            }
+        }
+    }
+
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "     ");
+    for (ic = 0; ic < r->i_ss.channels; ic++)
+        pa_strbuf_printf(s, "  I%02u ", ic);
+    pa_strbuf_puts(s, "\n    +");
+
+    for (ic = 0; ic < r->i_ss.channels; ic++)
+        pa_strbuf_printf(s, "------");
+    pa_strbuf_puts(s, "\n");
+
+    for (oc = 0; oc < r->o_ss.channels; oc++) {
+        pa_strbuf_printf(s, "O%02u |", oc);
+
+        for (ic = 0; ic < r->i_ss.channels; ic++)
+            pa_strbuf_printf(s, " %1.3f", r->map_table[oc][ic]);
+
+        pa_strbuf_puts(s, "\n");
+    }
+
+    pa_log_debug("Channel matrix:\n%s", t = pa_strbuf_tostring_free(s));
+    pa_xfree(t);
+}
+
+static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
+    unsigned n_samples;
+    void *src, *dst;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(input->memblock);
+
+    /* Convert the incoming sample into the work sample format and place them in buf1 */
+
+    if (!r->to_work_format_func || !input->length)
+        return input;
+
+    n_samples = (input->length / r->i_fz) * r->i_ss.channels;
+
+    r->buf1.index = 0;
+    r->buf1.length = r->w_sz * n_samples;
+
+    if (!r->buf1.memblock || r->buf1_samples < n_samples) {
+        if (r->buf1.memblock)
+            pa_memblock_unref(r->buf1.memblock);
+
+        r->buf1_samples = n_samples;
+        r->buf1.memblock = pa_memblock_new(r->mempool, r->buf1.length);
+    }
+
+    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
+    dst = (uint8_t*) pa_memblock_acquire(r->buf1.memblock);
+
+    r->to_work_format_func(n_samples, src, dst);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(r->buf1.memblock);
+
+    return &r->buf1;
+}
+
+static void vectoradd_s16_with_fraction(
+        int16_t *d, int dstr,
+        const int16_t *s1, int sstr1,
+        const int16_t *s2, int sstr2,
+        int n,
+        float s3, float s4) {
+
+    int32_t i3, i4;
+
+    i3 = (int32_t) (s3 * 0x10000);
+    i4 = (int32_t) (s4 * 0x10000);
+
+    for (; n > 0; n--) {
+        int32_t a, b;
+
+        a = *s1;
+        b = *s2;
+
+        a = (a * i3) / 0x10000;
+        b = (b * i4) / 0x10000;
+
+        *d = (int16_t) (a + b);
+
+        s1 = (const int16_t*) ((const uint8_t*) s1 + sstr1);
+        s2 = (const int16_t*) ((const uint8_t*) s2 + sstr2);
+        d = (int16_t*) ((uint8_t*) d + dstr);
+
+    }
+}
+
+static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
+    unsigned in_n_samples, out_n_samples, n_frames;
+    int i_skip, o_skip;
+    unsigned oc;
+    void *src, *dst;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(input->memblock);
+
+    /* Remap channels and place the result int buf2 */
+
+    if (!r->map_required || !input->length)
+        return input;
+
+    in_n_samples = input->length / r->w_sz;
+    n_frames = in_n_samples / r->i_ss.channels;
+    out_n_samples = n_frames * r->o_ss.channels;
+
+    r->buf2.index = 0;
+    r->buf2.length = r->w_sz * out_n_samples;
+
+    if (!r->buf2.memblock || r->buf2_samples < out_n_samples) {
+        if (r->buf2.memblock)
+            pa_memblock_unref(r->buf2.memblock);
+
+        r->buf2_samples = out_n_samples;
+        r->buf2.memblock = pa_memblock_new(r->mempool, r->buf2.length);
+    }
+
+    src = ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+    dst = pa_memblock_acquire(r->buf2.memblock);
+
+    memset(dst, 0, r->buf2.length);
+
+    o_skip = r->w_sz * r->o_ss.channels;
+    i_skip = r->w_sz * r->i_ss.channels;
+
+    switch (r->work_format) {
+        case PA_SAMPLE_FLOAT32NE:
+
+            for (oc = 0; oc < r->o_ss.channels; oc++) {
+                unsigned ic;
+                static const float one = 1.0;
+
+                for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+                    if (r->map_table[oc][ic] <= 0.0)
+                        continue;
+
+                    oil_vectoradd_f32(
+                            (float*) dst + oc, o_skip,
+                            (float*) dst + oc, o_skip,
+                            (float*) src + ic, i_skip,
+                            n_frames,
+                            &one, &r->map_table[oc][ic]);
+                }
+            }
+
+            break;
+
+        case PA_SAMPLE_S16NE:
+
+            for (oc = 0; oc < r->o_ss.channels; oc++) {
+                unsigned ic;
+
+                for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+                    if (r->map_table[oc][ic] <= 0.0)
+                        continue;
+
+                    if (r->map_table[oc][ic] >= 1.0) {
+                        static const int16_t one = 1;
+
+                        oil_vectoradd_s16(
+                                (int16_t*) dst + oc, o_skip,
+                                (int16_t*) dst + oc, o_skip,
+                                (int16_t*) src + ic, i_skip,
+                                n_frames,
+                                &one, &one);
+
+                    } else
+
+                        vectoradd_s16_with_fraction(
+                                (int16_t*) dst + oc, o_skip,
+                                (int16_t*) dst + oc, o_skip,
+                                (int16_t*) src + ic, i_skip,
+                                n_frames,
+                                1.0, r->map_table[oc][ic]);
+                }
+            }
+
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(r->buf2.memblock);
+
+    r->buf2.length = out_n_samples * r->w_sz;
+
+    return &r->buf2;
+}
+
+static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
+    unsigned in_n_frames, in_n_samples;
+    unsigned out_n_frames, out_n_samples;
+
+    pa_assert(r);
+    pa_assert(input);
+
+    /* Resample the data and place the result in buf3 */
+
+    if (!r->impl_resample || !input->length)
+        return input;
+
+    in_n_samples = input->length / r->w_sz;
+    in_n_frames = in_n_samples / r->o_ss.channels;
+
+    out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES;
+    out_n_samples = out_n_frames * r->o_ss.channels;
+
+    r->buf3.index = 0;
+    r->buf3.length = r->w_sz * out_n_samples;
+
+    if (!r->buf3.memblock || r->buf3_samples < out_n_samples) {
+        if (r->buf3.memblock)
+            pa_memblock_unref(r->buf3.memblock);
+
+        r->buf3_samples = out_n_samples;
+        r->buf3.memblock = pa_memblock_new(r->mempool, r->buf3.length);
+    }
+
+    r->impl_resample(r, input, in_n_frames, &r->buf3, &out_n_frames);
+    r->buf3.length = out_n_frames * r->w_sz * r->o_ss.channels;
+
+    return &r->buf3;
+}
+
+static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input) {
+    unsigned n_samples, n_frames;
+    void *src, *dst;
+
+    pa_assert(r);
+    pa_assert(input);
+
+    /* Convert the data into the correct sample type and place the result in buf4 */
+
+    if (!r->from_work_format_func || !input->length)
+        return input;
+
+    n_samples = input->length / r->w_sz;
+    n_frames =  n_samples / r->o_ss.channels;
+
+    r->buf4.index = 0;
+    r->buf4.length = r->o_fz * n_frames;
+
+    if (!r->buf4.memblock || r->buf4_samples < n_samples) {
+        if (r->buf4.memblock)
+            pa_memblock_unref(r->buf4.memblock);
+
+        r->buf4_samples = n_samples;
+        r->buf4.memblock = pa_memblock_new(r->mempool, r->buf4.length);
+    }
+
+    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
+    dst = pa_memblock_acquire(r->buf4.memblock);
+    r->from_work_format_func(n_samples, src, dst);
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(r->buf4.memblock);
+
+    r->buf4.length = r->o_fz * n_frames;
+
+    return &r->buf4;
+}
+
+void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
+    pa_memchunk *buf;
+
+    pa_assert(r);
+    pa_assert(in);
+    pa_assert(out);
+    pa_assert(in->length);
+    pa_assert(in->memblock);
+    pa_assert(in->length % r->i_fz == 0);
+
+    buf = (pa_memchunk*) in;
+    buf = convert_to_work_format(r, buf);
+    buf = remap_channels(r, buf);
+    buf = resample(r, buf);
+
+    if (buf->length) {
+        buf = convert_from_work_format(r, buf);
+        *out = *buf;
+
+        if (buf == in)
+            pa_memblock_ref(buf->memblock);
+        else
+            pa_memchunk_reset(buf);
+    } else
+        pa_memchunk_reset(out);
+}
+
+/*** libsamplerate based implementation ***/
+
+#ifdef HAVE_LIBSAMPLERATE
+static void libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    SRC_DATA data;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    memset(&data, 0, sizeof(data));
+
+    data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+    data.input_frames = in_n_frames;
+
+    data.data_out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
+    data.output_frames = *out_n_frames;
+
+    data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
+    data.end_of_input = 0;
+
+    pa_assert_se(src_process(r->src.state, &data) == 0);
+    pa_assert((unsigned) data.input_frames_used == in_n_frames);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    *out_n_frames = data.output_frames_gen;
+}
+
+static void libsamplerate_update_rates(pa_resampler *r) {
+    pa_assert(r);
+
+    pa_assert_se(src_set_ratio(r->src.state, (double) r->o_ss.rate / r->i_ss.rate) == 0);
+}
+
+static void libsamplerate_reset(pa_resampler *r) {
+    pa_assert(r);
+
+    pa_assert_se(src_reset(r->src.state) == 0);
+}
+
+static void libsamplerate_free(pa_resampler *r) {
+    pa_assert(r);
+
+    if (r->src.state)
+        src_delete(r->src.state);
+}
+
+static int libsamplerate_init(pa_resampler *r) {
+    int err;
+
+    pa_assert(r);
+
+    if (!(r->src.state = src_new(r->method, r->o_ss.channels, &err)))
+        return -1;
+
+    r->impl_free = libsamplerate_free;
+    r->impl_update_rates = libsamplerate_update_rates;
+    r->impl_resample = libsamplerate_resample;
+    r->impl_reset = libsamplerate_reset;
+
+    return 0;
+}
+#endif
+
+/*** speex based implementation ***/
+
+static void speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    float *in, *out;
+    uint32_t inf = in_n_frames, outf = *out_n_frames;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+    out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
+
+    pa_assert_se(paspfl_resampler_process_interleaved_float(r->speex.state, in, &inf, out, &outf) == 0);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    pa_assert(inf == in_n_frames);
+    *out_n_frames = outf;
+}
+
+static void speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    int16_t *in, *out;
+    uint32_t inf = in_n_frames, outf = *out_n_frames;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    in = (int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+    out = (int16_t*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
+
+    pa_assert_se(paspfx_resampler_process_interleaved_int(r->speex.state, in, &inf, out, &outf) == 0);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    pa_assert(inf == in_n_frames);
+    *out_n_frames = outf;
+}
+
+static void speex_update_rates(pa_resampler *r) {
+    pa_assert(r);
+
+    if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+        pa_assert_se(paspfx_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0);
+    else {
+        pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+        pa_assert_se(paspfl_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0);
+    }
+}
+
+static void speex_reset(pa_resampler *r) {
+    pa_assert(r);
+
+    if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+        pa_assert_se(paspfx_resampler_reset_mem(r->speex.state) == 0);
+    else  {
+        pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+        pa_assert_se(paspfl_resampler_reset_mem(r->speex.state) == 0);
+    }
+}
+
+static void speex_free(pa_resampler *r) {
+    pa_assert(r);
+
+    if (!r->speex.state)
+        return;
+
+    if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+        paspfx_resampler_destroy(r->speex.state);
+    else {
+        pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+        paspfl_resampler_destroy(r->speex.state);
+    }
+}
+
+static int speex_init(pa_resampler *r) {
+    int q, err;
+
+    pa_assert(r);
+
+    r->impl_free = speex_free;
+    r->impl_update_rates = speex_update_rates;
+    r->impl_reset = speex_reset;
+
+    if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
+        q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE;
+
+        pa_log_info("Choosing speex quality setting %i.", q);
+
+        if (!(r->speex.state = paspfx_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
+            return -1;
+
+        r->impl_resample = speex_resample_int;
+    } else {
+        pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
+        q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
+
+        pa_log_info("Choosing speex quality setting %i.", q);
+
+        if (!(r->speex.state = paspfl_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
+            return -1;
+
+        r->impl_resample = speex_resample_float;
+    }
+
+    return 0;
+}
+
+/* Trivial implementation */
+
+static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    size_t fz;
+    unsigned o_index;
+    void *src, *dst;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    fz = r->w_sz * r->o_ss.channels;
+
+    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
+    dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index;
+
+    for (o_index = 0;; o_index++, r->trivial.o_counter++) {
+        unsigned j;
+
+        j = ((r->trivial.o_counter * r->i_ss.rate) / r->o_ss.rate);
+        j = j > r->trivial.i_counter ? j - r->trivial.i_counter : 0;
+
+        if (j >= in_n_frames)
+            break;
+
+        pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
+
+        oil_memcpy((uint8_t*) dst + fz * o_index,
+                   (uint8_t*) src + fz * j, fz);
+    }
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    *out_n_frames = o_index;
+
+    r->trivial.i_counter += in_n_frames;
+
+    /* Normalize counters */
+    while (r->trivial.i_counter >= r->i_ss.rate) {
+        pa_assert(r->trivial.o_counter >= r->o_ss.rate);
+
+        r->trivial.i_counter -= r->i_ss.rate;
+        r->trivial.o_counter -= r->o_ss.rate;
+    }
+}
+
+static void trivial_update_rates_or_reset(pa_resampler *r) {
+    pa_assert(r);
+
+    r->trivial.i_counter = 0;
+    r->trivial.o_counter = 0;
+}
+
+static int trivial_init(pa_resampler*r) {
+    pa_assert(r);
+
+    r->trivial.o_counter = r->trivial.i_counter = 0;
+
+    r->impl_resample = trivial_resample;
+    r->impl_update_rates = trivial_update_rates_or_reset;
+    r->impl_reset = trivial_update_rates_or_reset;
+
+    return 0;
+}
+
+/* Peak finder implementation */
+
+static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    size_t fz;
+    unsigned o_index;
+    void *src, *dst;
+    unsigned start = 0;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    fz = r->w_sz * r->o_ss.channels;
+
+    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
+    dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index;
+
+    for (o_index = 0;; o_index++, r->peaks.o_counter++) {
+        unsigned j;
+
+        j = ((r->peaks.o_counter * r->i_ss.rate) / r->o_ss.rate);
+        j = j > r->peaks.i_counter ? j - r->peaks.i_counter : 0;
+
+        if (j >= in_n_frames)
+            break;
+
+        pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
+
+        if (r->work_format == PA_SAMPLE_S16NE) {
+            unsigned i, c;
+            int16_t *s = (int16_t*) ((uint8_t*) src + fz * j);
+            int16_t *d = (int16_t*) ((uint8_t*) dst + fz * o_index);
+
+            for (i = start; i <= j; i++)
+                for (c = 0; c < r->o_ss.channels; c++, s++) {
+                    int16_t n;
+
+                    n = *s < 0 ? -*s : *s;
+
+                    if (n > r->peaks.max_i[c])
+                        r->peaks.max_i[c] = n;
+                }
+
+            for (c = 0; c < r->o_ss.channels; c++, d++) {
+                 *d = r->peaks.max_i[c];
+                 r->peaks.max_i[c] = 0;
+            }
+        } else {
+            unsigned i, c;
+            float *s = (float*) ((uint8_t*) src + fz * j);
+            float *d = (float*) ((uint8_t*) dst + fz * o_index);
+
+            pa_assert(r->work_format == PA_SAMPLE_FLOAT32NE);
+
+            for (i = start; i <= j; i++)
+                for (c = 0; c < r->o_ss.channels; c++, s++) {
+                    float n = fabsf(*s);
+
+                    if (n > r->peaks.max_f[c])
+                        r->peaks.max_f[c] = n;
+                }
+
+            for (c = 0; c < r->o_ss.channels; c++, d++) {
+                *d = r->peaks.max_f[c];
+                r->peaks.max_f[c] = 0;
+            }
+        }
+
+        start = j+1;
+    }
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    *out_n_frames = o_index;
+
+    r->peaks.i_counter += in_n_frames;
+
+    /* Normalize counters */
+    while (r->peaks.i_counter >= r->i_ss.rate) {
+        pa_assert(r->peaks.o_counter >= r->o_ss.rate);
+
+        r->peaks.i_counter -= r->i_ss.rate;
+        r->peaks.o_counter -= r->o_ss.rate;
+    }
+}
+
+static void peaks_update_rates_or_reset(pa_resampler *r) {
+    pa_assert(r);
+
+    r->peaks.i_counter = 0;
+    r->peaks.o_counter = 0;
+}
+
+static int peaks_init(pa_resampler*r) {
+    pa_assert(r);
+
+    r->peaks.o_counter = r->peaks.i_counter = 0;
+    memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i));
+    memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f));
+
+    r->impl_resample = peaks_resample;
+    r->impl_update_rates = peaks_update_rates_or_reset;
+    r->impl_reset = peaks_update_rates_or_reset;
+
+    return 0;
+}
+
+/*** ffmpeg based implementation ***/
+
+static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    unsigned used_frames = 0, c;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    for (c = 0; c < r->o_ss.channels; c++) {
+        unsigned u;
+        pa_memblock *b, *w;
+        int16_t *p, *t, *k, *q, *s;
+        int consumed_frames;
+        unsigned in, l;
+
+        /* Allocate a new block */
+        b = pa_memblock_new(r->mempool, r->ffmpeg.buf[c].length + in_n_frames * sizeof(int16_t));
+        p = pa_memblock_acquire(b);
+
+        /* Copy the remaining data into it */
+        l = r->ffmpeg.buf[c].length;
+        if (r->ffmpeg.buf[c].memblock) {
+            t = (int16_t*) ((uint8_t*) pa_memblock_acquire(r->ffmpeg.buf[c].memblock) + r->ffmpeg.buf[c].index);
+            memcpy(p, t, l);
+            pa_memblock_release(r->ffmpeg.buf[c].memblock);
+            pa_memblock_unref(r->ffmpeg.buf[c].memblock);
+            pa_memchunk_reset(&r->ffmpeg.buf[c]);
+        }
+
+        /* Now append the new data, splitting up channels */
+        t = ((int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index)) + c;
+        k = (int16_t*) ((uint8_t*) p + l);
+        for (u = 0; u < in_n_frames; u++) {
+            *k = *t;
+            t += r->o_ss.channels;
+            k ++;
+        }
+        pa_memblock_release(input->memblock);
+
+        /* Calculate the resulting number of frames */
+        in = in_n_frames + l / sizeof(int16_t);
+
+        /* Allocate buffer for the result */
+        w = pa_memblock_new(r->mempool, *out_n_frames * sizeof(int16_t));
+        q = pa_memblock_acquire(w);
+
+        /* Now, resample */
+        used_frames = av_resample(r->ffmpeg.state,
+                                  q, p,
+                                  &consumed_frames,
+                                  in, *out_n_frames,
+                                  c >= (unsigned) r->o_ss.channels-1);
+
+        pa_memblock_release(b);
+
+        /* Now store the remaining samples away */
+        pa_assert(consumed_frames <= (int) in);
+        if (consumed_frames < (int) in) {
+            r->ffmpeg.buf[c].memblock = b;
+            r->ffmpeg.buf[c].index = consumed_frames * sizeof(int16_t);
+            r->ffmpeg.buf[c].length = (in - consumed_frames) * sizeof(int16_t);
+        } else
+            pa_memblock_unref(b);
+
+        /* And place the results in the output buffer */
+        s = (short*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index) + c;
+        for (u = 0; u < used_frames; u++) {
+            *s = *q;
+            q++;
+            s += r->o_ss.channels;
+        }
+        pa_memblock_release(output->memblock);
+        pa_memblock_release(w);
+        pa_memblock_unref(w);
+    }
+
+    *out_n_frames = used_frames;
+}
+
+static void ffmpeg_free(pa_resampler *r) {
+    unsigned c;
+
+    pa_assert(r);
+
+    if (r->ffmpeg.state)
+        av_resample_close(r->ffmpeg.state);
+
+    for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++)
+        if (r->ffmpeg.buf[c].memblock)
+            pa_memblock_unref(r->ffmpeg.buf[c].memblock);
+}
+
+static int ffmpeg_init(pa_resampler *r) {
+    unsigned c;
+
+    pa_assert(r);
+
+    /* We could probably implement different quality levels by
+     * adjusting the filter parameters here. However, ffmpeg
+     * internally only uses these hardcoded values, so let's use them
+     * here for now as well until ffmpeg makes this configurable. */
+
+    if (!(r->ffmpeg.state = av_resample_init(r->o_ss.rate, r->i_ss.rate, 16, 10, 0, 0.8)))
+        return -1;
+
+    r->impl_free = ffmpeg_free;
+    r->impl_resample = ffmpeg_resample;
+
+    for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++)
+        pa_memchunk_reset(&r->ffmpeg.buf[c]);
+
+    return 0;
+}
+
+/*** copy (noop) implementation ***/
+
+static int copy_init(pa_resampler *r) {
+    pa_assert(r);
+
+    pa_assert(r->o_ss.rate == r->i_ss.rate);
+
+    return 0;
+}
diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h
new file mode 100644 (file)
index 0000000..5e302a9
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef fooresamplerhfoo
+#define fooresamplerhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_resampler pa_resampler;
+
+typedef enum pa_resample_method {
+    PA_RESAMPLER_INVALID                 = -1,
+    PA_RESAMPLER_SRC_SINC_BEST_QUALITY   = 0, /* = SRC_SINC_BEST_QUALITY */
+    PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = 1, /* = SRC_SINC_MEDIUM_QUALITY */
+    PA_RESAMPLER_SRC_SINC_FASTEST        = 2, /* = SRC_SINC_FASTEST */
+    PA_RESAMPLER_SRC_ZERO_ORDER_HOLD     = 3, /* = SRC_ZERO_ORDER_HOLD */
+    PA_RESAMPLER_SRC_LINEAR              = 4, /* = SRC_LINEAR */
+    PA_RESAMPLER_TRIVIAL,
+    PA_RESAMPLER_SPEEX_FLOAT_BASE,
+    PA_RESAMPLER_SPEEX_FLOAT_MAX = PA_RESAMPLER_SPEEX_FLOAT_BASE + 10,
+    PA_RESAMPLER_SPEEX_FIXED_BASE,
+    PA_RESAMPLER_SPEEX_FIXED_MAX = PA_RESAMPLER_SPEEX_FIXED_BASE + 10,
+    PA_RESAMPLER_FFMPEG,
+    PA_RESAMPLER_AUTO, /* automatic select based on sample format */
+    PA_RESAMPLER_COPY,
+    PA_RESAMPLER_PEAKS,
+    PA_RESAMPLER_MAX
+} pa_resample_method_t;
+
+typedef enum pa_resample_flags {
+    PA_RESAMPLER_VARIABLE_RATE = 1,
+    PA_RESAMPLER_NO_REMAP = 2,  /* implies NO_REMIX */
+    PA_RESAMPLER_NO_REMIX = 4
+} pa_resample_flags_t;
+
+pa_resampler* pa_resampler_new(
+        pa_mempool *pool,
+        const pa_sample_spec *a,
+        const pa_channel_map *am,
+        const pa_sample_spec *b,
+        const pa_channel_map *bm,
+        pa_resample_method_t resample_method,
+        pa_resample_flags_t flags);
+
+void pa_resampler_free(pa_resampler *r);
+
+/* Returns the size of an input memory block which is required to return the specified amount of output data */
+size_t pa_resampler_request(pa_resampler *r, size_t out_length);
+
+/* Inverse of pa_resampler_request() */
+size_t pa_resampler_result(pa_resampler *r, size_t in_length);
+
+/* Returns the maximum size of input blocks we can process without needing bounce buffers larger than the mempool tile size. */
+size_t pa_resampler_max_block_size(pa_resampler *r);
+
+/* Pass the specified memory chunk to the resampler and return the newly resampled data */
+void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out);
+
+/* Change the input rate of the resampler object */
+void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate);
+
+/* Change the output rate of the resampler object */
+void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate);
+
+/* Reinitialize state of the resampler, possibly due to seeking or other discontinuities */
+void pa_resampler_reset(pa_resampler *r);
+
+/* Return the resampling method of the resampler object */
+pa_resample_method_t pa_resampler_get_method(pa_resampler *r);
+
+/* Try to parse the resampler method */
+pa_resample_method_t pa_parse_resample_method(const char *string);
+
+/* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */
+const char *pa_resample_method_to_string(pa_resample_method_t m);
+
+/* Return 1 when the specified resampling method is supported */
+int pa_resample_method_supported(pa_resample_method_t m);
+
+
+#endif
diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c
new file mode 100644 (file)
index 0000000..f33de83
--- /dev/null
@@ -0,0 +1,117 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <stddef.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <pulse/timeval.h>
+#include <pulsecore/macro.h>
+
+#include "rtclock.h"
+
+pa_usec_t pa_rtclock_age(const struct timeval *tv) {
+    struct timeval now;
+    pa_assert(tv);
+
+    return pa_timeval_diff(pa_rtclock_get(&now), tv);
+}
+
+struct timeval *pa_rtclock_get(struct timeval *tv) {
+#ifdef HAVE_CLOCK_GETTIME
+    struct timespec ts;
+
+#ifdef CLOCK_MONOTONIC
+    /* No locking or atomic ops for no_monotonic here */
+    static pa_bool_t no_monotonic = FALSE;
+
+    if (!no_monotonic)
+        if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
+            no_monotonic = TRUE;
+
+    if (no_monotonic)
+#endif
+        pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+
+    pa_assert(tv);
+
+    tv->tv_sec = ts.tv_sec;
+    tv->tv_usec = ts.tv_nsec / 1000;
+
+    return tv;
+
+#else /* HAVE_CLOCK_GETTIME */
+
+    return pa_gettimeofday(tv);
+
+#endif
+}
+
+pa_bool_t pa_rtclock_hrtimer(void) {
+#ifdef HAVE_CLOCK_GETTIME
+    struct timespec ts;
+
+#ifdef CLOCK_MONOTONIC
+    if (clock_getres(CLOCK_MONOTONIC, &ts) >= 0)
+        return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000;
+#endif
+
+    pa_assert_se(clock_getres(CLOCK_REALTIME, &ts) == 0);
+    return ts.tv_sec == 0 && ts.tv_nsec <= PA_HRTIMER_THRESHOLD_USEC*1000;
+
+#else /* HAVE_CLOCK_GETTIME */
+
+    return FALSE;
+
+#endif
+}
+
+pa_usec_t pa_rtclock_usec(void) {
+    struct timeval tv;
+
+    return pa_timeval_load(pa_rtclock_get(&tv));
+}
+
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
+
+#ifdef HAVE_CLOCK_GETTIME
+    struct timeval wc_now, rt_now;
+
+    pa_gettimeofday(&wc_now);
+    pa_rtclock_get(&rt_now);
+
+    pa_assert(tv);
+
+    if (pa_timeval_cmp(&wc_now, tv) < 0)
+        pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
+    else
+        pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
+
+    *tv = rt_now;
+#endif
+
+    return tv;
+}
diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h
new file mode 100644 (file)
index 0000000..705de48
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef foopulsertclockhfoo
+#define foopulsertclockhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/macro.h>
+
+struct timeval;
+
+/* Something like pulse/timeval.h but based on CLOCK_MONOTONIC */
+
+struct timeval *pa_rtclock_get(struct timeval *ts);
+
+pa_usec_t pa_rtclock_usec(void);
+
+pa_usec_t pa_rtclock_age(const struct timeval *tv);
+pa_bool_t pa_rtclock_hrtimer(void);
+
+/* timer with a resolution better than this are considered high-resolution */
+#define PA_HRTIMER_THRESHOLD_USEC 10
+
+struct timeval* pa_rtclock_from_wallclock(struct timeval *tv);
+
+#endif
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
new file mode 100644 (file)
index 0000000..a67a551
--- /dev/null
@@ -0,0 +1,766 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else
+#include <pulsecore/poll.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/rtsig.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/core-util.h>
+
+#include <pulsecore/winsock.h>
+
+#include "rtpoll.h"
+
+struct pa_rtpoll {
+    struct pollfd *pollfd, *pollfd2;
+    unsigned n_pollfd_alloc, n_pollfd_used;
+
+    pa_bool_t timer_enabled;
+    struct timeval next_elapse;
+
+    pa_bool_t scan_for_dead;
+    pa_bool_t running, installed, rebuild_needed, quit;
+
+#ifdef HAVE_PPOLL
+    int rtsig;
+    sigset_t sigset_unblocked;
+    timer_t timer;
+    pa_bool_t timer_armed;
+#ifdef __linux__
+    pa_bool_t dont_use_ppoll;
+#endif
+#endif
+
+    PA_LLIST_HEAD(pa_rtpoll_item, items);
+};
+
+struct pa_rtpoll_item {
+    pa_rtpoll *rtpoll;
+    pa_bool_t dead;
+
+    pa_rtpoll_priority_t priority;
+
+    struct pollfd *pollfd;
+    unsigned n_pollfd;
+
+    int (*work_cb)(pa_rtpoll_item *i);
+    int (*before_cb)(pa_rtpoll_item *i);
+    void (*after_cb)(pa_rtpoll_item *i);
+    void *userdata;
+
+    PA_LLIST_FIELDS(pa_rtpoll_item);
+};
+
+PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
+
+static void signal_handler_noop(int s) { /* write(2, "signal\n", 7); */ }
+
+pa_rtpoll *pa_rtpoll_new(void) {
+    pa_rtpoll *p;
+
+    p = pa_xnew(pa_rtpoll, 1);
+
+#ifdef HAVE_PPOLL
+
+#ifdef __linux__
+    /* ppoll is broken on Linux < 2.6.16 */
+    p->dont_use_ppoll = FALSE;
+
+    {
+        struct utsname u;
+        unsigned major, minor, micro;
+
+        pa_assert_se(uname(&u) == 0);
+
+        if (sscanf(u.release, "%u.%u.%u", &major, &minor, &micro) != 3 ||
+            (major < 2) ||
+            (major == 2 && minor < 6) ||
+            (major == 2 && minor == 6 && micro < 16))
+
+            p->dont_use_ppoll = TRUE;
+    }
+
+#endif
+
+    p->rtsig = -1;
+    sigemptyset(&p->sigset_unblocked);
+    p->timer = (timer_t) -1;
+    p->timer_armed = FALSE;
+
+#endif
+
+    p->n_pollfd_alloc = 32;
+    p->pollfd = pa_xnew(struct pollfd, p->n_pollfd_alloc);
+    p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc);
+    p->n_pollfd_used = 0;
+
+    memset(&p->next_elapse, 0, sizeof(p->next_elapse));
+    p->timer_enabled = FALSE;
+
+    p->running = FALSE;
+    p->installed = FALSE;
+    p->scan_for_dead = FALSE;
+    p->rebuild_needed = FALSE;
+    p->quit = FALSE;
+
+    PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items);
+
+    return p;
+}
+
+void pa_rtpoll_install(pa_rtpoll *p) {
+    pa_assert(p);
+    pa_assert(!p->installed);
+
+    p->installed = 1;
+
+#ifdef HAVE_PPOLL
+# ifdef __linux__
+    if (p->dont_use_ppoll)
+        return;
+# endif
+
+    if ((p->rtsig = pa_rtsig_get_for_thread()) < 0) {
+        pa_log_warn("Failed to reserve POSIX realtime signal.");
+        return;
+    }
+
+    pa_log_debug("Acquired POSIX realtime signal %s", pa_sig2str(p->rtsig));
+
+    {
+        sigset_t ss;
+        struct sigaction sa;
+
+        pa_assert_se(sigemptyset(&ss) == 0);
+        pa_assert_se(sigaddset(&ss, p->rtsig) == 0);
+        pa_assert_se(pthread_sigmask(SIG_BLOCK, &ss, &p->sigset_unblocked) == 0);
+        pa_assert_se(sigdelset(&p->sigset_unblocked, p->rtsig) == 0);
+
+        memset(&sa, 0, sizeof(sa));
+        sa.sa_handler = signal_handler_noop;
+        pa_assert_se(sigemptyset(&sa.sa_mask) == 0);
+
+        pa_assert_se(sigaction(p->rtsig, &sa, NULL) == 0);
+
+        /* We never reset the signal handler. Why should we? */
+    }
+
+#endif
+}
+
+static void rtpoll_rebuild(pa_rtpoll *p) {
+
+    struct pollfd *e, *t;
+    pa_rtpoll_item *i;
+    int ra = 0;
+
+    pa_assert(p);
+
+    p->rebuild_needed = FALSE;
+
+    if (p->n_pollfd_used > p->n_pollfd_alloc) {
+        /* Hmm, we have to allocate some more space */
+        p->n_pollfd_alloc = p->n_pollfd_used * 2;
+        p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd));
+        ra = 1;
+    }
+
+    e = p->pollfd2;
+
+    for (i = p->items; i; i = i->next) {
+
+        if (i->n_pollfd > 0)  {
+            size_t l = i->n_pollfd * sizeof(struct pollfd);
+
+            if (i->pollfd)
+                memcpy(e, i->pollfd, l);
+            else
+                memset(e, 0, l);
+
+            i->pollfd = e;
+        } else
+            i->pollfd = NULL;
+
+        e += i->n_pollfd;
+    }
+
+    pa_assert((unsigned) (e - p->pollfd2) == p->n_pollfd_used);
+    t = p->pollfd;
+    p->pollfd = p->pollfd2;
+    p->pollfd2 = t;
+
+    if (ra)
+        p->pollfd2 = pa_xrealloc(p->pollfd2, p->n_pollfd_alloc * sizeof(struct pollfd));
+
+}
+
+static void rtpoll_item_destroy(pa_rtpoll_item *i) {
+    pa_rtpoll *p;
+
+    pa_assert(i);
+
+    p = i->rtpoll;
+
+    PA_LLIST_REMOVE(pa_rtpoll_item, p->items, i);
+
+    p->n_pollfd_used -= i->n_pollfd;
+
+    if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
+        pa_xfree(i);
+
+    p->rebuild_needed = TRUE;
+}
+
+void pa_rtpoll_free(pa_rtpoll *p) {
+    pa_assert(p);
+
+    while (p->items)
+        rtpoll_item_destroy(p->items);
+
+    pa_xfree(p->pollfd);
+    pa_xfree(p->pollfd2);
+
+#ifdef HAVE_PPOLL
+    if (p->timer != (timer_t) -1)
+        timer_delete(p->timer);
+#endif
+
+    pa_xfree(p);
+}
+
+static void reset_revents(pa_rtpoll_item *i) {
+    struct pollfd *f;
+    unsigned n;
+
+    pa_assert(i);
+
+    if (!(f = pa_rtpoll_item_get_pollfd(i, &n)))
+        return;
+
+    for (; n > 0; n--)
+        f[n-1].revents = 0;
+}
+
+static void reset_all_revents(pa_rtpoll *p) {
+    pa_rtpoll_item *i;
+
+    pa_assert(p);
+
+    for (i = p->items; i; i = i->next) {
+
+        if (i->dead)
+            continue;
+
+        reset_revents(i);
+    }
+}
+
+int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
+    pa_rtpoll_item *i;
+    int r = 0;
+    struct timeval timeout;
+
+    pa_assert(p);
+    pa_assert(!p->running);
+    pa_assert(p->installed);
+
+    p->running = TRUE;
+
+    /* First, let's do some work */
+    for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+        int k;
+
+        if (i->dead)
+            continue;
+
+        if (!i->work_cb)
+            continue;
+
+        if (p->quit)
+            goto finish;
+
+        if ((k = i->work_cb(i)) != 0) {
+            if (k < 0)
+                r = k;
+
+            goto finish;
+        }
+    }
+
+    /* Now let's prepare for entering the sleep */
+    for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+        int k = 0;
+
+        if (i->dead)
+            continue;
+
+        if (!i->before_cb)
+            continue;
+
+        if (p->quit || (k = i->before_cb(i)) != 0) {
+
+            /* Hmm, this one doesn't let us enter the poll, so rewind everything */
+
+            for (i = i->prev; i; i = i->prev) {
+
+                if (i->dead)
+                    continue;
+
+                if (!i->after_cb)
+                    continue;
+
+                i->after_cb(i);
+            }
+
+            if (k < 0)
+                r = k;
+
+            goto finish;
+        }
+    }
+
+    if (p->rebuild_needed)
+        rtpoll_rebuild(p);
+
+    memset(&timeout, 0, sizeof(timeout));
+
+    /* Calculate timeout */
+    if (wait && !p->quit && p->timer_enabled) {
+        struct timeval now;
+        pa_rtclock_get(&now);
+
+        if (pa_timeval_cmp(&p->next_elapse, &now) > 0)
+            pa_timeval_add(&timeout, pa_timeval_diff(&p->next_elapse, &now));
+    }
+
+    /* OK, now let's sleep */
+#ifdef HAVE_PPOLL
+
+#ifdef __linux__
+    if (!p->dont_use_ppoll)
+#endif
+    {
+        struct timespec ts;
+        ts.tv_sec = timeout.tv_sec;
+        ts.tv_nsec = timeout.tv_usec * 1000;
+        r = ppoll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? &ts : NULL, p->rtsig < 0 ? NULL : &p->sigset_unblocked);
+    }
+#ifdef __linux__
+    else
+#endif
+
+#endif
+        r = poll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1);
+
+    if (r < 0) {
+        if (errno == EAGAIN || errno == EINTR)
+            r = 0;
+        else
+            pa_log_error("poll(): %s", pa_cstrerror(errno));
+
+        reset_all_revents(p);
+    }
+
+    /* Let's tell everyone that we left the sleep */
+    for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
+
+        if (i->dead)
+            continue;
+
+        if (!i->after_cb)
+            continue;
+
+        i->after_cb(i);
+    }
+
+finish:
+
+    p->running = FALSE;
+
+    if (p->scan_for_dead) {
+        pa_rtpoll_item *n;
+
+        p->scan_for_dead = FALSE;
+
+        for (i = p->items; i; i = n) {
+            n = i->next;
+
+            if (i->dead)
+                rtpoll_item_destroy(i);
+        }
+    }
+
+    return r < 0 ? r : !p->quit;
+}
+
+static void update_timer(pa_rtpoll *p) {
+    pa_assert(p);
+
+#ifdef HAVE_PPOLL
+
+#ifdef __linux__
+    if (!p->dont_use_ppoll) {
+#endif
+
+        if (p->timer == (timer_t) -1) {
+            struct sigevent se;
+
+            memset(&se, 0, sizeof(se));
+            se.sigev_notify = SIGEV_SIGNAL;
+            se.sigev_signo = p->rtsig;
+
+            if (timer_create(CLOCK_MONOTONIC, &se, &p->timer) < 0)
+                if (timer_create(CLOCK_REALTIME, &se, &p->timer) < 0) {
+                    pa_log_warn("Failed to allocate POSIX timer: %s", pa_cstrerror(errno));
+                    p->timer = (timer_t) -1;
+                }
+        }
+
+        if (p->timer != (timer_t) -1) {
+            struct itimerspec its;
+            struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
+            sigset_t ss;
+
+            if (p->timer_armed) {
+                /* First disarm timer */
+                memset(&its, 0, sizeof(its));
+                pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
+
+                /* Remove a signal that might be waiting in the signal q */
+                pa_assert_se(sigemptyset(&ss) == 0);
+                pa_assert_se(sigaddset(&ss, p->rtsig) == 0);
+                sigtimedwait(&ss, NULL, &ts);
+            }
+
+            /* And install the new timer */
+            if (p->timer_enabled) {
+                memset(&its, 0, sizeof(its));
+
+                its.it_value.tv_sec = p->next_elapse.tv_sec;
+                its.it_value.tv_nsec = p->next_elapse.tv_usec*1000;
+
+                /* Make sure that 0,0 is not understood as
+                 * "disarming" */
+                if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0)
+                    its.it_value.tv_nsec = 1;
+                pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
+            }
+
+            p->timer_armed = p->timer_enabled;
+        }
+
+#ifdef __linux__
+    }
+#endif
+
+#endif
+}
+
+void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec) {
+    pa_assert(p);
+
+    pa_timeval_store(&p->next_elapse, usec);
+    p->timer_enabled = TRUE;
+
+    update_timer(p);
+}
+
+void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
+    pa_assert(p);
+
+    /* Scheduling a timeout for more than an hour is very very suspicious */
+    pa_assert(usec <= PA_USEC_PER_SEC*60ULL*60ULL);
+
+    pa_rtclock_get(&p->next_elapse);
+    pa_timeval_add(&p->next_elapse, usec);
+    p->timer_enabled = TRUE;
+
+    update_timer(p);
+}
+
+void pa_rtpoll_set_timer_disabled(pa_rtpoll *p) {
+    pa_assert(p);
+
+    memset(&p->next_elapse, 0, sizeof(p->next_elapse));
+    p->timer_enabled = FALSE;
+
+    update_timer(p);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds) {
+    pa_rtpoll_item *i, *j, *l = NULL;
+
+    pa_assert(p);
+
+    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
+        i = pa_xnew(pa_rtpoll_item, 1);
+
+    i->rtpoll = p;
+    i->dead = FALSE;
+    i->n_pollfd = n_fds;
+    i->pollfd = NULL;
+    i->priority = prio;
+
+    i->userdata = NULL;
+    i->before_cb = NULL;
+    i->after_cb = NULL;
+    i->work_cb = NULL;
+
+    for (j = p->items; j; j = j->next) {
+        if (prio <= j->priority)
+            break;
+
+        l = j;
+    }
+
+    PA_LLIST_INSERT_AFTER(pa_rtpoll_item, p->items, j ? j->prev : l, i);
+
+    if (n_fds > 0) {
+        p->rebuild_needed = 1;
+        p->n_pollfd_used += n_fds;
+    }
+
+    return i;
+}
+
+void pa_rtpoll_item_free(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    if (i->rtpoll->running) {
+        i->dead = TRUE;
+        i->rtpoll->scan_for_dead = TRUE;
+        return;
+    }
+
+    rtpoll_item_destroy(i);
+}
+
+struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds) {
+    pa_assert(i);
+
+    if (i->n_pollfd > 0)
+        if (i->rtpoll->rebuild_needed)
+            rtpoll_rebuild(i->rtpoll);
+
+    if (n_fds)
+        *n_fds = i->n_pollfd;
+
+    return i->pollfd;
+}
+
+void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i)) {
+    pa_assert(i);
+    pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+    i->before_cb = before_cb;
+}
+
+void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i)) {
+    pa_assert(i);
+    pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+    i->after_cb = after_cb;
+}
+
+void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i)) {
+    pa_assert(i);
+    pa_assert(i->priority < PA_RTPOLL_NEVER);
+
+    i->work_cb = work_cb;
+}
+
+void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata) {
+    pa_assert(i);
+
+    i->userdata = userdata;
+}
+
+void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    return i->userdata;
+}
+
+static int fdsem_before(pa_rtpoll_item *i) {
+
+    if (pa_fdsem_before_poll(i->userdata) < 0)
+        return 1; /* 1 means immediate restart of the loop */
+
+    return 0;
+}
+
+static void fdsem_after(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+    pa_fdsem_after_poll(i->userdata);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *f) {
+    pa_rtpoll_item *i;
+    struct pollfd *pollfd;
+
+    pa_assert(p);
+    pa_assert(f);
+
+    i = pa_rtpoll_item_new(p, prio, 1);
+
+    pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+
+    pollfd->fd = pa_fdsem_get(f);
+    pollfd->events = POLLIN;
+
+    i->before_cb = fdsem_before;
+    i->after_cb = fdsem_after;
+    i->userdata = f;
+
+    return i;
+}
+
+static int asyncmsgq_read_before(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    if (pa_asyncmsgq_read_before_poll(i->userdata) < 0)
+        return 1; /* 1 means immediate restart of the loop */
+
+    return 0;
+}
+
+static void asyncmsgq_read_after(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+    pa_asyncmsgq_read_after_poll(i->userdata);
+}
+
+static int asyncmsgq_read_work(pa_rtpoll_item *i) {
+    pa_msgobject *object;
+    int code;
+    void *data;
+    pa_memchunk chunk;
+    int64_t offset;
+
+    pa_assert(i);
+
+    if (pa_asyncmsgq_get(i->userdata, &object, &code, &data, &offset, &chunk, 0) == 0) {
+        int ret;
+
+        if (!object && code == PA_MESSAGE_SHUTDOWN) {
+            pa_asyncmsgq_done(i->userdata, 0);
+            pa_rtpoll_quit(i->rtpoll);
+            return 1;
+        }
+
+        ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+        pa_asyncmsgq_done(i->userdata, ret);
+        return 1;
+    }
+
+    return 0;
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
+    pa_rtpoll_item *i;
+    struct pollfd *pollfd;
+
+    pa_assert(p);
+    pa_assert(q);
+
+    i = pa_rtpoll_item_new(p, prio, 1);
+
+    pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+    pollfd->fd = pa_asyncmsgq_read_fd(q);
+    pollfd->events = POLLIN;
+
+    i->before_cb = asyncmsgq_read_before;
+    i->after_cb = asyncmsgq_read_after;
+    i->work_cb = asyncmsgq_read_work;
+    i->userdata = q;
+
+    return i;
+}
+
+static int asyncmsgq_write_before(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    pa_asyncmsgq_write_before_poll(i->userdata);
+    return 0;
+}
+
+static void asyncmsgq_write_after(pa_rtpoll_item *i) {
+    pa_assert(i);
+
+    pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
+    pa_asyncmsgq_write_after_poll(i->userdata);
+}
+
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
+    pa_rtpoll_item *i;
+    struct pollfd *pollfd;
+
+    pa_assert(p);
+    pa_assert(q);
+
+    i = pa_rtpoll_item_new(p, prio, 1);
+
+    pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+    pollfd->fd = pa_asyncmsgq_write_fd(q);
+    pollfd->events = POLLIN;
+
+    i->before_cb = asyncmsgq_write_before;
+    i->after_cb = asyncmsgq_write_after;
+    i->work_cb = NULL;
+    i->userdata = q;
+
+    return i;
+}
+
+void pa_rtpoll_quit(pa_rtpoll *p) {
+    pa_assert(p);
+
+    p->quit = TRUE;
+}
diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h
new file mode 100644 (file)
index 0000000..08776ef
--- /dev/null
@@ -0,0 +1,114 @@
+#ifndef foopulsertpollhfoo
+#define foopulsertpollhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+#include <limits.h>
+
+#include <pulse/sample.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/fdsem.h>
+#include <pulsecore/macro.h>
+
+/* An implementation of a "real-time" poll loop. Basically, this is
+ * yet another wrapper around poll(). However it has certain
+ * advantages over pa_mainloop and suchlike:
+ *
+ * 1) It uses timer_create() and POSIX real time signals to guarantee
+ * optimal high-resolution timing. Starting with Linux 2.6.21 hrtimers
+ * are available, and since right now only nanosleep() and the POSIX
+ * clock and timer interfaces have been ported to hrtimers (and not
+ * ppoll/pselect!) we have to combine ppoll() with timer_create(). The
+ * fact that POSIX timers and POSIX rt signals are used internally is
+ * completely hidden.
+ *
+ * 2) It allows raw access to the pollfd data to users
+ *
+ * 3) It allows arbitrary functions to be run before entering the
+ * actual poll() and after it.
+ *
+ * Only a single interval timer is supported..*/
+
+typedef struct pa_rtpoll pa_rtpoll;
+typedef struct pa_rtpoll_item pa_rtpoll_item;
+
+typedef enum pa_rtpoll_priority {
+    PA_RTPOLL_EARLY  = -100,          /* For veeery important stuff, like handling control messages */
+    PA_RTPOLL_NORMAL = 0,             /* For normal stuff */
+    PA_RTPOLL_LATE   = +100,          /* For housekeeping */
+    PA_RTPOLL_NEVER  = INT_MAX,       /* For stuff that doesn't register any callbacks, but only fds to listen on */
+} pa_rtpoll_priority_t;
+
+pa_rtpoll *pa_rtpoll_new(void);
+void pa_rtpoll_free(pa_rtpoll *p);
+
+/* Install the rtpoll in the current thread */
+void pa_rtpoll_install(pa_rtpoll *p);
+
+/* Sleep on the rtpoll until the time event, or any of the fd events
+ * is triggered. If "wait" is 0 we don't sleep but only update the
+ * struct pollfd. Returns negative on error, positive if the loop
+ * should continue to run, 0 when the loop should be terminated
+ * cleanly. */
+int pa_rtpoll_run(pa_rtpoll *f, pa_bool_t wait);
+
+void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec);
+void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec);
+void pa_rtpoll_set_timer_disabled(pa_rtpoll *p);
+
+/* A new fd wakeup item for pa_rtpoll */
+pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds);
+void pa_rtpoll_item_free(pa_rtpoll_item *i);
+
+/* Please note that this pointer might change on every call and when
+ * pa_rtpoll_run() is called. Hence: call this immediately before
+ * using the pointer and don't save the result anywhere */
+struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds);
+
+/* Set the callback that shall be called when there's time to do some work: If the
+ * callback returns a value > 0, the poll is skipped and the next
+ * iteraton of the loop will start immediately. */
+void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i));
+
+/* Set the callback that shall be called immediately before entering
+ * the sleeping poll: If the callback returns a value > 0, the poll is
+ * skipped and the next iteraton of the loop will start
+ * immediately.. */
+void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i));
+
+/* Set the callback that shall be called immediately after having
+ * entered the sleeping poll */
+void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i));
+
+void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata);
+void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i);
+
+pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *s);
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
+pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
+
+/* Requests the loop to exit. Will cause the next iteration of
+ * pa_rtpoll_run() to return 0 */
+void pa_rtpoll_quit(pa_rtpoll *p);
+
+#endif
diff --git a/src/pulsecore/rtsig.c b/src/pulsecore/rtsig.c
new file mode 100644 (file)
index 0000000..4df217c
--- /dev/null
@@ -0,0 +1,131 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulsecore/macro.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/once.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-util.h>
+
+#include "rtsig.h"
+
+#ifdef SIGRTMIN
+
+static void _free_rtsig(void *p) {
+    pa_rtsig_put(PA_PTR_TO_INT(p));
+}
+
+PA_STATIC_FLIST_DECLARE(rtsig_flist, pa_make_power_of_two(SIGRTMAX-SIGRTMIN+1), NULL);
+PA_STATIC_TLS_DECLARE(rtsig_tls, _free_rtsig);
+
+static pa_atomic_t rtsig_current = PA_ATOMIC_INIT(-1);
+
+static int rtsig_start = -1, rtsig_end = -1;
+
+int pa_rtsig_get(void) {
+    void *p;
+    int sig;
+
+    if ((p = pa_flist_pop(PA_STATIC_FLIST_GET(rtsig_flist))))
+        return PA_PTR_TO_INT(p);
+
+    sig = pa_atomic_dec(&rtsig_current);
+
+    pa_assert(sig <= SIGRTMAX);
+    pa_assert(sig <= rtsig_end);
+
+    if (sig < rtsig_start) {
+        pa_atomic_inc(&rtsig_current);
+        return -1;
+    }
+
+    return sig;
+}
+
+int pa_rtsig_get_for_thread(void) {
+    int sig;
+    void *p;
+
+    if ((p = PA_STATIC_TLS_GET(rtsig_tls)))
+        return PA_PTR_TO_INT(p);
+
+    if ((sig = pa_rtsig_get()) < 0)
+        return -1;
+
+    PA_STATIC_TLS_SET(rtsig_tls, PA_INT_TO_PTR(sig));
+    return sig;
+}
+
+void pa_rtsig_put(int sig) {
+    pa_assert(sig >= rtsig_start);
+    pa_assert(sig <= rtsig_end);
+
+    pa_assert_se(pa_flist_push(PA_STATIC_FLIST_GET(rtsig_flist), PA_INT_TO_PTR(sig)) >= 0);
+}
+
+void pa_rtsig_configure(int start, int end) {
+    int s;
+    sigset_t ss;
+
+    pa_assert(pa_atomic_load(&rtsig_current) == -1);
+
+    pa_assert(SIGRTMIN <= start);
+    pa_assert(start <= end);
+    pa_assert(end <= SIGRTMAX);
+
+    rtsig_start = start;
+    rtsig_end = end;
+
+    sigemptyset(&ss);
+
+    for (s = rtsig_start; s <= rtsig_end; s++)
+        pa_assert_se(sigaddset(&ss, s) == 0);
+
+    pa_assert(pthread_sigmask(SIG_BLOCK, &ss, NULL) == 0);
+
+    /* We allocate starting from the end */
+    pa_atomic_store(&rtsig_current, rtsig_end);
+}
+
+#else /* SIGRTMIN */
+
+int pa_rtsig_get(void) {
+    return -1;
+}
+
+int pa_rtsig_get_for_thread(void) {
+    return -1;
+}
+
+void pa_rtsig_put(int sig) {
+}
+
+void pa_rtsig_configure(int start, int end) {
+}
+
+#endif /* SIGRTMIN */
diff --git a/src/pulsecore/rtsig.h b/src/pulsecore/rtsig.h
new file mode 100644 (file)
index 0000000..e414122
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef foopulsertsighfoo
+#define foopulsertsighfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* Return the next unused POSIX Realtime signals */
+int pa_rtsig_get(void);
+
+/* If not called before in the current thread, return the next unused
+ * rtsig, and install it in a TLS region and give it up automatically
+ * when the thread shuts down */
+int pa_rtsig_get_for_thread(void);
+
+/* Give an rtsig back. */
+void pa_rtsig_put(int sig);
+
+/* Block all RT signals */
+void pa_rtsig_configure(int start, int end);
+
+#endif
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
new file mode 100644 (file)
index 0000000..b42b79d
--- /dev/null
@@ -0,0 +1,1027 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdlib.h>
+
+#include <liboil/liboilfuncs.h>
+#include <liboil/liboil.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/g711.h>
+
+#include "sample-util.h"
+#include "endianmacros.h"
+
+#define PA_SILENCE_MAX (PA_PAGE_SIZE*16)
+
+pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
+    void *data;
+
+    pa_assert(b);
+    pa_assert(spec);
+
+    data = pa_memblock_acquire(b);
+    pa_silence_memory(data, pa_memblock_get_length(b), spec);
+    pa_memblock_release(b);
+
+    return b;
+}
+
+pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
+    void *data;
+
+    pa_assert(c);
+    pa_assert(c->memblock);
+    pa_assert(spec);
+
+    data = pa_memblock_acquire(c->memblock);
+    pa_silence_memory((uint8_t*) data+c->index, c->length, spec);
+    pa_memblock_release(c->memblock);
+
+    return c;
+}
+
+static uint8_t silence_byte(pa_sample_format_t format) {
+    switch (format) {
+        case PA_SAMPLE_U8:
+            return 0x80;
+        case PA_SAMPLE_S16LE:
+        case PA_SAMPLE_S16BE:
+        case PA_SAMPLE_S32LE:
+        case PA_SAMPLE_S32BE:
+        case PA_SAMPLE_FLOAT32LE:
+        case PA_SAMPLE_FLOAT32BE:
+            return 0;
+        case PA_SAMPLE_ALAW:
+            return 0xd5;
+        case PA_SAMPLE_ULAW:
+            return 0xff;
+        default:
+            pa_assert_not_reached();
+    }
+    return 0;
+}
+
+void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
+    pa_assert(p);
+    pa_assert(length > 0);
+    pa_assert(spec);
+
+    memset(p, silence_byte(spec->format), length);
+    return p;
+}
+
+static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) {
+    unsigned k;
+
+    pa_assert(streams);
+    pa_assert(spec);
+
+    for (k = 0; k < nstreams; k++) {
+        unsigned channel;
+
+        for (channel = 0; channel < spec->channels; channel++) {
+            pa_mix_info *m = streams + k;
+            m->linear[channel].i = (int32_t) (pa_sw_volume_to_linear(m->volume.values[channel]) * 0x10000);
+        }
+    }
+}
+
+static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) {
+    unsigned channel;
+
+    pa_assert(linear);
+    pa_assert(volume);
+
+    for (channel = 0; channel < volume->channels; channel++)
+        linear[channel] = (int32_t) (pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
+}
+
+static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) {
+    unsigned k;
+
+    pa_assert(streams);
+    pa_assert(spec);
+
+    for (k = 0; k < nstreams; k++) {
+        unsigned channel;
+
+        for (channel = 0; channel < spec->channels; channel++) {
+            pa_mix_info *m = streams + k;
+            m->linear[channel].f = pa_sw_volume_to_linear(m->volume.values[channel]);
+        }
+    }
+}
+
+static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) {
+    unsigned channel;
+
+    pa_assert(linear);
+    pa_assert(volume);
+
+    for (channel = 0; channel < volume->channels; channel++)
+        linear[channel] = pa_sw_volume_to_linear(volume->values[channel]);
+}
+
+size_t pa_mix(
+        pa_mix_info streams[],
+        unsigned nstreams,
+        void *data,
+        size_t length,
+        const pa_sample_spec *spec,
+        const pa_cvolume *volume,
+        pa_bool_t mute) {
+
+    pa_cvolume full_volume;
+    unsigned k;
+    size_t d = 0;
+
+    pa_assert(streams);
+    pa_assert(data);
+    pa_assert(length);
+    pa_assert(spec);
+
+    if (!volume)
+        volume = pa_cvolume_reset(&full_volume, spec->channels);
+
+    for (k = 0; k < nstreams; k++)
+        streams[k].ptr = (uint8_t*) pa_memblock_acquire(streams[k].chunk.memblock) + streams[k].chunk.index;
+
+    switch (spec->format) {
+
+        case PA_SAMPLE_S16NE:{
+            unsigned channel = 0;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_stream_volumes(streams, nstreams, spec);
+            calc_linear_integer_volume(linear, volume);
+
+            for (d = 0;; d += sizeof(int16_t)) {
+                int32_t sum = 0;
+                unsigned i;
+
+                if (PA_UNLIKELY(d >= length))
+                    goto finish;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int32_t v, cv = m->linear[channel].i;
+
+                    if (PA_UNLIKELY(d >= m->chunk.length))
+                        goto finish;
+
+                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+                        v = 0;
+                    else {
+                        v = *((int16_t*) m->ptr);
+                        v = (v * cv) / 0x10000;
+                    }
+
+                    sum += v;
+                    m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+                }
+
+                sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+                sum = (sum * linear[channel]) / 0x10000;
+                *((int16_t*) data) = (int16_t) sum;
+
+                data = (uint8_t*) data + sizeof(int16_t);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_S16RE:{
+            unsigned channel = 0;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_stream_volumes(streams, nstreams, spec);
+            calc_linear_integer_volume(linear, volume);
+
+            for (d = 0;; d += sizeof(int16_t)) {
+                int32_t sum = 0;
+                unsigned i;
+
+                if (PA_UNLIKELY(d >= length))
+                    goto finish;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int32_t v, cv = m->linear[channel].i;
+
+                    if (PA_UNLIKELY(d >= m->chunk.length))
+                        goto finish;
+
+                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+                        v = 0;
+                    else {
+                        v = PA_INT16_SWAP(*((int16_t*) m->ptr));
+                        v = (v * cv) / 0x10000;
+                    }
+
+                    sum += v;
+                    m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
+                }
+
+                sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+                sum = (sum * linear[channel]) / 0x10000;
+                *((int16_t*) data) = PA_INT16_SWAP((int16_t) sum);
+
+                data = (uint8_t*) data + sizeof(int16_t);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_S32NE:{
+            unsigned channel = 0;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_stream_volumes(streams, nstreams, spec);
+            calc_linear_integer_volume(linear, volume);
+
+            for (d = 0;; d += sizeof(int32_t)) {
+                int64_t sum = 0;
+                unsigned i;
+
+                if (PA_UNLIKELY(d >= length))
+                    goto finish;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int64_t v;
+                    int32_t cv = m->linear[channel].i;
+
+                    if (PA_UNLIKELY(d >= m->chunk.length))
+                        goto finish;
+
+                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+                        v = 0;
+                    else {
+                        v = *((int32_t*) m->ptr);
+                        v = (v * cv) / 0x10000;
+                    }
+
+                    sum += v;
+                    m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+                }
+
+                sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+                sum = (sum * linear[channel]) / 0x10000;
+                *((int32_t*) data) = (int32_t) sum;
+
+                data = (uint8_t*) data + sizeof(int32_t);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_S32RE:{
+            unsigned channel = 0;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_stream_volumes(streams, nstreams, spec);
+            calc_linear_integer_volume(linear, volume);
+
+            for (d = 0;; d += sizeof(int32_t)) {
+                int64_t sum = 0;
+                unsigned i;
+
+                if (PA_UNLIKELY(d >= length))
+                    goto finish;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int64_t v;
+                    int32_t cv = m->linear[channel].i;
+
+                    if (PA_UNLIKELY(d >= m->chunk.length))
+                        goto finish;
+
+                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+                        v = 0;
+                    else {
+                        v = PA_INT32_SWAP(*((int32_t*) m->ptr));
+                        v = (v * cv) / 0x10000;
+                    }
+
+                    sum += v;
+                    m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+                }
+
+                sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+                sum = (sum * linear[channel]) / 0x10000;
+                *((int32_t*) data) = PA_INT32_SWAP((int32_t) sum);
+
+                data = (uint8_t*) data + sizeof(int32_t);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_U8: {
+            unsigned channel = 0;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_stream_volumes(streams, nstreams, spec);
+            calc_linear_integer_volume(linear, volume);
+
+            for (d = 0;; d ++) {
+                int32_t sum = 0;
+                unsigned i;
+
+                if (PA_UNLIKELY(d >= length))
+                    goto finish;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int32_t v, cv = m->linear[channel].i;
+
+                    if (PA_UNLIKELY(d >= m->chunk.length))
+                        goto finish;
+
+                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+                        v = 0;
+                    else {
+                        v = (int32_t) *((uint8_t*) m->ptr) - 0x80;
+                        v = (v * cv) / 0x10000;
+                    }
+
+                    sum += v;
+                    m->ptr = (uint8_t*) m->ptr + 1;
+                }
+
+                sum = (sum * linear[channel]) / 0x10000;
+                sum = PA_CLAMP_UNLIKELY(sum, -0x80, 0x7F);
+                *((uint8_t*) data) = (uint8_t) (sum + 0x80);
+
+                data = (uint8_t*) data + 1;
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_ULAW: {
+            unsigned channel = 0;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_stream_volumes(streams, nstreams, spec);
+            calc_linear_integer_volume(linear, volume);
+
+            for (d = 0;; d ++) {
+                int32_t sum = 0;
+                unsigned i;
+
+                if (PA_UNLIKELY(d >= length))
+                    goto finish;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int32_t v, cv = m->linear[channel].i;
+
+                    if (PA_UNLIKELY(d >= m->chunk.length))
+                        goto finish;
+
+                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+                        v = 0;
+                    else {
+                        v = (int32_t) st_ulaw2linear16(*((uint8_t*) m->ptr));
+                        v = (v * cv) / 0x10000;
+                    }
+
+                    sum += v;
+                    m->ptr = (uint8_t*) m->ptr + 1;
+                }
+
+                sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+                sum = (sum * linear[channel]) / 0x10000;
+                *((uint8_t*) data) = (uint8_t) st_14linear2ulaw(sum >> 2);
+
+                data = (uint8_t*) data + 1;
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_ALAW: {
+            unsigned channel = 0;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_stream_volumes(streams, nstreams, spec);
+            calc_linear_integer_volume(linear, volume);
+
+            for (d = 0;; d ++) {
+                int32_t sum = 0;
+                unsigned i;
+
+                if (PA_UNLIKELY(d >= length))
+                    goto finish;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int32_t v, cv = m->linear[channel].i;
+
+                    if (PA_UNLIKELY(d >= m->chunk.length))
+                        goto finish;
+
+                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+                        v = 0;
+                    else {
+                        v = (int32_t) st_alaw2linear16(*((uint8_t*) m->ptr));
+                        v = (v * cv) / 0x10000;
+                    }
+
+                    sum += v;
+                    m->ptr = (uint8_t*) m->ptr + 1;
+                }
+
+                sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
+                sum = (sum * linear[channel]) / 0x10000;
+                *((uint8_t*) data) = (uint8_t) st_13linear2alaw(sum >> 3);
+
+                data = (uint8_t*) data + 1;
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32NE: {
+            unsigned channel = 0;
+            float linear[PA_CHANNELS_MAX];
+
+            calc_linear_float_stream_volumes(streams, nstreams, spec);
+            calc_linear_float_volume(linear, volume);
+
+            for (d = 0;; d += sizeof(float)) {
+                float sum = 0;
+                unsigned i;
+
+                if (PA_UNLIKELY(d >= length))
+                    goto finish;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    float v, cv = m->linear[channel].f;
+
+                    if (PA_UNLIKELY(d >= m->chunk.length))
+                        goto finish;
+
+                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+                        v = 0;
+                    else {
+                        v = *((float*) m->ptr);
+                        v *= cv;
+                    }
+
+                    sum += v;
+                    m->ptr = (uint8_t*) m->ptr + sizeof(float);
+                }
+
+                sum *= linear[channel];
+                *((float*) data) = sum;
+
+                data = (uint8_t*) data + sizeof(float);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32RE: {
+            unsigned channel = 0;
+            float linear[PA_CHANNELS_MAX];
+
+            calc_linear_float_stream_volumes(streams, nstreams, spec);
+            calc_linear_float_volume(linear, volume);
+
+            for (d = 0;; d += sizeof(float)) {
+                float sum = 0;
+                unsigned i;
+
+                if (PA_UNLIKELY(d >= length))
+                    goto finish;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    float v, cv = m->linear[channel].f;
+
+                    if (PA_UNLIKELY(d >= m->chunk.length))
+                        goto finish;
+
+                    if (PA_UNLIKELY(cv <= 0) || PA_UNLIKELY(!!mute) || PA_UNLIKELY(linear[channel] <= 0))
+                        v = 0;
+                    else {
+                        uint32_t z = *(uint32_t*) m->ptr;
+                        z = PA_UINT32_SWAP(z);
+                        v = *((float*) &z);
+                        v *= cv;
+                    }
+
+                    sum += v;
+                    m->ptr = (uint8_t*) m->ptr + sizeof(float);
+                }
+
+                sum *= linear[channel];
+                *((uint32_t*) data) = PA_UINT32_SWAP(*(uint32_t*) &sum);
+
+                data = (uint8_t*) data + sizeof(float);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        default:
+            pa_log_error("ERROR: Unable to mix audio data of format %s.", pa_sample_format_to_string(spec->format));
+            pa_assert_not_reached();
+    }
+
+finish:
+
+    for (k = 0; k < nstreams; k++)
+        pa_memblock_release(streams[k].chunk.memblock);
+
+    return d;
+}
+
+
+void pa_volume_memchunk(
+        pa_memchunk*c,
+        const pa_sample_spec *spec,
+        const pa_cvolume *volume) {
+
+    void *ptr;
+
+    pa_assert(c);
+    pa_assert(spec);
+    pa_assert(c->length % pa_frame_size(spec) == 0);
+    pa_assert(volume);
+
+    if (pa_memblock_is_silence(c->memblock))
+        return;
+
+    if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM))
+        return;
+
+    if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_MUTED)) {
+        pa_silence_memchunk(c, spec);
+        return;
+    }
+
+    ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
+
+    switch (spec->format) {
+
+        case PA_SAMPLE_S16NE: {
+            int16_t *d;
+            size_t n;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+                int32_t t;
+
+                t = (int32_t)(*d);
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+                *d = (int16_t) t;
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_S16RE: {
+            int16_t *d;
+            size_t n;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            for (channel = 0, d = ptr, n = c->length/sizeof(int16_t); n > 0; d++, n--) {
+                int32_t t;
+
+                t = (int32_t)(PA_INT16_SWAP(*d));
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+                *d = PA_INT16_SWAP((int16_t) t);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_S32NE: {
+            int32_t *d;
+            size_t n;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            for (channel = 0, d = ptr, n = c->length/sizeof(int32_t); n > 0; d++, n--) {
+                int64_t t;
+
+                t = (int64_t)(*d);
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+                *d = (int32_t) t;
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_S32RE: {
+            int32_t *d;
+            size_t n;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            for (channel = 0, d = ptr, n = c->length/sizeof(int32_t); n > 0; d++, n--) {
+                int64_t t;
+
+                t = (int64_t)(PA_INT32_SWAP(*d));
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+                *d = PA_INT32_SWAP((int32_t) t);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_U8: {
+            uint8_t *d;
+            size_t n;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
+                int32_t t;
+
+                t = (int32_t) *d - 0x80;
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
+                *d = (uint8_t) (t + 0x80);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_ULAW: {
+            uint8_t *d;
+            size_t n;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
+                int32_t t;
+
+                t = (int32_t) st_ulaw2linear16(*d);
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+                *d = (uint8_t) st_14linear2ulaw(t >> 2);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_ALAW: {
+            uint8_t *d;
+            size_t n;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            for (channel = 0, d = ptr, n = c->length; n > 0; d++, n--) {
+                int32_t t;
+
+                t = (int32_t) st_alaw2linear16(*d);
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
+                *d = (uint8_t) st_13linear2alaw(t >> 3);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32NE: {
+            float *d;
+            int skip;
+            unsigned n;
+            unsigned channel;
+
+            d = ptr;
+            skip = spec->channels * sizeof(float);
+            n = c->length/sizeof(float)/spec->channels;
+
+            for (channel = 0; channel < spec->channels; channel ++) {
+                float v, *t;
+
+                if (PA_UNLIKELY(volume->values[channel] == PA_VOLUME_NORM))
+                    continue;
+
+                v = (float) pa_sw_volume_to_linear(volume->values[channel]);
+                t = d + channel;
+                oil_scalarmult_f32(t, skip, t, skip, &v, n);
+            }
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32RE: {
+            uint32_t *d;
+            size_t n;
+            unsigned channel;
+            float linear[PA_CHANNELS_MAX];
+
+            calc_linear_float_volume(linear, volume);
+
+            for (channel = 0, d = ptr, n = c->length/sizeof(float); n > 0; d++, n--) {
+                float t;
+                uint32_t z;
+
+                z = PA_UINT32_SWAP(*d);
+                t = *(float*) &z;
+                t *= linear[channel];
+                z = *(uint32_t*) &t;
+                *d = PA_UINT32_SWAP(z);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+
+        default:
+            pa_log_warn(" Unable to change volume of format %s.", pa_sample_format_to_string(spec->format));
+            /* If we cannot change the volume, we just don't do it */
+    }
+
+    pa_memblock_release(c->memblock);
+}
+
+size_t pa_frame_align(size_t l, const pa_sample_spec *ss) {
+    size_t fs;
+
+    pa_assert(ss);
+
+    fs = pa_frame_size(ss);
+
+    return (l/fs) * fs;
+}
+
+int pa_frame_aligned(size_t l, const pa_sample_spec *ss) {
+    size_t fs;
+
+    pa_assert(ss);
+
+    fs = pa_frame_size(ss);
+
+    return l % fs == 0;
+}
+
+void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n) {
+    unsigned c;
+    size_t fs;
+
+    pa_assert(src);
+    pa_assert(channels > 0);
+    pa_assert(dst);
+    pa_assert(ss > 0);
+    pa_assert(n > 0);
+
+    fs = ss * channels;
+
+    for (c = 0; c < channels; c++) {
+        unsigned j;
+        void *d;
+        const void *s;
+
+        s = src[c];
+        d = (uint8_t*) dst + c * ss;
+
+        for (j = 0; j < n; j ++) {
+            oil_memcpy(d, s, ss);
+            s = (uint8_t*) s + ss;
+            d = (uint8_t*) d + fs;
+        }
+    }
+}
+
+void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n) {
+    size_t fs;
+    unsigned c;
+
+    pa_assert(src);
+    pa_assert(dst);
+    pa_assert(channels > 0);
+    pa_assert(ss > 0);
+    pa_assert(n > 0);
+
+    fs = ss * channels;
+
+    for (c = 0; c < channels; c++) {
+        unsigned j;
+        const void *s;
+        void *d;
+
+        s = (uint8_t*) src + c * ss;
+        d = dst[c];
+
+        for (j = 0; j < n; j ++) {
+            oil_memcpy(d, s, ss);
+            s = (uint8_t*) s + fs;
+            d = (uint8_t*) d + ss;
+        }
+    }
+}
+
+static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) {
+    pa_memblock *b;
+    size_t length;
+    void *data;
+
+    pa_assert(pool);
+
+    length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX);
+
+    b = pa_memblock_new(pool, length);
+
+    data = pa_memblock_acquire(b);
+    memset(data, c, length);
+    pa_memblock_release(b);
+
+    pa_memblock_set_is_silence(b, TRUE);
+
+    return b;
+}
+
+void pa_silence_cache_init(pa_silence_cache *cache) {
+    pa_assert(cache);
+
+    memset(cache, 0, sizeof(pa_silence_cache));
+}
+
+void pa_silence_cache_done(pa_silence_cache *cache) {
+    pa_sample_format_t f;
+    pa_assert(cache);
+
+    for (f = 0; f < PA_SAMPLE_MAX; f++)
+        if (cache->blocks[f])
+            pa_memblock_unref(cache->blocks[f]);
+
+    memset(cache, 0, sizeof(pa_silence_cache));
+}
+
+pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length) {
+    pa_memblock *b;
+    size_t l;
+
+    pa_assert(cache);
+    pa_assert(pa_sample_spec_valid(spec));
+
+    if (!(b = cache->blocks[spec->format]))
+
+        switch (spec->format) {
+            case PA_SAMPLE_U8:
+                cache->blocks[PA_SAMPLE_U8] = b = silence_memblock_new(pool, 0x80);
+                break;
+            case PA_SAMPLE_S16LE:
+            case PA_SAMPLE_S16BE:
+            case PA_SAMPLE_S32LE:
+            case PA_SAMPLE_S32BE:
+            case PA_SAMPLE_FLOAT32LE:
+            case PA_SAMPLE_FLOAT32BE:
+                cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
+                cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
+                break;
+            case PA_SAMPLE_ALAW:
+                cache->blocks[PA_SAMPLE_ALAW] = b = silence_memblock_new(pool, 0xd5);
+                break;
+            case PA_SAMPLE_ULAW:
+                cache->blocks[PA_SAMPLE_ULAW] = b = silence_memblock_new(pool, 0xff);
+                break;
+            default:
+                pa_assert_not_reached();
+    }
+
+    pa_assert(b);
+
+    ret->memblock = pa_memblock_ref(b);
+
+    l = pa_memblock_get_length(b);
+    if (length > l || length == 0)
+        length = l;
+
+    ret->length = pa_frame_align(length, spec);
+    ret->index = 0;
+
+    return ret;
+}
+
+void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n) {
+    const float *s;
+    float *d;
+
+    s = src; d = dst;
+
+    if (format == PA_SAMPLE_FLOAT32NE) {
+
+        float minus_one = -1.0, plus_one = 1.0;
+        oil_clip_f32(d, dstr, s, sstr, n, &minus_one, &plus_one);
+
+    } else {
+        pa_assert(format == PA_SAMPLE_FLOAT32RE);
+
+        for (; n > 0; n--) {
+            float f;
+
+            f = PA_FLOAT32_SWAP(*s);
+            f = PA_CLAMP_UNLIKELY(f, -1.0, 1.0);
+            *d = PA_FLOAT32_SWAP(f);
+
+            s = (const float*) ((const uint8_t*) s + sstr);
+            d = (float*) ((uint8_t*) d + dstr);
+        }
+    }
+}
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
new file mode 100644 (file)
index 0000000..cef7075
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef foosampleutilhfoo
+#define foosampleutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/volume.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
+
+typedef struct pa_silence_cache {
+    pa_memblock* blocks[PA_SAMPLE_MAX];
+} pa_silence_cache;
+
+void pa_silence_cache_init(pa_silence_cache *cache);
+void pa_silence_cache_done(pa_silence_cache *cache);
+
+void *pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
+pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
+pa_memblock* pa_silence_memblock(pa_memblock *b, const pa_sample_spec *spec);
+
+pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length);
+
+typedef struct pa_mix_info {
+    pa_memchunk chunk;
+    pa_cvolume volume;
+    void *userdata;
+
+    /* The following fields are used internally by pa_mix(), should
+     * not be initialised by the caller of pa_mix(). */
+    void *ptr;
+    union {
+        int32_t i;
+        float f;
+    } linear[PA_CHANNELS_MAX];
+} pa_mix_info;
+
+size_t pa_mix(
+    pa_mix_info channels[],
+    unsigned nchannels,
+    void *data,
+    size_t length,
+    const pa_sample_spec *spec,
+    const pa_cvolume *volume,
+    pa_bool_t mute);
+
+void pa_volume_memchunk(
+    pa_memchunk*c,
+    const pa_sample_spec *spec,
+    const pa_cvolume *volume);
+
+size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
+
+int pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
+
+void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n);
+void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n);
+
+void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n);
+
+#endif
diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c
new file mode 100644 (file)
index 0000000..e7e1449
--- /dev/null
@@ -0,0 +1,59 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "endianmacros.h"
+
+#define INT16_FROM PA_INT16_FROM_BE
+#define INT16_TO PA_INT16_TO_BE
+
+#define INT32_FROM PA_INT32_FROM_BE
+#define INT32_TO PA_INT32_TO_BE
+
+#define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne
+#define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne
+
+#define pa_sconv_s16le_to_float32re pa_sconv_s16be_to_float32re
+#define pa_sconv_s16le_from_float32re pa_sconv_s16be_from_float32re
+
+#define pa_sconv_s32le_to_float32ne pa_sconv_s32be_to_float32ne
+#define pa_sconv_s32le_from_float32ne pa_sconv_s32be_from_float32ne
+
+#define pa_sconv_s32le_to_float32re pa_sconv_s32be_to_float32re
+#define pa_sconv_s32le_from_float32re pa_sconv_s32be_from_float32re
+
+#define pa_sconv_s32le_to_s16ne pa_sconv_s32be_to_s16ne
+#define pa_sconv_s32le_from_s16ne pa_sconv_s32be_from_s16ne
+
+#define pa_sconv_s32le_to_s16re pa_sconv_s32be_to_s16re
+#define pa_sconv_s32le_from_s16re pa_sconv_s32be_from_s16re
+
+#ifdef WORDS_BIGENDIAN
+#define SWAP_WORDS 0
+#else
+#define SWAP_WORDS 1
+#endif
+
+#include "sconv-s16le.h"
+#include "sconv-s16le.c"
diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h
new file mode 100644 (file)
index 0000000..6058081
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef foosconv_s16befoo
+#define foosconv_s16befoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+void pa_sconv_s16be_to_float32ne(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, int16_t *b);
+void pa_sconv_s16be_to_float32re(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16be_from_float32re(unsigned n, const float *a, int16_t *b);
+
+void pa_sconv_s32be_to_float32ne(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32be_from_float32ne(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s32be_to_float32re(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32be_from_float32re(unsigned n, const float *a, int32_t *b);
+
+void pa_sconv_s32be_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32be_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s32be_to_s16re(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32be_from_s16re(unsigned n, const int16_t *a, int32_t *b);
+
+#ifdef WORDS_BIGENDIAN
+#define pa_sconv_float32be_to_s16ne pa_sconv_s16be_from_float32ne
+#define pa_sconv_float32be_from_s16ne pa_sconv_s16be_to_float32ne
+#define pa_sconv_float32le_to_s16ne pa_sconv_s16be_from_float32re
+#define pa_sconv_float32le_from_s16ne pa_sconv_s16be_to_float32re
+
+#define pa_sconv_float32be_to_s32ne pa_sconv_s32be_from_float32ne
+#define pa_sconv_float32be_from_s32ne pa_sconv_s32be_to_float32ne
+#define pa_sconv_float32le_to_s32ne pa_sconv_s32be_from_float32re
+#define pa_sconv_float32le_from_s32ne pa_sconv_s32be_to_float32re
+
+#define pa_sconv_s16be_to_s32ne pa_sconv_s32be_from_s16ne
+#define pa_sconv_s16be_from_s32ne pa_sconv_s32be_to_s16ne
+#define pa_sconv_s16le_to_s32ne pa_sconv_s32be_from_s16re
+#define pa_sconv_s16le_from_s32ne pa_sconv_s32be_to_s16re
+#endif
+
+#endif
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
new file mode 100644 (file)
index 0000000..41670f2
--- /dev/null
@@ -0,0 +1,249 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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
+
+/* Despite the name of this file we implement S32 handling here, too. */
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <liboil/liboilfuncs.h>
+
+#include <pulsecore/sconv.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+#include "endianmacros.h"
+
+#include "sconv-s16le.h"
+
+#ifndef INT16_FROM
+#define INT16_FROM PA_INT16_FROM_LE
+#endif
+
+#ifndef INT16_TO
+#define INT16_TO PA_INT16_TO_LE
+#endif
+
+#ifndef INT32_FROM
+#define INT32_FROM PA_INT32_FROM_LE
+#endif
+
+#ifndef INT32_TO
+#define INT32_TO PA_INT32_TO_LE
+#endif
+
+#ifndef SWAP_WORDS
+#ifdef WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#else
+#define SWAP_WORDS 0
+#endif
+#endif
+
+void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+#if SWAP_WORDS == 1
+
+    for (; n > 0; n--) {
+        int16_t s = *(a++);
+        *(b++) = ((float) INT16_FROM(s))/0x7FFF;
+    }
+
+#else
+{
+    static const double add = 0, factor = 1.0/0x7FFF;
+    oil_scaleconv_f32_s16(b, a, n, &add, &factor);
+}
+#endif
+}
+
+void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+#if SWAP_WORDS == 1
+
+    for (; n > 0; n--) {
+        int32_t s = *(a++);
+        *(b++) = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
+    }
+
+#else
+{
+    static const double add = 0, factor = 1.0/0x7FFFFFFF;
+    oil_scaleconv_f32_s32(b, a, n, &add, &factor);
+}
+#endif
+}
+
+void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+#if SWAP_WORDS == 1
+
+    for (; n > 0; n--) {
+        int16_t s;
+        float v = *(a++);
+
+        v = PA_CLAMP_UNLIKELY(v, -1, 1);
+        s = (int16_t) (v * 0x7FFF);
+        *(b++) = INT16_TO(s);
+    }
+
+#else
+{
+    static const double add = 0, factor = 0x7FFF;
+    oil_scaleconv_s16_f32(b, a, n, &add, &factor);
+}
+#endif
+}
+
+void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+#if SWAP_WORDS == 1
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = *(a++);
+
+        v = PA_CLAMP_UNLIKELY(v, -1, 1);
+        s = (int32_t) ((double) v * (double) 0x7FFFFFFF);
+        *(b++) = INT32_TO(s);
+    }
+
+#else
+{
+    static const double add = 0, factor = 0x7FFFFFFF;
+    oil_scaleconv_s32_f32(b, a, n, &add, &factor);
+}
+#endif
+}
+
+void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int16_t s = *(a++);
+        float k = ((float) INT16_FROM(s))/0x7FFF;
+        uint32_t *j = (uint32_t*) &k;
+        *j = PA_UINT32_SWAP(*j);
+        *(b++) = k;
+    }
+}
+
+void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = *(a++);
+        float k = (float) (((double) INT32_FROM(s))/0x7FFFFFFF);
+        uint32_t *j = (uint32_t*) &k;
+        *j = PA_UINT32_SWAP(*j);
+        *(b++) = k;
+    }
+}
+
+void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int16_t s;
+        float v = *(a++);
+        uint32_t *j = (uint32_t*) &v;
+        *j = PA_UINT32_SWAP(*j);
+        v = PA_CLAMP_UNLIKELY(v, -1, 1);
+        s = (int16_t) (v * 0x7FFF);
+        *(b++) = INT16_TO(s);
+    }
+}
+
+void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = *(a++);
+        uint32_t *j = (uint32_t*) &v;
+        *j = PA_UINT32_SWAP(*j);
+        v = PA_CLAMP_UNLIKELY(v, -1, 1);
+        s = (int32_t) ((double) v * 0x7FFFFFFF);
+        *(b++) = INT32_TO(s);
+    }
+}
+
+void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t*a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        *b = (int16_t) (INT32_FROM(*a) >> 16);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s32le_to_s16re(unsigned n, const int32_t*a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int16_t s = (int16_t) (INT32_FROM(*a) >> 16);
+        *b = PA_UINT32_SWAP(s);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        *b = INT32_TO(((int32_t) *a) << 16);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = ((int32_t) PA_UINT16_SWAP(*a)) << 16;
+        *b = INT32_TO(s);
+        a++;
+        b++;
+    }
+}
diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h
new file mode 100644 (file)
index 0000000..8d4c87c
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef foosconv_s16lefoo
+#define foosconv_s16lefoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+void pa_sconv_s16le_to_float32ne(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, int16_t *b);
+void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b);
+void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b);
+
+void pa_sconv_s32le_to_float32ne(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b);
+void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b);
+void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b);
+
+void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
+void pa_sconv_s32le_to_s16re(unsigned n, const int32_t *a, int16_t *b);
+void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b);
+
+#ifndef WORDS_BIGENDIAN
+#define pa_sconv_float32be_to_s16ne pa_sconv_s16le_from_float32re
+#define pa_sconv_float32be_from_s16ne pa_sconv_s16le_to_float32re
+#define pa_sconv_float32le_to_s16ne pa_sconv_s16le_from_float32ne
+#define pa_sconv_float32le_from_s16ne pa_sconv_s16le_to_float32ne
+
+#define pa_sconv_float32be_to_s32ne pa_sconv_s32le_from_float32re
+#define pa_sconv_float32be_from_s32ne pa_sconv_s32le_to_float32re
+#define pa_sconv_float32le_to_s32ne pa_sconv_s32le_from_float32ne
+#define pa_sconv_float32le_from_s32ne pa_sconv_s32le_to_float32ne
+
+#define pa_sconv_s16be_to_s32ne pa_sconv_s32le_from_s16re
+#define pa_sconv_s16be_from_s32ne pa_sconv_s32le_to_s16re
+#define pa_sconv_s16le_to_s32ne pa_sconv_s32le_from_s16ne
+#define pa_sconv_s16le_from_s32ne pa_sconv_s32le_to_s16ne
+#endif
+
+#endif
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
new file mode 100644 (file)
index 0000000..581e468
--- /dev/null
@@ -0,0 +1,269 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <liboil/liboilfuncs.h>
+#include <liboil/liboil.h>
+
+#include <pulsecore/g711.h>
+#include <pulsecore/macro.h>
+
+#include "endianmacros.h"
+#include "sconv-s16le.h"
+#include "sconv-s16be.h"
+
+#include "sconv.h"
+
+/* u8 */
+static void u8_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+    static const double add = -1, factor = 1.0/128.0;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    oil_scaleconv_f32_u8(b, a, n, &add, &factor);
+}
+
+static void u8_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+    static const double add = 128, factor = 127.0;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    oil_scaleconv_u8_f32(b, a, n, &add, &factor);
+}
+
+static void u8_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+    static const int16_t add = -0x80, factor = 0x100;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    oil_conv_s16_u8(b, 2, a, 1, n);
+    oil_scalaradd_s16(b, 2, b, 2, &add, n);
+    oil_scalarmult_s16(b, 2, b, 2, &factor, n);
+}
+
+static void u8_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = (uint8_t) (*a / 0x100 + 0x80);
+}
+
+/* float32 */
+
+static void float32ne_to_float32ne(unsigned n, const float *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    oil_memcpy(b, a, sizeof(float) * n);
+}
+
+static void float32re_to_float32ne(unsigned n, const float *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *((uint32_t *) b) = PA_UINT32_SWAP(*((uint32_t *) a));
+}
+
+/* s16 */
+
+static void s16ne_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    oil_memcpy(b, a, sizeof(int16_t) * n);
+}
+
+static void s16re_to_s16ne(unsigned n, const int16_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = PA_UINT16_SWAP(*a);
+}
+
+/* ulaw */
+
+static void ulaw_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--)
+        *(b++) = (float) st_ulaw2linear16(*(a++)) / 0x8000;
+}
+
+static void ulaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        float v = *(a++);
+        v = PA_CLAMP_UNLIKELY(v, -1, 1);
+        v *= 0x1FFF;
+        *(b++) = st_14linear2ulaw((int16_t) v);
+    }
+}
+
+static void ulaw_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = st_ulaw2linear16(*a);
+}
+
+static void ulaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = st_14linear2ulaw(*a >> 2);
+}
+
+/* alaw */
+
+static void alaw_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = (float) st_alaw2linear16(*a) / 0x8000;
+}
+
+static void alaw_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++) {
+        float v = *a;
+        v = PA_CLAMP_UNLIKELY(v, -1, 1);
+        v *= 0xFFF;
+        *b = st_13linear2alaw((int16_t) v);
+    }
+}
+
+static void alaw_to_s16ne(unsigned n, const int8_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = st_alaw2linear16(*a);
+}
+
+static void alaw_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--, a++, b++)
+        *b = st_13linear2alaw(*a >> 3);
+}
+
+pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
+
+    static const pa_convert_func_t table[] = {
+        [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_to_float32ne,
+        [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_to_float32ne,
+        [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_to_float32ne,
+        [PA_SAMPLE_S16LE]     = (pa_convert_func_t) pa_sconv_s16le_to_float32ne,
+        [PA_SAMPLE_S16BE]     = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
+        [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
+        [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
+        [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
+        [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
+    };
+
+    pa_assert(f >= 0);
+    pa_assert(f < PA_SAMPLE_MAX);
+
+    return table[f];
+}
+
+pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
+
+    static const pa_convert_func_t table[] = {
+        [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_from_float32ne,
+        [PA_SAMPLE_S16LE]     = (pa_convert_func_t) pa_sconv_s16le_from_float32ne,
+        [PA_SAMPLE_S16BE]     = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
+        [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
+        [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
+        [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
+        [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
+        [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_from_float32ne,
+        [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_from_float32ne
+    };
+
+    pa_assert(f >= 0);
+    pa_assert(f < PA_SAMPLE_MAX);
+
+    return table[f];
+}
+
+pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
+
+    static const pa_convert_func_t table[] = {
+        [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_to_s16ne,
+        [PA_SAMPLE_S16NE]     = (pa_convert_func_t) s16ne_to_s16ne,
+        [PA_SAMPLE_S16RE]     = (pa_convert_func_t) s16re_to_s16ne,
+        [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_to_s16ne,
+        [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
+        [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
+        [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
+        [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_to_s16ne,
+        [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_to_s16ne
+    };
+
+    pa_assert(f >= 0);
+    pa_assert(f < PA_SAMPLE_MAX);
+
+    return table[f];
+}
+
+pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
+
+    static const pa_convert_func_t table[] = {
+        [PA_SAMPLE_U8]        = (pa_convert_func_t) u8_from_s16ne,
+        [PA_SAMPLE_S16NE]     = (pa_convert_func_t) s16ne_to_s16ne,
+        [PA_SAMPLE_S16RE]     = (pa_convert_func_t) s16re_to_s16ne,
+        [PA_SAMPLE_FLOAT32BE] = (pa_convert_func_t) pa_sconv_float32be_from_s16ne,
+        [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
+        [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
+        [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
+        [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_from_s16ne,
+        [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_from_s16ne,
+    };
+
+    pa_assert(f >= 0);
+    pa_assert(f < PA_SAMPLE_MAX);
+
+    return table[f];
+}
diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h
new file mode 100644 (file)
index 0000000..5971036
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef foosconvhfoo
+#define foosconvhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/sample.h>
+
+typedef void (*pa_convert_func_t)(unsigned n, const void *a, void *b);
+
+pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) PA_GCC_PURE;
+pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) PA_GCC_PURE;
+
+pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
+pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) PA_GCC_PURE;
+
+#endif
diff --git a/src/pulsecore/semaphore-posix.c b/src/pulsecore/semaphore-posix.c
new file mode 100644 (file)
index 0000000..7c9f859
--- /dev/null
@@ -0,0 +1,67 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pthread.h>
+#include <semaphore.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "semaphore.h"
+
+struct pa_semaphore {
+    sem_t sem;
+};
+
+pa_semaphore* pa_semaphore_new(unsigned value) {
+    pa_semaphore *s;
+
+    s = pa_xnew(pa_semaphore, 1);
+    pa_assert_se(sem_init(&s->sem, 0, value) == 0);
+    return s;
+}
+
+void pa_semaphore_free(pa_semaphore *s) {
+    pa_assert(s);
+    pa_assert_se(sem_destroy(&s->sem) == 0);
+    pa_xfree(s);
+}
+
+void pa_semaphore_post(pa_semaphore *s) {
+    pa_assert(s);
+    pa_assert_se(sem_post(&s->sem) == 0);
+}
+
+void pa_semaphore_wait(pa_semaphore *s) {
+    int ret;
+    pa_assert(s);
+
+    do {
+        ret = sem_wait(&s->sem);
+    } while (ret < 0 && errno == EINTR);
+
+    pa_assert(ret == 0);
+}
diff --git a/src/pulsecore/semaphore-win32.c b/src/pulsecore/semaphore-win32.c
new file mode 100644 (file)
index 0000000..41e0b8d
--- /dev/null
@@ -0,0 +1,63 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <windows.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+#include "semaphore.h"
+
+struct pa_semaphore
+{
+    HANDLE sema;
+};
+
+pa_semaphore* pa_semaphore_new(unsigned value) {
+    pa_semaphore *s;
+
+    s = pa_xnew(pa_semaphore, 1);
+
+    s->sema = CreateSemaphore(NULL, value, 32767, NULL);
+    pa_assert(s->sema != NULL);
+
+    return s;
+}
+
+void pa_semaphore_free(pa_semaphore *s) {
+    pa_assert(s);
+    CloseHandle(s->sema);
+    pa_xfree(s);
+}
+
+void pa_semaphore_post(pa_semaphore *s) {
+    pa_assert(s);
+    ReleaseSemaphore(s->sema, 1, NULL);
+}
+
+void pa_semaphore_wait(pa_semaphore *s) {
+    pa_assert(s);
+    WaitForSingleObject(s->sema, INFINITE);
+}
diff --git a/src/pulsecore/semaphore.h b/src/pulsecore/semaphore.h
new file mode 100644 (file)
index 0000000..850ae81
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef foopulsesemaphorehfoo
+#define foopulsesemaphorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+typedef struct pa_semaphore pa_semaphore;
+
+pa_semaphore* pa_semaphore_new(unsigned value);
+void pa_semaphore_free(pa_semaphore *m);
+
+void pa_semaphore_post(pa_semaphore *m);
+void pa_semaphore_wait(pa_semaphore *m);
+
+#endif
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
new file mode 100644 (file)
index 0000000..298bf71
--- /dev/null
@@ -0,0 +1,385 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2006 Lennart Poettering
+    Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as
+    published by the Free Software Foundation; either version 2.1 of the
+    License, or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with PulseAudio; 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/random.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+
+#include "shm.h"
+
+#if defined(__linux__) && !defined(MADV_REMOVE)
+#define MADV_REMOVE 9
+#endif
+
+#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
+
+#ifdef __linux__
+/* On Linux we know that the shared memory blocks are files in
+ * /dev/shm. We can use that information to list all blocks and
+ * cleanup unused ones */
+#define SHM_PATH "/dev/shm/"
+#else
+#undef SHM_PATH
+#endif
+
+#define SHM_MARKER ((int) 0xbeefcafe)
+
+/* We now put this SHM marker at the end of each segment. It's
+ * optional, to not require a reboot when upgrading, though */
+struct shm_marker PA_GCC_PACKED {
+    pa_atomic_t marker; /* 0xbeefcafe */
+    pa_atomic_t pid;
+    uint64_t *_reserverd1;
+    uint64_t *_reserverd2;
+    uint64_t *_reserverd3;
+    uint64_t *_reserverd4;
+};
+
+static char *segment_name(char *fn, size_t l, unsigned id) {
+    pa_snprintf(fn, l, "/pulse-shm-%u", id);
+    return fn;
+}
+
+int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
+    char fn[32];
+    int fd = -1;
+
+    pa_assert(m);
+    pa_assert(size > 0);
+    pa_assert(size <= MAX_SHM_SIZE);
+    pa_assert(mode >= 0600);
+
+    /* Each time we create a new SHM area, let's first drop all stale
+     * ones */
+    pa_shm_cleanup();
+
+    /* Round up to make it aligned */
+    size = PA_ALIGN(size);
+
+    if (!shared) {
+        m->id = 0;
+        m->size = size;
+
+#ifdef MAP_ANONYMOUS
+        if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
+            pa_log("mmap() failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+#elif defined(HAVE_POSIX_MEMALIGN)
+        {
+            int r;
+
+            if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
+                pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
+                goto fail;
+            }
+        }
+#else
+        m->ptr = pa_xmalloc(m->size);
+#endif
+
+        m->do_unlink = FALSE;
+
+    } else {
+#ifdef HAVE_SHM_OPEN
+        struct shm_marker *marker;
+
+        pa_random(&m->id, sizeof(m->id));
+        segment_name(fn, sizeof(fn), m->id);
+
+        if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) {
+            pa_log("shm_open() failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+        m->size = size + PA_ALIGN(sizeof(struct shm_marker));
+
+        if (ftruncate(fd, m->size) < 0) {
+            pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+        if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+            pa_log("mmap() failed: %s", pa_cstrerror(errno));
+            goto fail;
+        }
+
+        /* We store our PID at the end of the shm block, so that we
+         * can check for dead shm segments later */
+        marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - PA_ALIGN(sizeof(struct shm_marker)));
+        pa_atomic_store(&marker->pid, (int) getpid());
+        pa_atomic_store(&marker->marker, SHM_MARKER);
+
+        pa_assert_se(close(fd) == 0);
+        m->do_unlink = TRUE;
+#else
+        return -1;
+#endif
+    }
+
+    m->shared = shared;
+
+    return 0;
+
+fail:
+
+#ifdef HAVE_SHM_OPEN
+    if (fd >= 0) {
+        shm_unlink(fn);
+        pa_close(fd);
+    }
+#endif
+
+    return -1;
+}
+
+void pa_shm_free(pa_shm *m) {
+    pa_assert(m);
+    pa_assert(m->ptr);
+    pa_assert(m->size > 0);
+
+#ifdef MAP_FAILED
+    pa_assert(m->ptr != MAP_FAILED);
+#endif
+
+    if (!m->shared) {
+#ifdef MAP_ANONYMOUS
+        if (munmap(m->ptr, m->size) < 0)
+            pa_log("munmap() failed: %s", pa_cstrerror(errno));
+#elif defined(HAVE_POSIX_MEMALIGN)
+        free(m->ptr);
+#else
+        pa_xfree(m->ptr);
+#endif
+    } else {
+#ifdef HAVE_SHM_OPEN
+        if (munmap(m->ptr, m->size) < 0)
+            pa_log("munmap() failed: %s", pa_cstrerror(errno));
+
+        if (m->do_unlink) {
+            char fn[32];
+
+            segment_name(fn, sizeof(fn), m->id);
+
+            if (shm_unlink(fn) < 0)
+                pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
+        }
+#else
+        /* We shouldn't be here without shm support */
+        pa_assert_not_reached();
+#endif
+    }
+
+    memset(m, 0, sizeof(*m));
+}
+
+void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
+    void *ptr;
+    size_t o, ps;
+
+    pa_assert(m);
+    pa_assert(m->ptr);
+    pa_assert(m->size > 0);
+    pa_assert(offset+size <= m->size);
+
+#ifdef MAP_FAILED
+    pa_assert(m->ptr != MAP_FAILED);
+#endif
+
+    /* You're welcome to implement this as NOOP on systems that don't
+     * support it */
+
+    /* Align this to multiples of the page size */
+    ptr = (uint8_t*) m->ptr + offset;
+    o = (uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr);
+
+    if (o > 0) {
+        ps = PA_PAGE_SIZE;
+        ptr = (uint8_t*) ptr + (ps - o);
+        size -= ps - o;
+    }
+
+#ifdef MADV_REMOVE
+    if (madvise(ptr, size, MADV_REMOVE) >= 0)
+        return;
+#endif
+
+#ifdef MADV_FREE
+    if (madvise(ptr, size, MADV_FREE) >= 0)
+        return;
+#endif
+
+#ifdef MADV_DONTNEED
+    pa_assert_se(madvise(ptr, size, MADV_DONTNEED) == 0);
+#elif defined(POSIX_MADV_DONTNEED)
+    pa_assert_se(posix_madvise(ptr, size, POSIX_MADV_DONTNEED) == 0);
+#endif
+}
+
+#ifdef HAVE_SHM_OPEN
+
+int pa_shm_attach_ro(pa_shm *m, unsigned id) {
+    char fn[32];
+    int fd = -1;
+    struct stat st;
+
+    pa_assert(m);
+
+    segment_name(fn, sizeof(fn), m->id = id);
+
+    if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
+        if (errno != EACCES)
+            pa_log("shm_open() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (fstat(fd, &st) < 0) {
+        pa_log("fstat() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (st.st_size <= 0 ||
+        st.st_size > (off_t) (MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker))) ||
+        PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
+        pa_log("Invalid shared memory segment size");
+        goto fail;
+    }
+
+    m->size = st.st_size;
+
+    if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+        pa_log("mmap() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    m->do_unlink = 0;
+    m->shared = 1;
+
+    pa_assert_se(pa_close(fd) == 0);
+
+    return 0;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return -1;
+}
+
+#else /* HAVE_SHM_OPEN */
+
+int pa_shm_attach_ro(pa_shm *m, unsigned id) {
+    return -1;
+}
+
+#endif /* HAVE_SHM_OPEN */
+
+int pa_shm_cleanup(void) {
+
+#ifdef HAVE_SHM_OPEN
+#ifdef SHM_PATH
+    DIR *d;
+    struct dirent *de;
+
+    if (!(d = opendir(SHM_PATH))) {
+        pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    while ((de = readdir(d))) {
+        pa_shm seg;
+        unsigned id;
+        pid_t pid;
+        char fn[128];
+        struct shm_marker *m;
+
+        if (strncmp(de->d_name, "pulse-shm-", 10))
+            continue;
+
+        if (pa_atou(de->d_name + 10, &id) < 0)
+            continue;
+
+        if (pa_shm_attach_ro(&seg, id) < 0)
+            continue;
+
+        if (seg.size < PA_ALIGN(sizeof(struct shm_marker))) {
+            pa_shm_free(&seg);
+            continue;
+        }
+
+        m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - PA_ALIGN(sizeof(struct shm_marker)));
+
+        if (pa_atomic_load(&m->marker) != SHM_MARKER) {
+            pa_shm_free(&seg);
+            continue;
+        }
+
+        if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
+            pa_shm_free(&seg);
+            continue;
+        }
+
+        if (kill(pid, 0) == 0 || errno != ESRCH) {
+            pa_shm_free(&seg);
+            continue;
+        }
+
+        pa_shm_free(&seg);
+
+        /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
+        segment_name(fn, sizeof(fn), id);
+
+        if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
+            pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
+    }
+
+    closedir(d);
+#endif /* SHM_PATH */
+#endif /* HAVE_SHM_OPEN */
+
+    return 0;
+}
diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h
new file mode 100644 (file)
index 0000000..c2adbd0
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef foopulseshmhfoo
+#define foopulseshmhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulsecore/macro.h>
+
+typedef struct pa_shm {
+    unsigned id;
+    void *ptr;
+    size_t size;
+    pa_bool_t do_unlink:1;
+    pa_bool_t shared:1;
+} pa_shm;
+
+int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode);
+int pa_shm_attach_ro(pa_shm *m, unsigned id);
+
+void pa_shm_punch(pa_shm *m, size_t offset, size_t size);
+
+void pa_shm_free(pa_shm *m);
+
+int pa_shm_cleanup(void);
+
+#endif
diff --git a/src/pulsecore/shmasyncq.c b/src/pulsecore/shmasyncq.c
new file mode 100644 (file)
index 0000000..eac56ad
--- /dev/null
@@ -0,0 +1,220 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulsecore/atomic.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulse/xmalloc.h>
+
+#include "fdsem.h"
+
+/* For debugging purposes we can define _Y to put and extra thread
+ * yield between each operation. */
+
+/* #define PROFILE */
+
+#ifdef PROFILE
+#define _Y pa_thread_yield()
+#else
+#define _Y do { } while(0)
+#endif
+
+
+struct pa_shmasyncq {
+    pa_fdsem *read_fdsem, *write_fdsem;
+    pa_shmasyncq_data *data;
+};
+
+static int is_power_of_two(unsigned size) {
+    return !(size & (size - 1));
+}
+
+static int reduce(pa_shmasyncq *l, int value) {
+    return value & (unsigned) (l->n_elements - 1);
+}
+
+static pa_atomic_t* get_cell(pa_shmasyncq *l, unsigned i) {
+    pa_assert(i < l->data->n_elements);
+
+    return (pa_atomic_t*) ((uint8*t) l->data + PA_ALIGN(sizeof(pa_shmasyncq_data)) + i * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))
+}
+
+static void *get_cell_data(pa_atomic_t *a) {
+    return (uint8_t*) a + PA_ALIGN(sizeof(atomic_t));
+}
+
+pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]) {
+    pa_shmasyncq *l;
+
+    pa_assert(n_elements > 0);
+    pa_assert(is_power_of_two(n_elements));
+    pa_assert(element_size > 0);
+    pa_assert(data);
+    pa_assert(fd);
+
+    l = pa_xnew(pa_shmasyncq, 1);
+
+    l->data = data;
+    memset(data, 0, PA_SHMASYNCQ_SIZE(n_elements, element_size));
+
+    l->data->n_elements = n_elements;
+    l->data->element_size = element_size;
+
+    if (!(l->read_fdsem = pa_fdsem_new_shm(&d->read_fdsem_data, &fd[0]))) {
+        pa_xfree(l);
+        return NULL;
+    }
+
+    if (!(l->write_fdsem = pa_fdsem_new(&d->write_fdsem_data, &fd[1]))) {
+        pa_fdsem_free(l->read_fdsem);
+        pa_xfree(l);
+        return NULL;
+    }
+
+    return l;
+}
+
+void pa_shmasyncq_free(pa_shmasyncq *l, pa_free_cb_t free_cb) {
+    pa_assert(l);
+
+    if (free_cb) {
+        void *p;
+
+        while ((p = pa_shmasyncq_pop(l, 0)))
+            free_cb(p);
+    }
+
+    pa_fdsem_free(l->read_fdsem);
+    pa_fdsem_free(l->write_fdsem);
+    pa_xfree(l);
+}
+
+int pa_shmasyncq_push(pa_shmasyncq*l, void *p, int wait) {
+    int idx;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+    pa_assert(p);
+
+    cells = PA_SHMASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->write_idx);
+
+    if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
+
+        if (!wait)
+            return -1;
+
+/*         pa_log("sleeping on push"); */
+
+        do {
+            pa_fdsem_wait(l->read_fdsem);
+        } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
+    }
+
+    _Y;
+    l->write_idx++;
+
+    pa_fdsem_post(l->write_fdsem);
+
+    return 0;
+}
+
+void* pa_shmasyncq_pop(pa_shmasyncq*l, int wait) {
+    int idx;
+    void *ret;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+
+    cells = PA_SHMASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->read_idx);
+
+    if (!(ret = pa_atomic_ptr_load(&cells[idx]))) {
+
+        if (!wait)
+            return NULL;
+
+/*         pa_log("sleeping on pop"); */
+
+        do {
+            pa_fdsem_wait(l->write_fdsem);
+        } while (!(ret = pa_atomic_ptr_load(&cells[idx])));
+    }
+
+    pa_assert(ret);
+
+    /* Guaranteed to succeed if we only have a single reader */
+    pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL));
+
+    _Y;
+    l->read_idx++;
+
+    pa_fdsem_post(l->read_fdsem);
+
+    return ret;
+}
+
+int pa_shmasyncq_get_fd(pa_shmasyncq *q) {
+    pa_assert(q);
+
+    return pa_fdsem_get(q->write_fdsem);
+}
+
+int pa_shmasyncq_before_poll(pa_shmasyncq *l) {
+    int idx;
+    pa_atomic_ptr_t *cells;
+
+    pa_assert(l);
+
+    cells = PA_SHMASYNCQ_CELLS(l);
+
+    _Y;
+    idx = reduce(l, l->read_idx);
+
+    for (;;) {
+        if (pa_atomic_ptr_load(&cells[idx]))
+            return -1;
+
+        if (pa_fdsem_before_poll(l->write_fdsem) >= 0)
+            return 0;
+    }
+
+    return 0;
+}
+
+void pa_shmasyncq_after_poll(pa_shmasyncq *l) {
+    pa_assert(l);
+
+    pa_fdsem_after_poll(l->write_fdsem);
+}
diff --git a/src/pulsecore/shmasyncq.h b/src/pulsecore/shmasyncq.h
new file mode 100644 (file)
index 0000000..9845c39
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef foopulseshmasyncqhfoo
+#define foopulseshmasyncqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+
+#include <pulse/def.h>
+#include <pulsecore/macro.h>
+
+/* Similar to pa_asyncq, but stores data in a shared memory segment */
+
+
+struct pa_shmasyncq_data {
+    unsigned n_elements;
+    size_t element_size;
+    unsigned read_idx;
+    unsigned write_idx;
+    pa_fdsem_data read_fdsem_data, write_fdsem_data;
+};
+
+#define PA_SHMASYNCQ_DEFAULT_N_ELEMENTS 128
+#define PA_SHMASYNCQ_SIZE(n_elements, element_size) (PA_ALIGN(sizeof(pa_shmasyncq_data)) + (((n_elements) * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))))
+#define PA_SHMASYNCQ_DEFAULT_SIZE(element_size) PA_SHMASYNCQ_SIZE(PA_SHMASYNCQ_DEFAULT_N_ELEMENTS, element_size)
+
+typedef struct pa_shmasyncq pa_shmasyncq;
+
+pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]);
+void pa_shmasyncq_free(pa_shmasyncq* q, pa_free_cb_t free_cb);
+
+void* pa_shmasyncq_pop_begin(pa_shmasyncq *q, pa_bool_t wait);
+void pa_shmasyncq_pop_commit(pa_shmasyncq *q);
+
+int* pa_shmasyncq_push_begin(pa_shmasyncq *q, pa_bool_t wait);
+void pa_shmasyncq_push_commit(pa_shmasyncq *q);
+
+int pa_shmasyncq_get_fd(pa_shmasyncq *q);
+int pa_shmasyncq_before_poll(pa_shmasyncq *a);
+void pa_shmasyncq_after_poll(pa_shmasyncq *a);
+
+#endif
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
new file mode 100644 (file)
index 0000000..43e7bb2
--- /dev/null
@@ -0,0 +1,1202 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/play-memblockq.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+
+#include "sink-input.h"
+
+#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
+#define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE)
+
+static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);
+
+static void sink_input_free(pa_object *o);
+
+pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {
+    pa_assert(data);
+
+    memset(data, 0, sizeof(*data));
+    data->resample_method = PA_RESAMPLER_INVALID;
+    data->proplist = pa_proplist_new();
+
+    return data;
+}
+
+void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) {
+    pa_assert(data);
+
+    if ((data->sample_spec_is_set = !!spec))
+        data->sample_spec = *spec;
+}
+
+void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map) {
+    pa_assert(data);
+
+    if ((data->channel_map_is_set = !!map))
+        data->channel_map = *map;
+}
+
+void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
+    pa_assert(data);
+
+    if ((data->volume_is_set = !!volume))
+        data->volume = *volume;
+}
+
+void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
+    pa_assert(data);
+
+    data->muted_is_set = TRUE;
+    data->muted = !!mute;
+}
+
+void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
+    pa_assert(data);
+
+    pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_sink_input *i) {
+    pa_assert(i);
+
+    i->pop = NULL;
+    i->process_rewind = NULL;
+    i->update_max_rewind = NULL;
+    i->update_max_request = NULL;
+    i->update_sink_requested_latency = NULL;
+    i->update_sink_latency_range = NULL;
+    i->attach = NULL;
+    i->detach = NULL;
+    i->suspend = NULL;
+    i->moved = NULL;
+    i->kill = NULL;
+    i->get_latency = NULL;
+    i->state_change = NULL;
+}
+
+/* Called from main context */
+pa_sink_input* pa_sink_input_new(
+        pa_core *core,
+        pa_sink_input_new_data *data,
+        pa_sink_input_flags_t flags) {
+
+    pa_sink_input *i;
+    pa_resampler *resampler = NULL;
+    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+    pa_assert(core);
+    pa_assert(data);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data) < 0)
+        return NULL;
+
+    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+
+    if (!data->sink)
+        data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, TRUE);
+
+    pa_return_null_if_fail(data->sink);
+    pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED);
+    pa_return_null_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED));
+
+    if (!data->sample_spec_is_set)
+        data->sample_spec = data->sink->sample_spec;
+
+    pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
+
+    if (!data->channel_map_is_set) {
+        if (data->sink->channel_map.channels == data->sample_spec.channels)
+            data->channel_map = data->sink->channel_map;
+        else
+            pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+    }
+
+    pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+    pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+    if (!data->volume_is_set)
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+
+    pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+    pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+
+    if (!data->muted_is_set)
+        data->muted = FALSE;
+
+    if (flags & PA_SINK_INPUT_FIX_FORMAT)
+        data->sample_spec.format = data->sink->sample_spec.format;
+
+    if (flags & PA_SINK_INPUT_FIX_RATE)
+        data->sample_spec.rate = data->sink->sample_spec.rate;
+
+    if (flags & PA_SINK_INPUT_FIX_CHANNELS) {
+        data->sample_spec.channels = data->sink->sample_spec.channels;
+        data->channel_map = data->sink->channel_map;
+    }
+
+    pa_assert(pa_sample_spec_valid(&data->sample_spec));
+    pa_assert(pa_channel_map_valid(&data->channel_map));
+
+    /* Due to the fixing of the sample spec the volume might not match anymore */
+    if (data->volume.channels != data->sample_spec.channels)
+        pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume));
+
+    if (data->resample_method == PA_RESAMPLER_INVALID)
+        data->resample_method = core->resample_method;
+
+    pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0)
+        return NULL;
+
+    if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
+        pa_log_warn("Failed to create sink input: too many inputs per sink.");
+        return NULL;
+    }
+
+    if ((flags & PA_SINK_INPUT_VARIABLE_RATE) ||
+        !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) ||
+        !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) {
+
+        if (!(resampler = pa_resampler_new(
+                      core->mempool,
+                      &data->sample_spec, &data->channel_map,
+                      &data->sink->sample_spec, &data->sink->channel_map,
+                      data->resample_method,
+                      ((flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                      ((flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                      (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
+            pa_log_warn("Unsupported resampling operation.");
+            return NULL;
+        }
+
+        data->resample_method = pa_resampler_get_method(resampler);
+    }
+
+    i = pa_msgobject_new(pa_sink_input);
+    i->parent.parent.free = sink_input_free;
+    i->parent.process_msg = pa_sink_input_process_msg;
+
+    i->core = core;
+    i->state = PA_SINK_INPUT_INIT;
+    i->flags = flags;
+    i->proplist = pa_proplist_copy(data->proplist);
+    i->driver = pa_xstrdup(data->driver);
+    i->module = data->module;
+    i->sink = data->sink;
+    i->client = data->client;
+
+    i->resample_method = data->resample_method;
+    i->sample_spec = data->sample_spec;
+    i->channel_map = data->channel_map;
+
+    i->volume = data->volume;
+    i->muted = data->muted;
+
+    if (data->sync_base) {
+        i->sync_next = data->sync_base->sync_next;
+        i->sync_prev = data->sync_base;
+
+        if (data->sync_base->sync_next)
+            data->sync_base->sync_next->sync_prev = i;
+        data->sync_base->sync_next = i;
+    } else
+        i->sync_next = i->sync_prev = NULL;
+
+    i->direct_outputs = pa_idxset_new(NULL, NULL);
+
+    reset_callbacks(i);
+    i->userdata = NULL;
+
+    i->thread_info.state = i->state;
+    i->thread_info.attached = FALSE;
+    pa_atomic_store(&i->thread_info.drained, 1);
+    i->thread_info.sample_spec = i->sample_spec;
+    i->thread_info.resampler = resampler;
+    i->thread_info.volume = i->volume;
+    i->thread_info.muted = i->muted;
+    i->thread_info.requested_sink_latency = (pa_usec_t) -1;
+    i->thread_info.rewrite_nbytes = 0;
+    i->thread_info.rewrite_flush = FALSE;
+    i->thread_info.underrun_for = (uint64_t) -1;
+    i->thread_info.playing_for = 0;
+    i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    i->thread_info.render_memblockq = pa_memblockq_new(
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            0,
+            pa_frame_size(&i->sink->sample_spec),
+            0,
+            1,
+            0,
+            &i->sink->silence);
+
+    pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0);
+    pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0);
+
+    pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s",
+                i->index,
+                pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)),
+                i->sink->name,
+                pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec),
+                pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
+
+    /* Don't forget to call pa_sink_input_put! */
+
+    return i;
+}
+
+/* Called from main context */
+static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
+    pa_assert(i);
+
+    if (i->state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED)
+        pa_assert_se(i->sink->n_corked -- >= 1);
+    else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED)
+        i->sink->n_corked++;
+
+    pa_sink_update_status(i->sink);
+}
+
+/* Called from main context */
+static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
+    pa_sink_input *ssync;
+    pa_assert(i);
+
+    if (state == PA_SINK_INPUT_DRAINED)
+        state = PA_SINK_INPUT_RUNNING;
+
+    if (i->state == state)
+        return 0;
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+    update_n_corked(i, state);
+    i->state = state;
+
+    for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev) {
+        update_n_corked(ssync, state);
+        ssync->state = state;
+    }
+    for (ssync = i->sync_next; ssync; ssync = ssync->sync_next) {
+        update_n_corked(ssync, state);
+        ssync->state = state;
+    }
+
+    if (state != PA_SINK_INPUT_UNLINKED) {
+        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i);
+
+        for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev)
+            pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+
+        for (ssync = i->sync_next; ssync; ssync = ssync->sync_next)
+            pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+    }
+
+    return 0;
+}
+
+/* Called from main context */
+void pa_sink_input_unlink(pa_sink_input *i) {
+    pa_bool_t linked;
+    pa_source_output *o, *p =  NULL;
+    pa_assert(i);
+
+    /* See pa_sink_unlink() for a couple of comments how this function
+     * works */
+
+    pa_sink_input_ref(i);
+
+    linked = PA_SINK_INPUT_IS_LINKED(i->state);
+
+    if (linked)
+        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
+
+    if (i->sync_prev)
+        i->sync_prev->sync_next = i->sync_next;
+    if (i->sync_next)
+        i->sync_next->sync_prev = i->sync_prev;
+
+    i->sync_prev = i->sync_next = NULL;
+
+    pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL);
+    if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
+        pa_sink_input_unref(i);
+
+    while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
+        pa_assert(o != p);
+        pa_source_output_kill(o);
+        p = o;
+    }
+
+    update_n_corked(i, PA_SINK_INPUT_UNLINKED);
+    i->state = PA_SINK_INPUT_UNLINKED;
+
+    if (linked)
+        if (i->sink->asyncmsgq)
+            pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
+
+    reset_callbacks(i);
+
+    if (linked) {
+        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
+        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
+    }
+
+    i->sink = NULL;
+    pa_sink_input_unref(i);
+}
+
+/* Called from main context */
+static void sink_input_free(pa_object *o) {
+    pa_sink_input* i = PA_SINK_INPUT(o);
+
+    pa_assert(i);
+    pa_assert(pa_sink_input_refcnt(i) == 0);
+
+    if (PA_SINK_INPUT_IS_LINKED(i->state))
+        pa_sink_input_unlink(i);
+
+    pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
+
+    pa_assert(!i->thread_info.attached);
+
+    if (i->thread_info.render_memblockq)
+        pa_memblockq_free(i->thread_info.render_memblockq);
+
+    if (i->thread_info.resampler)
+        pa_resampler_free(i->thread_info.resampler);
+
+    if (i->proplist)
+        pa_proplist_free(i->proplist);
+
+    if (i->direct_outputs)
+        pa_idxset_free(i->direct_outputs, NULL, NULL);
+
+    if (i->thread_info.direct_outputs)
+        pa_hashmap_free(i->thread_info.direct_outputs, NULL, NULL);
+
+    pa_xfree(i->driver);
+    pa_xfree(i);
+}
+
+/* Called from main context */
+void pa_sink_input_put(pa_sink_input *i) {
+    pa_sink_input_state_t state;
+    pa_sink_input_assert_ref(i);
+
+    pa_assert(i->state == PA_SINK_INPUT_INIT);
+
+    /* The following fields must be initialized properly */
+    pa_assert(i->pop);
+    pa_assert(i->process_rewind);
+    pa_assert(i->kill);
+
+    i->thread_info.volume = i->volume;
+    i->thread_info.muted = i->muted;
+
+    state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
+
+    update_n_corked(i, state);
+    i->state = state;
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0);
+
+    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
+    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i);
+}
+
+/* Called from main context */
+void pa_sink_input_kill(pa_sink_input*i) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    i->kill(i);
+}
+
+/* Called from main context */
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
+    pa_usec_t r[2] = { 0, 0 };
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
+
+    if (i->get_latency)
+        r[0] += i->get_latency(i);
+
+    if (sink_latency)
+        *sink_latency = r[1];
+
+    return r[0];
+}
+
+/* Called from thread context */
+int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
+    pa_bool_t do_volume_adj_here;
+    pa_bool_t volume_is_norm;
+    size_t block_size_max_sink, block_size_max_sink_input;
+    size_t ilength;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+    pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec));
+    pa_assert(chunk);
+    pa_assert(volume);
+
+/*     pa_log_debug("peek"); */
+
+    if (!i->pop)
+        return -1;
+
+    pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING ||
+              i->thread_info.state == PA_SINK_INPUT_CORKED ||
+              i->thread_info.state == PA_SINK_INPUT_DRAINED);
+
+    /* If there's still some rewrite request the handle, but the sink
+    didn't do this for us, we do it here. However, since the sink
+    apparently doesn't support rewinding, we pass 0 here. This still
+    allows rewinding through the render buffer. */
+    pa_sink_input_process_rewind(i, 0);
+
+    block_size_max_sink_input = i->thread_info.resampler ?
+        pa_resampler_max_block_size(i->thread_info.resampler) :
+        pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sample_spec);
+
+    block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sink->sample_spec);
+
+    /* Default buffer size */
+    if (slength <= 0)
+        slength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sink->sample_spec);
+
+    if (slength > block_size_max_sink)
+        slength = block_size_max_sink;
+
+    if (i->thread_info.resampler) {
+        ilength = pa_resampler_request(i->thread_info.resampler, slength);
+
+        if (ilength <= 0)
+            ilength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec);
+    } else
+        ilength = slength;
+
+    if (ilength > block_size_max_sink_input)
+        ilength = block_size_max_sink_input;
+
+    /* If the channel maps of the sink and this stream differ, we need
+     * to adjust the volume *before* we resample. Otherwise we can do
+     * it after and leave it for the sink code */
+
+    do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
+    volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted;
+
+    while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) {
+        pa_memchunk tchunk;
+
+        /* There's nothing in our render queue. We need to fill it up
+         * with data from the implementor. */
+
+        if (i->thread_info.state == PA_SINK_INPUT_CORKED ||
+            i->pop(i, ilength, &tchunk) < 0) {
+
+            /* OK, we're corked or the implementor didn't give us any
+             * data, so let's just hand out silence */
+            pa_atomic_store(&i->thread_info.drained, 1);
+
+            pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE);
+            i->thread_info.playing_for = 0;
+            if (i->thread_info.underrun_for != (uint64_t) -1)
+                i->thread_info.underrun_for += ilength;
+            break;
+        }
+
+        pa_atomic_store(&i->thread_info.drained, 0);
+
+        pa_assert(tchunk.length > 0);
+        pa_assert(tchunk.memblock);
+
+        i->thread_info.underrun_for = 0;
+        i->thread_info.playing_for += tchunk.length;
+
+        while (tchunk.length > 0) {
+            pa_memchunk wchunk;
+
+            wchunk = tchunk;
+            pa_memblock_ref(wchunk.memblock);
+
+            if (wchunk.length > block_size_max_sink_input)
+                wchunk.length = block_size_max_sink_input;
+
+            /* It might be necessary to adjust the volume here */
+            if (do_volume_adj_here && !volume_is_norm) {
+                pa_memchunk_make_writable(&wchunk, 0);
+
+                if (i->thread_info.muted)
+                    pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec);
+                else
+                    pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.volume);
+            }
+
+            if (!i->thread_info.resampler)
+                pa_memblockq_push_align(i->thread_info.render_memblockq, &wchunk);
+            else {
+                pa_memchunk rchunk;
+                pa_resampler_run(i->thread_info.resampler, &wchunk, &rchunk);
+
+/*                 pa_log_debug("pushing %lu", (unsigned long) rchunk.length); */
+
+                if (rchunk.memblock) {
+                    pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk);
+                    pa_memblock_unref(rchunk.memblock);
+                }
+            }
+
+            pa_memblock_unref(wchunk.memblock);
+
+            tchunk.index += wchunk.length;
+            tchunk.length -= wchunk.length;
+        }
+
+        pa_memblock_unref(tchunk.memblock);
+    }
+
+    pa_assert_se(pa_memblockq_peek(i->thread_info.render_memblockq, chunk) >= 0);
+
+    pa_assert(chunk->length > 0);
+    pa_assert(chunk->memblock);
+
+/*     pa_log_debug("peeking %lu", (unsigned long) chunk->length); */
+
+    if (chunk->length > block_size_max_sink)
+        chunk->length = block_size_max_sink;
+
+    /* Let's see if we had to apply the volume adjustment ourselves,
+     * or if this can be done by the sink for us */
+
+    if (do_volume_adj_here)
+        /* We had different channel maps, so we already did the adjustment */
+        pa_cvolume_reset(volume, i->sink->sample_spec.channels);
+    else if (i->thread_info.muted)
+        /* We've both the same channel map, so let's have the sink do the adjustment for us*/
+        pa_cvolume_mute(volume, i->sink->sample_spec.channels);
+    else
+        *volume = i->thread_info.volume;
+
+    return 0;
+}
+
+/* Called from thread context */
+void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
+    pa_sink_input_assert_ref(i);
+
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+    pa_assert(nbytes > 0);
+
+/*     pa_log_debug("dropping %lu", (unsigned long) nbytes); */
+
+    /* If there's still some rewrite request the handle, but the sink
+    didn't do this for us, we do it here. However, since the sink
+    apparently doesn't support rewinding, we pass 0 here. This still
+    allows rewinding through the render buffer. */
+    pa_sink_input_process_rewind(i, 0);
+
+    pa_memblockq_drop(i->thread_info.render_memblockq, nbytes);
+}
+
+/* Called from thread context */
+void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
+    size_t lbq;
+    pa_sink_input_assert_ref(i);
+
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+/*     pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */
+
+    lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+
+    if (nbytes > 0) {
+        pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes);
+        pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes);
+    }
+
+    if (i->thread_info.rewrite_nbytes == (size_t) -1) {
+
+        /* We were asked to drop all buffered data, and rerequest new
+         * data from implementor the next time push() is called */
+
+        pa_memblockq_flush(i->thread_info.render_memblockq);
+
+    } else if (i->thread_info.rewrite_nbytes > 0) {
+        size_t max_rewrite, amount;
+
+        /* Calculate how much make sense to rewrite at most */
+        max_rewrite = nbytes + lbq;
+
+        /* Transform into local domain */
+        if (i->thread_info.resampler)
+            max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite);
+
+        /* Calculate how much of the rewinded data should actually be rewritten */
+        amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite);
+
+        if (amount > 0) {
+            pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount);
+
+            /* Tell the implementor */
+            if (i->process_rewind)
+                i->process_rewind(i, amount);
+
+            /* Convert back to to sink domain */
+            if (i->thread_info.resampler)
+                amount = pa_resampler_result(i->thread_info.resampler, amount);
+
+            if (amount > 0)
+                /* Ok, now update the write pointer */
+                pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE);
+
+            if (i->thread_info.rewrite_flush)
+                pa_memblockq_silence(i->thread_info.render_memblockq);
+
+            /* And reset the resampler */
+            if (i->thread_info.resampler)
+                pa_resampler_reset(i->thread_info.resampler);
+        }
+    }
+
+    i->thread_info.rewrite_nbytes = 0;
+    i->thread_info.rewrite_flush = FALSE;
+}
+
+/* Called from thread context */
+void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+    pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes);
+
+    if (i->update_max_rewind)
+        i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* Called from thread context */
+void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
+
+    if (i->update_max_request)
+        i->update_max_request(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* Called from thread context */
+static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
+    pa_sink_assert_ref(s);
+
+    if (usec == (pa_usec_t) -1)
+        return usec;
+
+    if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
+        usec = s->thread_info.max_latency;
+
+    if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
+        usec = s->thread_info.min_latency;
+
+    return usec;
+}
+
+/* Called from thread context */
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
+    pa_sink_input_assert_ref(i);
+
+    usec = fixup_latency(i->sink, usec);
+    i->thread_info.requested_sink_latency = usec;
+    pa_sink_invalidate_requested_latency(i->sink);
+
+    return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
+    pa_sink_input_assert_ref(i);
+
+    if (PA_SINK_INPUT_IS_LINKED(i->state))
+        pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+    else {
+        /* If this sink input is not realized yet, we have to touch
+         * the thread info data directly */
+
+        usec = fixup_latency(i->sink, usec);
+        i->thread_info.requested_sink_latency = usec;
+        i->sink->thread_info.requested_latency_valid = FALSE;
+    }
+
+    return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
+    pa_usec_t usec = 0;
+
+    pa_sink_input_assert_ref(i);
+
+    if (PA_SINK_INPUT_IS_LINKED(i->state))
+        pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+    else
+        /* If this sink input is not realized yet, we have to touch
+         * the thread info data directly */
+        usec = i->thread_info.requested_sink_latency;
+
+    return usec;
+}
+
+/* Called from main context */
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(volume);
+
+    if (pa_cvolume_equal(&i->volume, volume))
+        return;
+
+    i->volume = *volume;
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &i->volume, 0, NULL) == 0);
+    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+}
+
+/* Called from main context */
+const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    return &i->volume;
+}
+
+/* Called from main context */
+void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
+    pa_assert(i);
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    if (!i->muted == !mute)
+        return;
+
+    i->muted = mute;
+
+    pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+}
+
+/* Called from main context */
+pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    return i->muted;
+}
+
+/* Called from main context */
+void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
+}
+
+/* Called from main context */
+int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_return_val_if_fail(i->thread_info.resampler, -1);
+
+    if (i->sample_spec.rate == rate)
+        return 0;
+
+    i->sample_spec.rate = rate;
+
+    pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
+
+    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    return 0;
+}
+
+/* Called from main context */
+void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
+    const char *old;
+    pa_sink_input_assert_ref(i);
+
+    if (!name && !pa_proplist_contains(i->proplist, PA_PROP_MEDIA_NAME))
+        return;
+
+    old = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME);
+
+    if (old && name && !strcmp(old, name))
+        return;
+
+    if (name)
+        pa_proplist_sets(i->proplist, PA_PROP_MEDIA_NAME, name);
+    else
+        pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME);
+
+    if (PA_SINK_INPUT_IS_LINKED(i->state)) {
+        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    }
+}
+
+/* Called from main context */
+pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+
+    return i->resample_method;
+}
+
+/* Called from main context */
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
+    pa_resampler *new_resampler;
+    pa_sink *origin;
+    pa_sink_input_move_hook_data hook_data;
+    pa_source_output *o, *p = NULL;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_sink_assert_ref(dest);
+
+    origin = i->sink;
+
+    if (dest == origin)
+        return 0;
+
+    if (i->flags & PA_SINK_INPUT_DONT_MOVE)
+        return -1;
+
+    if (i->sync_next || i->sync_prev) {
+        pa_log_warn("Moving synchronised streams not supported.");
+        return -1;
+    }
+
+    if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) {
+        pa_log_warn("Failed to move sink input: too many inputs per sink.");
+        return -1;
+    }
+
+    /* Kill directly connected outputs */
+    while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
+        pa_assert(o != p);
+        pa_source_output_kill(o);
+        p = o;
+    }
+
+    if (i->thread_info.resampler &&
+        pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
+        pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
+
+        /* Try to reuse the old resampler if possible */
+        new_resampler = i->thread_info.resampler;
+
+    else if ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ||
+             !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) ||
+             !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) {
+
+        /* Okey, we need a new resampler for the new sink */
+
+        if (!(new_resampler = pa_resampler_new(
+                      dest->core->mempool,
+                      &i->sample_spec, &i->channel_map,
+                      &dest->sample_spec, &dest->channel_map,
+                      i->resample_method,
+                      ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                      ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                      (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
+            pa_log_warn("Unsupported resampling operation.");
+            return -1;
+        }
+    } else
+        new_resampler = NULL;
+
+    hook_data.sink_input = i;
+    hook_data.destination = dest;
+    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data);
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
+
+    pa_idxset_remove_by_data(origin->inputs, i, NULL);
+    pa_idxset_put(dest->inputs, i, NULL);
+    i->sink = dest;
+
+    if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) {
+        pa_assert_se(origin->n_corked-- >= 1);
+        dest->n_corked++;
+    }
+
+    /* Replace resampler and render queue */
+    if (new_resampler != i->thread_info.resampler) {
+
+        if (i->thread_info.resampler)
+            pa_resampler_free(i->thread_info.resampler);
+        i->thread_info.resampler = new_resampler;
+
+        pa_memblockq_free(i->thread_info.render_memblockq);
+
+        i->thread_info.render_memblockq = pa_memblockq_new(
+                0,
+                MEMBLOCKQ_MAXLENGTH,
+                0,
+                pa_frame_size(&i->sink->sample_spec),
+                0,
+                1,
+                0,
+                &i->sink->silence);
+    }
+
+    pa_sink_update_status(origin);
+    pa_sink_update_status(dest);
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
+
+    if (i->moved)
+        i->moved(i);
+
+    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i);
+
+    pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name);
+
+    /* Notify everyone */
+    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+
+    return 0;
+}
+
+/* Called from IO thread context */
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) {
+    pa_sink_input_assert_ref(i);
+
+    if (state == i->thread_info.state)
+        return;
+
+    if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) &&
+        !(i->thread_info.state == PA_SINK_INPUT_DRAINED || i->thread_info.state != PA_SINK_INPUT_RUNNING))
+        pa_atomic_store(&i->thread_info.drained, 1);
+
+    if (state == PA_SINK_INPUT_CORKED && i->thread_info.state != PA_SINK_INPUT_CORKED) {
+
+        /* This will tell the implementing sink input driver to rewind
+         * so that the unplayed already mixed data is not lost */
+        pa_sink_input_request_rewind(i, 0, TRUE, TRUE);
+
+    } else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) {
+
+        /* OK, we're being uncorked. Make sure we're not rewound when
+         * the hw buffer is remixed and request a remix. */
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+    }
+
+    if (i->state_change)
+        i->state_change(i, state);
+
+    i->thread_info.state = state;
+}
+
+/* Called from thread context, except when it is not. */
+int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_sink_input *i = PA_SINK_INPUT(o);
+    pa_sink_input_assert_ref(i);
+
+    switch (code) {
+
+        case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
+            i->thread_info.volume = *((pa_cvolume*) userdata);
+            pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_SET_MUTE:
+            i->thread_info.muted = PA_PTR_TO_UINT(userdata);
+            pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = userdata;
+            pa_usec_t sink_usec = 0;
+
+            r[0] += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec);
+
+            if (i->sink->parent.process_msg(PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_usec, 0, NULL) >= 0)
+                r[1] += sink_usec;
+
+            return 0;
+        }
+
+        case PA_SINK_INPUT_MESSAGE_SET_RATE:
+
+            i->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
+            pa_resampler_set_input_rate(i->thread_info.resampler, PA_PTR_TO_UINT(userdata));
+
+            return 0;
+
+        case PA_SINK_INPUT_MESSAGE_SET_STATE: {
+            pa_sink_input *ssync;
+
+            pa_sink_input_set_state_within_thread(i, PA_PTR_TO_UINT(userdata));
+
+            for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
+                pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
+
+            for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next)
+                pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
+
+            return 0;
+        }
+
+        case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+            pa_usec_t *usec = userdata;
+
+            *usec = pa_sink_input_set_requested_latency_within_thread(i, *usec);
+            return 0;
+        }
+
+        case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            *r = i->thread_info.requested_sink_latency;
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+/* Called from main thread */
+pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+
+    if (i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED)
+        return pa_atomic_load(&i->thread_info.drained) ? PA_SINK_INPUT_DRAINED : PA_SINK_INPUT_RUNNING;
+
+    return i->state;
+}
+
+/* Called from IO context */
+pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+
+    if (PA_SINK_INPUT_IS_LINKED(i->thread_info.state))
+        return pa_memblockq_is_empty(i->thread_info.render_memblockq);
+
+    return TRUE;
+}
+
+/* Called from IO context */
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes  /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) {
+    size_t lbq;
+
+    /* If 'rewrite' is TRUE the sink is rewound as far as requested
+     * and possible and the exact value of this is passed back the
+     * implementor via process_rewind(). If 'flush' is also TRUE all
+     * already rendered data is also dropped.
+     *
+     * If 'rewrite' is FALSE the sink is rewound as far as requested
+     * and possible and the already rendered data is dropped so that
+     * in the next iteration we read new data from the
+     * implementor. This implies 'flush' is TRUE. */
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(i->thread_info.rewrite_nbytes == 0);
+
+    /* We don't take rewind requests while we are corked */
+    if (i->thread_info.state == PA_SINK_INPUT_CORKED)
+        return;
+
+    pa_assert(rewrite || flush);
+
+    /* Calculate how much we can rewind locally without having to
+     * touch the sink */
+    if (rewrite)
+        lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+    else
+        lbq = 0;
+
+    /* Check if rewinding for the maximum is requested, and if so, fix up */
+    if (nbytes <= 0) {
+
+        /* Calculate maximum number of bytes that could be rewound in theory */
+        nbytes = i->sink->thread_info.max_rewind + lbq;
+
+        /* Transform from sink domain */
+        if (i->thread_info.resampler)
+            nbytes = pa_resampler_request(i->thread_info.resampler, nbytes);
+    }
+
+    if (rewrite) {
+        /* Make sure to not overwrite over underruns */
+        if (nbytes > i->thread_info.playing_for)
+            nbytes = (size_t) i->thread_info.playing_for;
+
+        i->thread_info.rewrite_nbytes = nbytes;
+    } else
+        i->thread_info.rewrite_nbytes = (size_t) -1;
+
+    i->thread_info.rewrite_flush = flush && i->thread_info.rewrite_nbytes != 0;
+
+    /* Transform to sink domain */
+    if (i->thread_info.resampler)
+        nbytes = pa_resampler_result(i->thread_info.resampler, nbytes);
+
+    if (nbytes > lbq)
+        pa_sink_request_rewind(i->sink, nbytes - lbq);
+}
+
+/* Called from main context */
+pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(ret);
+
+    pa_silence_memchunk_get(
+                &i->sink->core->silence_cache,
+                i->sink->core->mempool,
+                ret,
+                &i->sample_spec,
+                i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0);
+
+    return ret;
+}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
new file mode 100644 (file)
index 0000000..c07a740
--- /dev/null
@@ -0,0 +1,307 @@
+#ifndef foopulsesinkinputhfoo
+#define foopulsesinkinputhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+typedef struct pa_sink_input pa_sink_input;
+
+#include <pulse/sample.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/module.h>
+#include <pulsecore/client.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/core.h>
+
+typedef enum pa_sink_input_state {
+    PA_SINK_INPUT_INIT,         /*< The stream is not active yet, because pa_sink_put() has not been called yet */
+    PA_SINK_INPUT_DRAINED,      /*< The stream stopped playing because there was no data to play */
+    PA_SINK_INPUT_RUNNING,      /*< The stream is alive and kicking */
+    PA_SINK_INPUT_CORKED,       /*< The stream was corked on user request */
+    PA_SINK_INPUT_UNLINKED      /*< The stream is dead */
+} pa_sink_input_state_t;
+
+static inline pa_bool_t PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) {
+    return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED;
+}
+
+typedef enum pa_sink_input_flags {
+    PA_SINK_INPUT_VARIABLE_RATE = 1,
+    PA_SINK_INPUT_DONT_MOVE = 2,
+    PA_SINK_INPUT_START_CORKED = 4,
+    PA_SINK_INPUT_NO_REMAP = 8,
+    PA_SINK_INPUT_NO_REMIX = 16,
+    PA_SINK_INPUT_FIX_FORMAT = 32,
+    PA_SINK_INPUT_FIX_RATE = 64,
+    PA_SINK_INPUT_FIX_CHANNELS = 128
+} pa_sink_input_flags_t;
+
+struct pa_sink_input {
+    pa_msgobject parent;
+
+    uint32_t index;
+    pa_core *core;
+
+    /* Please note that this state should only be read with
+     * pa_sink_input_get_state(). That function will transparently
+     * merge the thread_info.drained value in. */
+    pa_sink_input_state_t state;
+    pa_sink_input_flags_t flags;
+
+    char *driver;                       /* may be NULL */
+    pa_proplist *proplist;
+
+    pa_module *module;                  /* may be NULL */
+    pa_client *client;                  /* may be NULL */
+
+    pa_sink *sink;
+
+    /* A sink input may be connected to multiple source outputs
+     * directly, so that they don't get mixed data of the entire
+     * source. */
+    pa_idxset *direct_outputs;
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+
+    pa_sink_input *sync_prev, *sync_next;
+
+    pa_cvolume volume;
+    pa_bool_t muted;
+
+    pa_resample_method_t resample_method;
+
+    /* Returns the chunk of audio data and drops it from the
+     * queue. Returns -1 on failure. Called from IO thread context. If
+     * data needs to be generated from scratch then please in the
+     * specified length request_nbytes. This is an optimization
+     * only. If less data is available, it's fine to return a smaller
+     * block. If more data is already ready, it is better to return
+     * the full block. */
+    int (*pop) (pa_sink_input *i, size_t request_nbytes, pa_memchunk *chunk); /* may NOT be NULL */
+
+    /* Rewind the queue by the specified number of bytes. Called just
+     * before peek() if it is called at all. Only called if the sink
+     * input driver ever plans to call
+     * pa_sink_input_request_rewind(). Called from IO context. */
+    void (*process_rewind) (pa_sink_input *i, size_t nbytes);     /* may NOT be NULL */
+
+    /* Called whenever the maximum rewindable size of the sink
+     * changes. Called from IO context. */
+    void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */
+
+    /* Called whenever the maxiumum request size of the sink
+     * changes. Called from IO context. */
+    void (*update_max_request) (pa_sink_input *i, size_t nbytes); /* may be NULL */
+
+    /* Called whenever the configured latency of the sink
+     * changes. Called from IO context. */
+    void (*update_sink_requested_latency) (pa_sink_input *i); /* may be NULL */
+
+    /* Called whenver the latency range of the sink changes. Called
+     * from IO context. */
+    void (*update_sink_latency_range) (pa_sink_input *i); /* may be NULL */
+
+    /* If non-NULL this function is called when the input is first
+     * connected to a sink or when the rtpoll/asyncmsgq fields
+     * change. You usually don't need to implement this function
+     * unless you rewrite a sink that is piggy-backed onto
+     * another. Called from IO thread context */
+    void (*attach) (pa_sink_input *i);           /* may be NULL */
+
+    /* If non-NULL this function is called when the output is
+     * disconnected from its sink. Called from IO thread context */
+    void (*detach) (pa_sink_input *i);           /* may be NULL */
+
+    /* If non-NULL called whenever the the sink this input is attached
+     * to suspends or resumes. Called from main context */
+    void (*suspend) (pa_sink_input *i, pa_bool_t b);   /* may be NULL */
+
+    /* If non-NULL called whenever the the sink this input is attached
+     * to changes. Called from main context */
+    void (*moved) (pa_sink_input *i);   /* may be NULL */
+
+    /* Supposed to unlink and destroy this stream. Called from main
+     * context. */
+    void (*kill) (pa_sink_input *i);             /* may NOT be NULL */
+
+    /* Return the current latency (i.e. length of bufferd audio) of
+    this stream. Called from main context. This is added to what the
+    PA_SINK_INPUT_MESSAGE_GET_LATENCY message sent to the IO thread
+    returns */
+    pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
+
+    /* If non_NULL this function is called from thread context if the
+     * state changes. The old state is found in thread_info.state.  */
+    void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */
+
+    struct {
+        pa_sink_input_state_t state;
+        pa_atomic_t drained;
+
+        pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
+
+        pa_sample_spec sample_spec;
+
+        pa_resampler *resampler;                     /* may be NULL */
+
+        /* We maintain a history of resampled audio data here. */
+        pa_memblockq *render_memblockq;
+
+        size_t rewrite_nbytes;
+        pa_bool_t rewrite_flush;
+        uint64_t underrun_for, playing_for;
+
+        pa_sink_input *sync_prev, *sync_next;
+
+        pa_cvolume volume;
+        pa_bool_t muted;
+
+        /* The requested latency for the sink */
+        pa_usec_t requested_sink_latency;
+
+        pa_hashmap *direct_outputs;
+    } thread_info;
+
+    void *userdata;
+};
+
+PA_DECLARE_CLASS(pa_sink_input);
+#define PA_SINK_INPUT(o) pa_sink_input_cast(o)
+
+enum {
+    PA_SINK_INPUT_MESSAGE_SET_VOLUME,
+    PA_SINK_INPUT_MESSAGE_SET_MUTE,
+    PA_SINK_INPUT_MESSAGE_GET_LATENCY,
+    PA_SINK_INPUT_MESSAGE_SET_RATE,
+    PA_SINK_INPUT_MESSAGE_SET_STATE,
+    PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY,
+    PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY,
+    PA_SINK_INPUT_MESSAGE_MAX
+};
+
+typedef struct pa_sink_input_new_data {
+    pa_proplist *proplist;
+
+    const char *driver;
+    pa_module *module;
+    pa_client *client;
+
+    pa_sink *sink;
+
+    pa_sample_spec sample_spec;
+    pa_bool_t sample_spec_is_set;
+    pa_channel_map channel_map;
+    pa_bool_t channel_map_is_set;
+
+    pa_cvolume volume;
+    pa_bool_t volume_is_set;
+    pa_bool_t muted;
+    pa_bool_t muted_is_set;
+
+    pa_resample_method_t resample_method;
+
+    pa_sink_input *sync_base;
+} pa_sink_input_new_data;
+
+pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
+void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);
+void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
+void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
+void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
+void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
+
+typedef struct pa_sink_input_move_hook_data {
+    pa_sink_input *sink_input;
+    pa_sink *destination;
+} pa_sink_input_move_hook_data;
+
+/* To be called by the implementing module only */
+
+pa_sink_input* pa_sink_input_new(
+        pa_core *core,
+        pa_sink_input_new_data *data,
+        pa_sink_input_flags_t flags);
+
+void pa_sink_input_put(pa_sink_input *i);
+void pa_sink_input_unlink(pa_sink_input* i);
+
+void pa_sink_input_set_name(pa_sink_input *i, const char *name);
+
+pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec);
+
+/* Request that the specified number of bytes already written out to
+the hw device is rewritten, if possible.  Please note that this is
+only a kind request. The sink driver may not be able to fulfill it
+fully -- or at all. If the request for a rewrite was successful, the
+sink driver will call ->rewind() and pass the number of bytes that
+could be rewound in the HW device. This functionality is required for
+implementing the "zero latency" write-through functionality. */
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush);
+
+void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
+
+int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
+
+/* Callable by everyone from main thread*/
+
+/* External code may request disconnection with this function */
+void pa_sink_input_kill(pa_sink_input*i);
+
+pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
+
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume);
+const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
+void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute);
+pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
+
+pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
+
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest);
+
+pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
+
+pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i);
+
+/* To be used exclusively by the sink driver IO thread */
+
+int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume);
+void pa_sink_input_drop(pa_sink_input *i, size_t length);
+void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
+void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */);
+void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */);
+
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state);
+
+int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec);
+
+pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);
+
+pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
+
+#endif
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
new file mode 100644 (file)
index 0000000..edb023b
--- /dev/null
@@ -0,0 +1,1531 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdio.h>
+
+#include <pulse/introspect.h>
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/play-memblockq.h>
+
+#include "sink.h"
+
+#define MAX_MIX_CHANNELS 32
+#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
+#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+
+static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
+
+static void sink_free(pa_object *s);
+
+pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
+    pa_assert(data);
+
+    memset(data, 0, sizeof(*data));
+    data->proplist = pa_proplist_new();
+
+    return data;
+}
+
+void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    data->name = pa_xstrdup(name);
+}
+
+void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) {
+    pa_assert(data);
+
+    if ((data->sample_spec_is_set = !!spec))
+        data->sample_spec = *spec;
+}
+
+void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) {
+    pa_assert(data);
+
+    if ((data->channel_map_is_set = !!map))
+        data->channel_map = *map;
+}
+
+void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
+    pa_assert(data);
+
+    if ((data->volume_is_set = !!volume))
+        data->volume = *volume;
+}
+
+void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
+    pa_assert(data);
+
+    data->muted_is_set = TRUE;
+    data->muted = !!mute;
+}
+
+void pa_sink_new_data_done(pa_sink_new_data *data) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_sink *s) {
+    pa_assert(s);
+
+    s->set_state = NULL;
+    s->get_volume = NULL;
+    s->set_volume = NULL;
+    s->get_mute = NULL;
+    s->set_mute = NULL;
+    s->request_rewind = NULL;
+    s->update_requested_latency = NULL;
+}
+
+/* Called from main context */
+pa_sink* pa_sink_new(
+        pa_core *core,
+        pa_sink_new_data *data,
+        pa_sink_flags_t flags) {
+
+    pa_sink *s;
+    const char *name;
+    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    pa_source_new_data source_data;
+    const char *dn;
+
+    pa_assert(core);
+    pa_assert(data);
+    pa_assert(data->name);
+
+    s = pa_msgobject_new(pa_sink);
+
+    if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
+        pa_xfree(s);
+        return NULL;
+    }
+
+    pa_sink_new_data_set_name(data, name);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) {
+        pa_xfree(s);
+        pa_namereg_unregister(core, name);
+        return NULL;
+    }
+
+    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+    pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
+
+    pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
+
+    if (!data->channel_map_is_set)
+        pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+
+    pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+    pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+    if (!data->volume_is_set)
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+
+    pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+    pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+
+    if (!data->muted_is_set)
+        data->muted = FALSE;
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
+        pa_xfree(s);
+        pa_namereg_unregister(core, name);
+        return NULL;
+    }
+
+    s->parent.parent.free = sink_free;
+    s->parent.process_msg = pa_sink_process_msg;
+
+    s->core = core;
+    s->state = PA_SINK_INIT;
+    s->flags = flags;
+    s->name = pa_xstrdup(name);
+    s->proplist = pa_proplist_copy(data->proplist);
+    s->driver = pa_xstrdup(data->driver);
+    s->module = data->module;
+
+    s->sample_spec = data->sample_spec;
+    s->channel_map = data->channel_map;
+
+    s->inputs = pa_idxset_new(NULL, NULL);
+    s->n_corked = 0;
+
+    s->volume = data->volume;
+    s->muted = data->muted;
+    s->refresh_volume = s->refresh_muted = FALSE;
+
+    reset_callbacks(s);
+    s->userdata = NULL;
+
+    s->asyncmsgq = NULL;
+    s->rtpoll = NULL;
+
+    pa_silence_memchunk_get(
+            &core->silence_cache,
+            core->mempool,
+            &s->silence,
+            &s->sample_spec,
+            0);
+
+    s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
+    s->thread_info.soft_muted = FALSE;
+    s->thread_info.state = s->state;
+    s->thread_info.rewind_nbytes = 0;
+    s->thread_info.max_rewind = 0;
+    s->thread_info.max_request = 0;
+    s->thread_info.requested_latency_valid = FALSE;
+    s->thread_info.requested_latency = 0;
+    s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
+    s->thread_info.max_latency = 0;
+
+    pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
+
+    pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s",
+                s->index,
+                s->name,
+                pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
+                pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
+
+    pa_source_new_data_init(&source_data);
+    pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
+    pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
+    source_data.name = pa_sprintf_malloc("%s.monitor", name);
+    source_data.driver = data->driver;
+    source_data.module = data->module;
+
+    dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+    pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
+    pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
+
+    s->monitor_source = pa_source_new(core, &source_data, 0);
+
+    pa_source_new_data_done(&source_data);
+
+    if (!s->monitor_source) {
+        pa_sink_unlink(s);
+        pa_sink_unref(s);
+        return NULL;
+    }
+
+    s->monitor_source->monitor_of = s;
+
+    pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
+    pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
+
+    return s;
+}
+
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+    int ret;
+    pa_bool_t suspend_change;
+
+    pa_assert(s);
+
+    if (s->state == state)
+        return 0;
+
+    suspend_change =
+        (s->state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
+        (PA_SINK_IS_OPENED(s->state) && state == PA_SINK_SUSPENDED);
+
+    if (s->set_state)
+        if ((ret = s->set_state(s, state)) < 0)
+            return -1;
+
+    if (s->asyncmsgq)
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+    s->state = state;
+
+    if (suspend_change) {
+        pa_sink_input *i;
+        uint32_t idx;
+
+        /* We're suspending or resuming, tell everyone about it */
+
+        for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
+            if (i->suspend)
+                i->suspend(i, state == PA_SINK_SUSPENDED);
+    }
+
+    if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
+
+    return 0;
+}
+
+/* Called from main context */
+void pa_sink_put(pa_sink* s) {
+    pa_sink_assert_ref(s);
+
+    pa_assert(s->state == PA_SINK_INIT);
+
+    /* The following fields must be initialized properly when calling _put() */
+    pa_assert(s->asyncmsgq);
+    pa_assert(s->rtpoll);
+    pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
+              s->thread_info.min_latency <= s->thread_info.max_latency);
+
+    if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
+        s->flags |= PA_SINK_DECIBEL_VOLUME;
+
+        s->thread_info.soft_volume = s->volume;
+        s->thread_info.soft_muted = s->muted;
+    }
+
+    pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
+
+    pa_source_put(s->monitor_source);
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
+}
+
+/* Called from main context */
+void pa_sink_unlink(pa_sink* s) {
+    pa_bool_t linked;
+    pa_sink_input *i, *j = NULL;
+
+    pa_assert(s);
+
+    /* Please note that pa_sink_unlink() does more than simply
+     * reversing pa_sink_put(). It also undoes the registrations
+     * already done in pa_sink_new()! */
+
+    /* All operations here shall be idempotent, i.e. pa_sink_unlink()
+     * may be called multiple times on the same sink without bad
+     * effects. */
+
+    linked = PA_SINK_IS_LINKED(s->state);
+
+    if (linked)
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
+
+    if (s->state != PA_SINK_UNLINKED)
+        pa_namereg_unregister(s->core, s->name);
+    pa_idxset_remove_by_data(s->core->sinks, s, NULL);
+
+    while ((i = pa_idxset_first(s->inputs, NULL))) {
+        pa_assert(i != j);
+        pa_sink_input_kill(i);
+        j = i;
+    }
+
+    if (linked)
+        sink_set_state(s, PA_SINK_UNLINKED);
+    else
+        s->state = PA_SINK_UNLINKED;
+
+    reset_callbacks(s);
+
+    if (s->monitor_source)
+        pa_source_unlink(s->monitor_source);
+
+    if (linked) {
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s);
+    }
+}
+
+/* Called from main context */
+static void sink_free(pa_object *o) {
+    pa_sink *s = PA_SINK(o);
+    pa_sink_input *i;
+
+    pa_assert(s);
+    pa_assert(pa_sink_refcnt(s) == 0);
+
+    if (PA_SINK_IS_LINKED(s->state))
+        pa_sink_unlink(s);
+
+    pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
+
+    if (s->monitor_source) {
+        pa_source_unref(s->monitor_source);
+        s->monitor_source = NULL;
+    }
+
+    pa_idxset_free(s->inputs, NULL, NULL);
+
+    while ((i = pa_hashmap_steal_first(s->thread_info.inputs)))
+        pa_sink_input_unref(i);
+
+    pa_hashmap_free(s->thread_info.inputs, NULL, NULL);
+
+    if (s->silence.memblock)
+        pa_memblock_unref(s->silence.memblock);
+
+    pa_xfree(s->name);
+    pa_xfree(s->driver);
+
+    if (s->proplist)
+        pa_proplist_free(s->proplist);
+
+    pa_xfree(s);
+}
+
+/* Called from main context */
+void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
+    pa_sink_assert_ref(s);
+
+    s->asyncmsgq = q;
+
+    if (s->monitor_source)
+        pa_source_set_asyncmsgq(s->monitor_source, q);
+}
+
+/* Called from main context */
+void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
+    pa_sink_assert_ref(s);
+
+    s->rtpoll = p;
+    if (s->monitor_source)
+        pa_source_set_rtpoll(s->monitor_source, p);
+}
+
+/* Called from main context */
+int pa_sink_update_status(pa_sink*s) {
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (s->state == PA_SINK_SUSPENDED)
+        return 0;
+
+    return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
+}
+
+/* Called from main context */
+int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (suspend)
+        return sink_set_state(s, PA_SINK_SUSPENDED);
+    else
+        return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
+}
+
+/* Called from IO thread context */
+void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
+    pa_sink_input *i;
+    void *state = NULL;
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    /* Make sure the sink code already reset the counter! */
+    pa_assert(s->thread_info.rewind_nbytes <= 0);
+
+    if (nbytes <= 0)
+        return;
+
+    pa_log_debug("Processing rewind...");
+
+    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+        pa_sink_input_assert_ref(i);
+        pa_sink_input_process_rewind(i, nbytes);
+    }
+
+    if (s->monitor_source && PA_SOURCE_IS_OPENED(s->monitor_source->thread_info.state))
+        pa_source_process_rewind(s->monitor_source, nbytes);
+}
+
+/* Called from IO thread context */
+static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) {
+    pa_sink_input *i;
+    unsigned n = 0;
+    void *state = NULL;
+    size_t mixlength = *length;
+
+    pa_sink_assert_ref(s);
+    pa_assert(info);
+
+    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
+        pa_sink_input_assert_ref(i);
+
+        if (pa_sink_input_peek(i, *length, &info->chunk, &info->volume) < 0)
+            continue;
+
+        if (mixlength == 0 || info->chunk.length < mixlength)
+            mixlength = info->chunk.length;
+
+        if (pa_memblock_is_silence(info->chunk.memblock)) {
+            pa_memblock_unref(info->chunk.memblock);
+            continue;
+        }
+
+        info->userdata = pa_sink_input_ref(i);
+
+        pa_assert(info->chunk.memblock);
+        pa_assert(info->chunk.length > 0);
+
+        info++;
+        n++;
+        maxinfo--;
+    }
+
+    if (mixlength > 0)
+        *length = mixlength;
+
+    return n;
+}
+
+/* Called from IO thread context */
+static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
+    pa_sink_input *i;
+    void *state = NULL;
+    unsigned p = 0;
+    unsigned n_unreffed = 0;
+
+    pa_sink_assert_ref(s);
+    pa_assert(result);
+    pa_assert(result->memblock);
+    pa_assert(result->length > 0);
+
+    /* We optimize for the case where the order of the inputs has not changed */
+
+    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+        unsigned j;
+        pa_mix_info* m = NULL;
+
+        pa_sink_input_assert_ref(i);
+
+        /* Let's try to find the matching entry info the pa_mix_info array */
+        for (j = 0; j < n; j ++) {
+
+            if (info[p].userdata == i) {
+                m = info + p;
+                break;
+            }
+
+            p++;
+            if (p >= n)
+                p = 0;
+        }
+
+        /* Drop read data */
+        pa_sink_input_drop(i, result->length);
+
+        if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) {
+
+            if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
+                void *ostate = NULL;
+                pa_source_output *o;
+                pa_memchunk c;
+
+                if (m && m->chunk.memblock) {
+                    c = m->chunk;
+                    pa_memblock_ref(c.memblock);
+                    pa_assert(result->length <= c.length);
+                    c.length = result->length;
+
+                    pa_memchunk_make_writable(&c, 0);
+                    pa_volume_memchunk(&c, &s->sample_spec, &m->volume);
+                } else {
+                    c = s->silence;
+                    pa_memblock_ref(c.memblock);
+                    pa_assert(result->length <= c.length);
+                    c.length = result->length;
+                }
+
+                while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) {
+                    pa_source_output_assert_ref(o);
+                    pa_assert(o->direct_on_input == i);
+                    pa_source_post_direct(s->monitor_source, o, &c);
+                }
+
+                pa_memblock_unref(c.memblock);
+            }
+        }
+
+        if (m) {
+            if (m->chunk.memblock)
+                pa_memblock_unref(m->chunk.memblock);
+                pa_memchunk_reset(&m->chunk);
+
+            pa_sink_input_unref(m->userdata);
+            m->userdata = NULL;
+
+            n_unreffed += 1;
+        }
+    }
+
+    /* Now drop references to entries that are included in the
+     * pa_mix_info array but don't exist anymore */
+
+    if (n_unreffed < n) {
+        for (; n > 0; info++, n--) {
+            if (info->userdata)
+                pa_sink_input_unref(info->userdata);
+            if (info->chunk.memblock)
+                pa_memblock_unref(info->chunk.memblock);
+        }
+    }
+
+    if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
+        pa_source_post(s->monitor_source, result);
+}
+
+/* Called from IO thread context */
+void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
+    pa_mix_info info[MAX_MIX_CHANNELS];
+    unsigned n;
+    size_t block_size_max;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+    pa_assert(pa_frame_aligned(length, &s->sample_spec));
+    pa_assert(result);
+
+    pa_sink_ref(s);
+
+    s->thread_info.rewind_nbytes = 0;
+
+    if (length <= 0)
+        length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
+
+    block_size_max = pa_mempool_block_size_max(s->core->mempool);
+    if (length > block_size_max)
+        length = pa_frame_align(block_size_max, &s->sample_spec);
+
+    pa_assert(length > 0);
+
+    n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0;
+
+    if (n == 0) {
+
+        *result = s->silence;
+        pa_memblock_ref(result->memblock);
+
+        if (result->length > length)
+            result->length = length;
+
+    } else if (n == 1) {
+        pa_cvolume volume;
+
+        *result = info[0].chunk;
+        pa_memblock_ref(result->memblock);
+
+        if (result->length > length)
+            result->length = length;
+
+        pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
+
+        if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
+            pa_log("adjusting volume ");
+            pa_memchunk_make_writable(result, 0);
+            if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
+                pa_silence_memchunk(result, &s->sample_spec);
+            else
+                pa_volume_memchunk(result, &s->sample_spec, &volume);
+        }
+    } else {
+        void *ptr;
+        result->memblock = pa_memblock_new(s->core->mempool, length);
+
+        ptr = pa_memblock_acquire(result->memblock);
+        result->length = pa_mix(info, n,
+                                ptr, length,
+                                &s->sample_spec,
+                                &s->thread_info.soft_volume,
+                                s->thread_info.soft_muted);
+        pa_memblock_release(result->memblock);
+
+        result->index = 0;
+    }
+
+    if (s->thread_info.state == PA_SINK_RUNNING)
+        inputs_drop(s, info, n, result);
+
+    pa_sink_unref(s);
+}
+
+/* Called from IO thread context */
+void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
+    pa_mix_info info[MAX_MIX_CHANNELS];
+    unsigned n;
+    size_t length, block_size_max;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+    pa_assert(target);
+    pa_assert(target->memblock);
+    pa_assert(target->length > 0);
+    pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
+
+    pa_sink_ref(s);
+
+    s->thread_info.rewind_nbytes = 0;
+
+    length = target->length;
+    block_size_max = pa_mempool_block_size_max(s->core->mempool);
+    if (length > block_size_max)
+        length = pa_frame_align(block_size_max, &s->sample_spec);
+
+    pa_assert(length > 0);
+
+    n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0;
+
+    if (n == 0) {
+        if (target->length > length)
+            target->length = length;
+
+        pa_silence_memchunk(target, &s->sample_spec);
+    } else if (n == 1) {
+        pa_cvolume volume;
+
+        if (target->length > length)
+            target->length = length;
+
+        pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
+
+        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
+            pa_silence_memchunk(target, &s->sample_spec);
+        else {
+            pa_memchunk vchunk;
+
+            vchunk = info[0].chunk;
+            pa_memblock_ref(vchunk.memblock);
+
+            if (vchunk.length > length)
+                vchunk.length = length;
+
+            if (!pa_cvolume_is_norm(&volume)) {
+                pa_memchunk_make_writable(&vchunk, 0);
+                pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+            }
+
+            pa_memchunk_memcpy(target, &vchunk);
+            pa_memblock_unref(vchunk.memblock);
+        }
+
+    } else {
+        void *ptr;
+
+        ptr = pa_memblock_acquire(target->memblock);
+
+        target->length = pa_mix(info, n,
+                                (uint8_t*) ptr + target->index, length,
+                                &s->sample_spec,
+                                &s->thread_info.soft_volume,
+                                s->thread_info.soft_muted);
+
+        pa_memblock_release(target->memblock);
+    }
+
+    if (s->thread_info.state == PA_SINK_RUNNING)
+        inputs_drop(s, info, n, target);
+
+    pa_sink_unref(s);
+}
+
+/* Called from IO thread context */
+void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
+    pa_memchunk chunk;
+    size_t l, d;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+    pa_assert(target);
+    pa_assert(target->memblock);
+    pa_assert(target->length > 0);
+    pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
+
+    pa_sink_ref(s);
+
+    s->thread_info.rewind_nbytes = 0;
+
+    l = target->length;
+    d = 0;
+    while (l > 0) {
+        chunk = *target;
+        chunk.index += d;
+        chunk.length -= d;
+
+        pa_sink_render_into(s, &chunk);
+
+        d += chunk.length;
+        l -= chunk.length;
+    }
+
+    pa_sink_unref(s);
+}
+
+/* Called from IO thread context */
+void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+    pa_assert(length > 0);
+    pa_assert(pa_frame_aligned(length, &s->sample_spec));
+    pa_assert(result);
+
+    s->thread_info.rewind_nbytes = 0;
+
+    /*** This needs optimization ***/
+
+    result->index = 0;
+    result->length = length;
+    result->memblock = pa_memblock_new(s->core->mempool, length);
+
+    pa_sink_render_into_full(s, result);
+}
+
+/* Called from main thread */
+pa_usec_t pa_sink_get_latency(pa_sink *s) {
+    pa_usec_t usec = 0;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    /* The returned value is supposed to be in the time domain of the sound card! */
+
+    if (!PA_SINK_IS_OPENED(s->state))
+        return 0;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
+
+    return usec;
+}
+
+/* Called from main thread */
+void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
+    pa_bool_t changed;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(volume);
+
+    changed = !pa_cvolume_equal(volume, &s->volume);
+    s->volume = *volume;
+
+    if (s->set_volume && s->set_volume(s) < 0)
+        s->set_volume = NULL;
+
+    if (!s->set_volume)
+        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL);
+
+    if (changed)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main thread */
+const pa_cvolume *pa_sink_get_volume(pa_sink *s) {
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (s->refresh_volume) {
+        struct pa_cvolume old_volume = s->volume;
+
+        if (s->get_volume && s->get_volume(s) < 0)
+            s->get_volume = NULL;
+
+        if (!s->get_volume)
+            pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
+
+        if (!pa_cvolume_equal(&old_volume, &s->volume))
+            pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
+    return &s->volume;
+}
+
+/* Called from main thread */
+void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
+    pa_bool_t changed;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    changed = s->muted != mute;
+    s->muted = mute;
+
+    if (s->set_mute && s->set_mute(s) < 0)
+        s->set_mute = NULL;
+
+    if (!s->set_mute)
+        pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+
+    if (changed)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main thread */
+pa_bool_t pa_sink_get_mute(pa_sink *s) {
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (s->refresh_muted) {
+        pa_bool_t old_muted = s->muted;
+
+        if (s->get_mute && s->get_mute(s) < 0)
+            s->get_mute = NULL;
+
+        if (!s->get_mute)
+            pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
+
+        if (old_muted != s->muted)
+            pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
+    return s->muted;
+}
+
+/* Called from main thread */
+void pa_sink_set_description(pa_sink *s, const char *description) {
+    const char *old;
+    pa_sink_assert_ref(s);
+
+    if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
+        return;
+
+    old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+    if (old && description && !strcmp(old, description))
+        return;
+
+    if (description)
+        pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
+    else
+        pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+    if (s->monitor_source) {
+        char *n;
+
+        n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name);
+        pa_source_set_description(s->monitor_source, n);
+        pa_xfree(n);
+    }
+
+    if (PA_SINK_IS_LINKED(s->state)) {
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
+    }
+}
+
+/* Called from main thread */
+unsigned pa_sink_linked_by(pa_sink *s) {
+    unsigned ret;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    ret = pa_idxset_size(s->inputs);
+
+    /* We add in the number of streams connected to us here. Please
+     * not the asymmmetry to pa_sink_used_by()! */
+
+    if (s->monitor_source)
+        ret += pa_source_linked_by(s->monitor_source);
+
+    return ret;
+}
+
+/* Called from main thread */
+unsigned pa_sink_used_by(pa_sink *s) {
+    unsigned ret;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    ret = pa_idxset_size(s->inputs);
+    pa_assert(ret >= s->n_corked);
+
+    /* Streams connected to our monitor source do not matter for
+     * pa_sink_used_by()!.*/
+
+    return ret - s->n_corked;
+}
+
+/* Called from IO thread, except when it is not */
+int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_sink *s = PA_SINK(o);
+    pa_sink_assert_ref(s);
+
+    switch ((pa_sink_message_t) code) {
+
+        case PA_SINK_MESSAGE_ADD_INPUT: {
+            pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+            /* If you change anything here, make sure to change the
+             * sink input handling a few lines down at
+             * PA_SINK_MESSAGE_FINISH_MOVE, too. */
+
+            pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
+
+            /* Since the caller sleeps in pa_sink_input_put(), we can
+             * safely access data outside of thread_info even though
+             * it is mutable */
+
+            if ((i->thread_info.sync_prev = i->sync_prev)) {
+                pa_assert(i->sink == i->thread_info.sync_prev->sink);
+                pa_assert(i->sync_prev->sync_next == i);
+                i->thread_info.sync_prev->thread_info.sync_next = i;
+            }
+
+            if ((i->thread_info.sync_next = i->sync_next)) {
+                pa_assert(i->sink == i->thread_info.sync_next->sink);
+                pa_assert(i->sync_next->sync_prev == i);
+                i->thread_info.sync_next->thread_info.sync_prev = i;
+            }
+
+            pa_assert(!i->thread_info.attached);
+            i->thread_info.attached = TRUE;
+
+            if (i->attach)
+                i->attach(i);
+
+            pa_sink_input_set_state_within_thread(i, i->state);
+
+            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+            pa_sink_input_update_max_request(i, s->thread_info.max_request);
+
+            pa_sink_invalidate_requested_latency(s);
+
+            /* We don't rewind here automatically. This is left to the
+             * sink input implementor because some sink inputs need a
+             * slow start, i.e. need some time to buffer client
+             * samples before beginning streaming. */
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_REMOVE_INPUT: {
+            pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+            /* If you change anything here, make sure to change the
+             * sink input handling a few lines down at
+             * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
+
+            if (i->detach)
+                i->detach(i);
+
+            pa_sink_input_set_state_within_thread(i, i->state);
+
+            pa_assert(i->thread_info.attached);
+            i->thread_info.attached = FALSE;
+
+            /* Since the caller sleeps in pa_sink_input_unlink(),
+             * we can safely access data outside of thread_info even
+             * though it is mutable */
+
+            pa_assert(!i->thread_info.sync_prev);
+            pa_assert(!i->thread_info.sync_next);
+
+            if (i->thread_info.sync_prev) {
+                i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next;
+                i->thread_info.sync_prev = NULL;
+            }
+
+            if (i->thread_info.sync_next) {
+                i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev;
+                i->thread_info.sync_next = NULL;
+            }
+
+            if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
+                pa_sink_input_unref(i);
+
+            pa_sink_invalidate_requested_latency(s);
+            pa_sink_request_rewind(s, 0);
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_START_MOVE: {
+            pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+            /* We don't support moving synchronized streams. */
+            pa_assert(!i->sync_prev);
+            pa_assert(!i->sync_next);
+            pa_assert(!i->thread_info.sync_next);
+            pa_assert(!i->thread_info.sync_prev);
+
+            if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+                pa_usec_t usec = 0;
+                size_t sink_nbytes, total_nbytes;
+
+                /* Get the latency of the sink */
+                if (PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+                    usec = 0;
+
+                sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
+                total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
+
+                if (total_nbytes > 0) {
+                    i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes;
+                    i->thread_info.rewrite_flush = TRUE;
+                    pa_sink_input_process_rewind(i, sink_nbytes);
+                }
+            }
+
+            if (i->detach)
+                i->detach(i);
+
+            pa_assert(i->thread_info.attached);
+            i->thread_info.attached = FALSE;
+
+            /* Let's remove the sink input ...*/
+            if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
+                pa_sink_input_unref(i);
+
+            pa_sink_invalidate_requested_latency(s);
+
+            pa_log_debug("Requesting rewind due to started move");
+            pa_sink_request_rewind(s, 0);
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_FINISH_MOVE: {
+            pa_sink_input *i = PA_SINK_INPUT(userdata);
+
+            /* We don't support moving synchronized streams. */
+            pa_assert(!i->sync_prev);
+            pa_assert(!i->sync_next);
+            pa_assert(!i->thread_info.sync_next);
+            pa_assert(!i->thread_info.sync_prev);
+
+            pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
+
+            pa_assert(!i->thread_info.attached);
+            i->thread_info.attached = TRUE;
+
+            if (i->attach)
+                i->attach(i);
+
+            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+            pa_sink_input_update_max_request(i, s->thread_info.max_request);
+
+            pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
+
+            if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
+                pa_usec_t usec = 0;
+                size_t nbytes;
+
+                /* Get the latency of the sink */
+                if (PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+                    usec = 0;
+
+                nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
+
+                if (nbytes > 0)
+                    pa_sink_input_drop(i, nbytes);
+
+                pa_log_debug("Requesting rewind due to finished move");
+                pa_sink_request_rewind(s, nbytes);
+            }
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_VOLUME:
+            s->thread_info.soft_volume = *((pa_cvolume*) userdata);
+
+            pa_sink_request_rewind(s, 0);
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_MUTE:
+            s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+
+            pa_sink_request_rewind(s, 0);
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_VOLUME:
+            *((pa_cvolume*) userdata) = s->thread_info.soft_volume;
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_MUTE:
+            *((pa_bool_t*) userdata) = s->thread_info.soft_muted;
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_STATE:
+
+            s->thread_info.state = PA_PTR_TO_UINT(userdata);
+            return 0;
+
+        case PA_SINK_MESSAGE_DETACH:
+
+            /* Detach all streams */
+            pa_sink_detach_within_thread(s);
+            return 0;
+
+        case PA_SINK_MESSAGE_ATTACH:
+
+            /* Reattach all streams */
+            pa_sink_attach_within_thread(s);
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
+
+            pa_usec_t *usec = userdata;
+            *usec = pa_sink_get_requested_latency_within_thread(s);
+
+            if (*usec == (pa_usec_t) -1)
+                *usec = s->thread_info.max_latency;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
+            pa_usec_t *r = userdata;
+
+            pa_sink_update_latency_range(s, r[0], r[1]);
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_GET_LATENCY_RANGE: {
+            pa_usec_t *r = userdata;
+
+            r[0] = s->thread_info.min_latency;
+            r[1] = s->thread_info.max_latency;
+
+            return 0;
+        }
+
+        case PA_SINK_MESSAGE_GET_MAX_REWIND:
+
+            *((size_t*) userdata) = s->thread_info.max_rewind;
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_MAX_REQUEST:
+
+            *((size_t*) userdata) = s->thread_info.max_request;
+            return 0;
+
+        case PA_SINK_MESSAGE_GET_LATENCY:
+        case PA_SINK_MESSAGE_MAX:
+            ;
+    }
+
+    return -1;
+}
+
+/* Called from main thread */
+int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
+    pa_sink *sink;
+    uint32_t idx;
+    int ret = 0;
+
+    pa_core_assert_ref(c);
+
+    for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx)))
+        ret -= pa_sink_suspend(sink, suspend) < 0;
+
+    return ret;
+}
+
+/* Called from main thread */
+void pa_sink_detach(pa_sink *s) {
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
+}
+
+/* Called from main thread */
+void pa_sink_attach(pa_sink *s) {
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
+}
+
+/* Called from IO thread */
+void pa_sink_detach_within_thread(pa_sink *s) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+        if (i->detach)
+            i->detach(i);
+
+    if (s->monitor_source)
+        pa_source_detach_within_thread(s->monitor_source);
+}
+
+/* Called from IO thread */
+void pa_sink_attach_within_thread(pa_sink *s) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+        if (i->attach)
+            i->attach(i);
+
+    if (s->monitor_source)
+        pa_source_attach_within_thread(s->monitor_source);
+}
+
+/* Called from IO thread */
+void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    if (nbytes <= 0)
+        nbytes = s->thread_info.max_rewind;
+
+    nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
+
+    if (nbytes <= s->thread_info.rewind_nbytes)
+        return;
+
+    s->thread_info.rewind_nbytes = nbytes;
+
+    if (s->request_rewind)
+        s->request_rewind(s);
+}
+
+/* Called from IO thread */
+pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
+    pa_usec_t result = (pa_usec_t) -1;
+    pa_sink_input *i;
+    void *state = NULL;
+    pa_usec_t monitor_latency;
+
+    pa_sink_assert_ref(s);
+
+    if (s->thread_info.requested_latency_valid)
+        return s->thread_info.requested_latency;
+
+    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+
+        if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
+            (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
+            result = i->thread_info.requested_sink_latency;
+
+    monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
+
+    if (monitor_latency != (pa_usec_t) -1 &&
+        (result == (pa_usec_t) -1 || result > monitor_latency))
+        result = monitor_latency;
+
+    if (result != (pa_usec_t) -1) {
+        if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
+            result = s->thread_info.max_latency;
+
+        if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
+            result = s->thread_info.min_latency;
+    }
+
+    s->thread_info.requested_latency = result;
+    s->thread_info.requested_latency_valid = TRUE;
+
+    return result;
+}
+
+/* Called from main thread */
+pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
+    pa_usec_t usec = 0;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (!PA_SINK_IS_OPENED(s->state))
+        return 0;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+    return usec;
+}
+
+/* Called from IO thread */
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+
+    if (max_rewind == s->thread_info.max_rewind)
+        return;
+
+    s->thread_info.max_rewind = max_rewind;
+
+    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+        while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+    }
+
+    if (s->monitor_source)
+        pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
+}
+
+/* Called from IO thread */
+void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+
+    if (max_request == s->thread_info.max_request)
+        return;
+
+    s->thread_info.max_request = max_request;
+
+    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+        while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+            pa_sink_input_update_max_request(i, s->thread_info.max_request);
+    }
+}
+
+/* Called from IO thread */
+void pa_sink_invalidate_requested_latency(pa_sink *s) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+
+    s->thread_info.requested_latency_valid = FALSE;
+
+    if (s->update_requested_latency)
+        s->update_requested_latency(s);
+
+    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+        if (i->update_sink_requested_latency)
+            i->update_sink_requested_latency(i);
+}
+
+/* Called from main thread */
+void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+    pa_sink_assert_ref(s);
+
+    /* min_latency == 0:           no limit
+     * min_latency == (size_t) -1: default limit
+     * min_latency anything else:  specified limit
+     *
+     * Similar for max_latency */
+
+    if (min_latency == (pa_usec_t) -1)
+        min_latency = DEFAULT_MIN_LATENCY;
+
+    if (max_latency == (pa_usec_t) -1)
+        max_latency = min_latency;
+
+    pa_assert(!min_latency || !max_latency ||
+              min_latency <= max_latency);
+
+    if (PA_SINK_IS_LINKED(s->state)) {
+        pa_usec_t r[2];
+
+        r[0] = min_latency;
+        r[1] = max_latency;
+
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
+    } else {
+        s->thread_info.min_latency = min_latency;
+        s->thread_info.max_latency = max_latency;
+
+        s->monitor_source->thread_info.min_latency = min_latency;
+        s->monitor_source->thread_info.max_latency = max_latency;
+
+        s->thread_info.requested_latency_valid = s->monitor_source->thread_info.requested_latency_valid = FALSE;
+    }
+}
+
+/* Called from main thread */
+void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
+   pa_sink_assert_ref(s);
+   pa_assert(min_latency);
+   pa_assert(max_latency);
+
+   if (PA_SINK_IS_LINKED(s->state)) {
+       pa_usec_t r[2] = { 0, 0 };
+
+       pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
+
+       *min_latency = r[0];
+       *max_latency = r[1];
+   } else {
+       *min_latency = s->thread_info.min_latency;
+       *max_latency = s->thread_info.max_latency;
+   }
+}
+
+/* Called from IO thread */
+void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+
+    s->thread_info.min_latency = min_latency;
+    s->thread_info.max_latency = max_latency;
+
+    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+        if (i->update_sink_latency_range)
+            i->update_sink_latency_range(i);
+
+    pa_sink_invalidate_requested_latency(s);
+
+    pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
+}
+
+size_t pa_sink_get_max_rewind(pa_sink *s) {
+    size_t r;
+    pa_sink_assert_ref(s);
+
+    if (!PA_SINK_IS_LINKED(s->state))
+        return s->thread_info.max_rewind;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
+
+    return r;
+}
+
+size_t pa_sink_get_max_request(pa_sink *s) {
+    size_t r;
+    pa_sink_assert_ref(s);
+
+    if (!PA_SINK_IS_LINKED(s->state))
+        return s->thread_info.max_request;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
+
+    return r;
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
new file mode 100644 (file)
index 0000000..b73944e
--- /dev/null
@@ -0,0 +1,276 @@
+#ifndef foopulsesinkhfoo
+#define foopulsesinkhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+typedef struct pa_sink pa_sink;
+
+#include <inttypes.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/msgobject.h>
+#include <pulsecore/rtpoll.h>
+
+#define PA_MAX_INPUTS_PER_SINK 32
+
+typedef enum pa_sink_state {
+    PA_SINK_INIT,
+    PA_SINK_RUNNING,
+    PA_SINK_SUSPENDED,
+    PA_SINK_IDLE,
+    PA_SINK_UNLINKED
+} pa_sink_state_t;
+
+static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) {
+    return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
+}
+
+static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
+    return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
+}
+
+struct pa_sink {
+    pa_msgobject parent;
+
+    uint32_t index;
+    pa_core *core;
+    pa_sink_state_t state;
+    pa_sink_flags_t flags;
+
+    char *name;
+    char *driver;                           /* may be NULL */
+    pa_proplist *proplist;
+
+    pa_module *module;                      /* may be NULL */
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+
+    pa_idxset *inputs;
+    unsigned n_corked;
+    pa_source *monitor_source;
+
+    pa_cvolume volume;
+    pa_bool_t muted;
+
+    pa_bool_t refresh_volume:1;
+    pa_bool_t refresh_muted:1;
+
+    pa_asyncmsgq *asyncmsgq;
+    pa_rtpoll *rtpoll;
+
+    pa_memchunk silence;
+
+    /* Called when the main loop requests a state change. Called from
+     * main loop context. If returns -1 the state change will be
+     * inhibited */
+    int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
+
+    /* Callled when the volume is queried. Called from main loop
+     * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message
+     * will be sent to the IO thread instead. If refresh_volume is
+     * FALSE neither this function is called nor a message is sent. */
+    int (*get_volume)(pa_sink *s);             /* may be NULL */
+
+    /* Called when the volume shall be changed. Called from main loop
+     * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message
+     * will be sent to the IO thread instead. */
+    int (*set_volume)(pa_sink *s);             /* dito */
+
+    /* Called when the mute setting is queried. Called from main loop
+     * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message
+     * will be sent to the IO thread instead. If refresh_mute is
+     * FALSE neither this function is called nor a message is sent.*/
+    int (*get_mute)(pa_sink *s);               /* dito */
+
+    /* Called when the mute setting shall be changed. Called from main
+     * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE
+     * message will be sent to the IO thread instead. */
+    int (*set_mute)(pa_sink *s);               /* dito */
+
+    /* Called when a rewind request is issued. Called from IO thread
+     * context. */
+    void (*request_rewind)(pa_sink *s);        /* dito */
+
+    /* Called when a the requested latency is changed. Called from IO
+     * thread context. */
+    void (*update_requested_latency)(pa_sink *s); /* dito */
+
+    /* Contains copies of the above data so that the real-time worker
+     * thread can work without access locking */
+    struct {
+        pa_sink_state_t state;
+        pa_hashmap *inputs;
+        pa_cvolume soft_volume;
+        pa_bool_t soft_muted:1;
+
+        pa_bool_t requested_latency_valid:1;
+        pa_usec_t requested_latency;
+
+        /* The number of bytes streams need to keep around as history to
+         * be able to satisfy every DMA buffer rewrite */
+        size_t max_rewind;
+
+        /* The number of bytes streams need to keep around to satisfy
+         * every DMA write request */
+        size_t max_request;
+
+        /* Maximum of what clients requested to rewind in this cycle */
+        size_t rewind_nbytes;
+
+        pa_usec_t min_latency; /* we won't go below this latency */
+        pa_usec_t max_latency; /* An upper limit for the latencies */
+    } thread_info;
+
+    void *userdata;
+};
+
+PA_DECLARE_CLASS(pa_sink);
+#define PA_SINK(s) (pa_sink_cast(s))
+
+typedef enum pa_sink_message {
+    PA_SINK_MESSAGE_ADD_INPUT,
+    PA_SINK_MESSAGE_REMOVE_INPUT,
+    PA_SINK_MESSAGE_GET_VOLUME,
+    PA_SINK_MESSAGE_SET_VOLUME,
+    PA_SINK_MESSAGE_GET_MUTE,
+    PA_SINK_MESSAGE_SET_MUTE,
+    PA_SINK_MESSAGE_GET_LATENCY,
+    PA_SINK_MESSAGE_GET_REQUESTED_LATENCY,
+    PA_SINK_MESSAGE_SET_STATE,
+    PA_SINK_MESSAGE_START_MOVE,
+    PA_SINK_MESSAGE_FINISH_MOVE,
+    PA_SINK_MESSAGE_ATTACH,
+    PA_SINK_MESSAGE_DETACH,
+    PA_SINK_MESSAGE_SET_LATENCY_RANGE,
+    PA_SINK_MESSAGE_GET_LATENCY_RANGE,
+    PA_SINK_MESSAGE_GET_MAX_REWIND,
+    PA_SINK_MESSAGE_GET_MAX_REQUEST,
+    PA_SINK_MESSAGE_MAX
+} pa_sink_message_t;
+
+typedef struct pa_sink_new_data {
+    char *name;
+    pa_bool_t namereg_fail;
+    pa_proplist *proplist;
+
+    const char *driver;
+    pa_module *module;
+
+    pa_sample_spec sample_spec;
+    pa_bool_t sample_spec_is_set;
+    pa_channel_map channel_map;
+    pa_bool_t channel_map_is_set;
+
+    pa_cvolume volume;
+    pa_bool_t volume_is_set;
+    pa_bool_t muted;
+    pa_bool_t muted_is_set;
+} pa_sink_new_data;
+
+pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data);
+void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name);
+void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec);
+void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map);
+void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume);
+void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);
+void pa_sink_new_data_done(pa_sink_new_data *data);
+
+/* To be called exclusively by the sink driver, from main context */
+
+pa_sink* pa_sink_new(
+        pa_core *core,
+        pa_sink_new_data *data,
+        pa_sink_flags_t flags);
+
+void pa_sink_put(pa_sink *s);
+void pa_sink_unlink(pa_sink* s);
+
+void pa_sink_set_description(pa_sink *s, const char *description);
+void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q);
+void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p);
+
+void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
+void pa_sink_detach(pa_sink *s);
+void pa_sink_attach(pa_sink *s);
+
+/* May be called by everyone, from main context */
+
+/* The returned value is supposed to be in the time domain of the sound card! */
+pa_usec_t pa_sink_get_latency(pa_sink *s);
+pa_usec_t pa_sink_get_requested_latency(pa_sink *s);
+void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+
+size_t pa_sink_get_max_rewind(pa_sink *s);
+size_t pa_sink_get_max_request(pa_sink *s);
+
+int pa_sink_update_status(pa_sink*s);
+int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);
+int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
+
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume);
+const pa_cvolume *pa_sink_get_volume(pa_sink *sink);
+void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
+pa_bool_t pa_sink_get_mute(pa_sink *sink);
+
+unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
+unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
+#define pa_sink_get_state(s) ((s)->state)
+
+/* To be called exclusively by the sink driver, from IO context */
+
+void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
+void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
+void pa_sink_render_into(pa_sink*s, pa_memchunk *target);
+void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target);
+
+void pa_sink_process_rewind(pa_sink *s, size_t nbytes);
+
+int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+void pa_sink_attach_within_thread(pa_sink *s);
+void pa_sink_detach_within_thread(pa_sink *s);
+
+pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s);
+
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind);
+void pa_sink_set_max_request(pa_sink *s, size_t max_request);
+
+void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
+/* To be called exclusively by sink input drivers, from IO context */
+
+void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
+
+void pa_sink_invalidate_requested_latency(pa_sink *s);
+
+#endif
similarity index 57%
rename from polyp/sioman.c
rename to src/pulsecore/sioman.c
index 8d7b136c855085b8599364e1a5f70260f5a6524b..7e5b186ca8ae769d74246cd25b3c1cbfe775a4dd 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <config.h>
 #endif
 
-#include <assert.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+
 #include "sioman.h"
 
-static int stdio_inuse = 0;
+static pa_atomic_t stdio_inuse = PA_ATOMIC_INIT(0);
 
 int pa_stdio_acquire(void) {
-    if (stdio_inuse)
-        return -1;
-
-    stdio_inuse = 1;
-    return 0;
+    return pa_atomic_cmpxchg(&stdio_inuse, 0, 1) ? 0 : -1;
 }
 
 void pa_stdio_release(void) {
-    assert(stdio_inuse);
-    stdio_inuse = 0;
-} 
+    pa_assert_se(pa_atomic_cmpxchg(&stdio_inuse, 1, 0));
+}
similarity index 70%
rename from polyp/sioman.h
rename to src/pulsecore/sioman.h
index 840d93f23b49968bb2ba202bc3536ff3d855da92..d0cacc9b00d0fa8efd1a074736da23084f4c67b2 100644 (file)
@@ -1,23 +1,23 @@
 #ifndef foosiomanhfoo
 #define foosiomanhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c
new file mode 100644 (file)
index 0000000..e69a63d
--- /dev/null
@@ -0,0 +1,553 @@
+/***
+    This file is part of PulseAudio.
+
+    Copyright 2004-2006 Lennart Poettering
+    Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+    PulseAudio is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as
+    published by the Free Software Foundation; either version 2.1 of the
+    License, or (at your option) any later version.
+
+    PulseAudio is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with PulseAudio; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+    USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* #undef HAVE_LIBASYNCNS */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_LIBASYNCNS
+#include <asyncns.h>
+#endif
+
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/winsock.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/parseaddr.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+
+#include "socket-client.h"
+
+#define CONNECT_TIMEOUT 5
+
+struct pa_socket_client {
+    PA_REFCNT_DECLARE;
+    pa_mainloop_api *mainloop;
+    int fd;
+    pa_io_event *io_event;
+    pa_time_event *timeout_event;
+    pa_defer_event *defer_event;
+    pa_socket_client_cb_t callback;
+    void *userdata;
+    pa_bool_t local;
+#ifdef HAVE_LIBASYNCNS
+    asyncns_t *asyncns;
+    asyncns_query_t * asyncns_query;
+    pa_io_event *asyncns_io_event;
+#endif
+};
+
+static pa_socket_client* socket_client_new(pa_mainloop_api *m) {
+    pa_socket_client *c;
+    pa_assert(m);
+
+    c = pa_xnew(pa_socket_client, 1);
+    PA_REFCNT_INIT(c);
+    c->mainloop = m;
+    c->fd = -1;
+    c->io_event = NULL;
+    c->timeout_event = NULL;
+    c->defer_event = NULL;
+    c->callback = NULL;
+    c->userdata = NULL;
+    c->local = FALSE;
+
+#ifdef HAVE_LIBASYNCNS
+    c->asyncns = NULL;
+    c->asyncns_io_event = NULL;
+    c->asyncns_query = NULL;
+#endif
+
+    return c;
+}
+
+static void free_events(pa_socket_client *c) {
+    pa_assert(c);
+
+    if (c->io_event) {
+        c->mainloop->io_free(c->io_event);
+        c->io_event = NULL;
+    }
+
+    if (c->timeout_event) {
+        c->mainloop->time_free(c->timeout_event);
+        c->timeout_event = NULL;
+    }
+
+    if (c->defer_event) {
+        c->mainloop->defer_free(c->defer_event);
+        c->defer_event = NULL;
+    }
+}
+
+static void do_call(pa_socket_client *c) {
+    pa_iochannel *io = NULL;
+    int error;
+    socklen_t lerror;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->callback);
+
+    pa_socket_client_ref(c);
+
+    if (c->fd < 0)
+        goto finish;
+
+    lerror = sizeof(error);
+    if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &lerror) < 0) {
+        pa_log("getsockopt(): %s", pa_cstrerror(errno));
+        goto finish;
+    }
+
+    if (lerror != sizeof(error)) {
+        pa_log("getsockopt() returned invalid size.");
+        goto finish;
+    }
+
+    if (error != 0) {
+        pa_log_debug("connect(): %s", pa_cstrerror(error));
+        errno = error;
+        goto finish;
+    }
+
+    io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
+    pa_assert(io);
+
+finish:
+    if (!io && c->fd >= 0)
+        pa_close(c->fd);
+    c->fd = -1;
+
+    free_events(c);
+
+    pa_assert(c->callback);
+    c->callback(c, io, c->userdata);
+
+    pa_socket_client_unref(c);
+}
+
+static void connect_defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+    pa_socket_client *c = userdata;
+
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->defer_event == e);
+
+    do_call(c);
+}
+
+static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+    pa_socket_client *c = userdata;
+
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->io_event == e);
+    pa_assert(fd >= 0);
+
+    do_call(c);
+}
+
+static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
+    int r;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(sa);
+    pa_assert(len > 0);
+
+    pa_make_fd_nonblock(c->fd);
+
+    if ((r = connect(c->fd, sa, len)) < 0) {
+#ifdef OS_IS_WIN32
+        if (WSAGetLastError() != EWOULDBLOCK) {
+            pa_log_debug("connect(): %d", WSAGetLastError());
+#else
+        if (errno != EINPROGRESS) {
+            pa_log_debug("connect(): %s (%d)", pa_cstrerror(errno), errno);
+#endif
+            return -1;
+        }
+
+        pa_assert_se(c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c));
+    } else
+        pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c));
+
+    return 0;
+}
+
+pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port) {
+    struct sockaddr_in sa;
+
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons(port);
+    sa.sin_addr.s_addr = htonl(address);
+
+    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
+}
+
+#ifdef HAVE_SYS_UN_H
+
+pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
+    struct sockaddr_un sa;
+
+    pa_assert(m);
+    pa_assert(filename);
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sun_family = AF_UNIX;
+    pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
+
+    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
+}
+
+#else /* HAVE_SYS_UN_H */
+
+pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) {
+    return NULL;
+}
+
+#endif /* HAVE_SYS_UN_H */
+
+static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) {
+    pa_assert(c);
+    pa_assert(sa);
+    pa_assert(salen);
+
+    c->local = pa_socket_address_is_local(sa);
+
+    if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
+        pa_log("socket(): %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    pa_make_fd_cloexec(c->fd);
+
+    if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
+        pa_make_tcp_socket_low_delay(c->fd);
+    else
+        pa_make_socket_low_delay(c->fd);
+
+    if (do_connect(c, sa, salen) < 0)
+        return -1;
+
+    return 0;
+}
+
+pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {
+    pa_socket_client *c;
+
+    pa_assert(m);
+    pa_assert(sa);
+    pa_assert(salen > 0);
+
+    pa_assert_se(c = socket_client_new(m));
+
+    if (sockaddr_prepare(c, sa, salen) < 0)
+        goto fail;
+
+    return c;
+
+fail:
+    pa_socket_client_unref(c);
+    return NULL;
+}
+
+static void socket_client_free(pa_socket_client *c) {
+    pa_assert(c);
+    pa_assert(c->mainloop);
+
+    free_events(c);
+
+    if (c->fd >= 0)
+        pa_close(c->fd);
+
+#ifdef HAVE_LIBASYNCNS
+    if (c->asyncns_query)
+        asyncns_cancel(c->asyncns, c->asyncns_query);
+    if (c->asyncns)
+        asyncns_free(c->asyncns);
+    if (c->asyncns_io_event)
+        c->mainloop->io_free(c->asyncns_io_event);
+#endif
+
+    pa_xfree(c);
+}
+
+void pa_socket_client_unref(pa_socket_client *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (PA_REFCNT_DEC(c) <= 0)
+        socket_client_free(c);
+}
+
+pa_socket_client* pa_socket_client_ref(pa_socket_client *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_REFCNT_INC(c);
+    return c;
+}
+
+void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    c->callback = on_connection;
+    c->userdata = userdata;
+}
+
+pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
+    struct sockaddr_in6 sa;
+
+    pa_assert(m);
+    pa_assert(address);
+    pa_assert(port > 0);
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sin6_family = AF_INET6;
+    sa.sin6_port = htons(port);
+    memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr));
+
+    return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
+}
+
+#ifdef HAVE_LIBASYNCNS
+
+static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+    pa_socket_client *c = userdata;
+    struct addrinfo *res = NULL;
+    int ret;
+
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(c->asyncns_io_event == e);
+    pa_assert(fd >= 0);
+
+    if (asyncns_wait(c->asyncns, 0) < 0)
+        goto fail;
+
+    if (!asyncns_isdone(c->asyncns, c->asyncns_query))
+        return;
+
+    ret = asyncns_getaddrinfo_done(c->asyncns, c->asyncns_query, &res);
+    c->asyncns_query = NULL;
+
+    if (ret != 0 || !res)
+        goto fail;
+
+    if (res->ai_addr)
+        sockaddr_prepare(c, res->ai_addr, res->ai_addrlen);
+
+    asyncns_freeaddrinfo(res);
+
+    m->io_free(c->asyncns_io_event);
+    c->asyncns_io_event = NULL;
+    return;
+
+fail:
+    m->io_free(c->asyncns_io_event);
+    c->asyncns_io_event = NULL;
+
+    errno = EHOSTUNREACH;
+    do_call(c);
+    return;
+
+}
+
+#endif
+
+static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    pa_socket_client *c = userdata;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(tv);
+    pa_assert(c);
+
+    if (c->fd >= 0) {
+        pa_close(c->fd);
+        c->fd = -1;
+    }
+
+    errno = ETIMEDOUT;
+    do_call(c);
+}
+
+static void start_timeout(pa_socket_client *c) {
+    struct timeval tv;
+    pa_assert(c);
+    pa_assert(!c->timeout_event);
+
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, CONNECT_TIMEOUT * 1000000);
+    c->timeout_event = c->mainloop->time_new(c->mainloop, &tv, timeout_cb, c);
+}
+
+pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*name, uint16_t default_port) {
+    pa_socket_client *c = NULL;
+    pa_parsed_address a;
+
+    pa_assert(m);
+    pa_assert(name);
+
+    if (pa_parse_address(name, &a) < 0)
+        return NULL;
+
+    if (!a.port)
+        a.port = default_port;
+
+    switch (a.type) {
+        case PA_PARSED_ADDRESS_UNIX:
+            if ((c = pa_socket_client_new_unix(m, a.path_or_host)))
+                start_timeout(c);
+            break;
+
+        case PA_PARSED_ADDRESS_TCP4:  /* Fallthrough */
+        case PA_PARSED_ADDRESS_TCP6:  /* Fallthrough */
+        case PA_PARSED_ADDRESS_TCP_AUTO:{
+
+            struct addrinfo hints;
+            char port[12];
+
+            pa_snprintf(port, sizeof(port), "%u", (unsigned) a.port);
+
+            memset(&hints, 0, sizeof(hints));
+            hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC);
+            hints.ai_socktype = SOCK_STREAM;
+
+#if defined(HAVE_LIBASYNCNS)
+            {
+                asyncns_t *asyncns;
+
+                if (!(asyncns = asyncns_new(1)))
+                    goto finish;
+
+                pa_assert_se(c = socket_client_new(m));
+                c->asyncns = asyncns;
+                c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);
+                c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints);
+                pa_assert(c->asyncns_query);
+                start_timeout(c);
+            }
+#elif defined(HAVE_GETADDRINFO)
+            {
+                int ret;
+                struct addrinfo *res = NULL;
+
+                ret = getaddrinfo(a.path_or_host, port, &hints, &res);
+
+                if (ret < 0 || !res)
+                    goto finish;
+
+                if (res->ai_addr) {
+                    if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen)))
+                        start_timeout(c);
+                }
+
+                freeaddrinfo(res);
+            }
+#else
+            {
+                struct hostent *host = NULL;
+                struct sockaddr_in s;
+
+                /* FIXME: PF_INET6 support */
+                if (hints.ai_family == PF_INET6) {
+                    pa_log_error("IPv6 is not supported on Windows");
+                    goto finish;
+                }
+
+                host = gethostbyname(a.path_or_host);
+                if (!host) {
+                    unsigned int addr = inet_addr(a.path_or_host);
+                    if (addr != INADDR_NONE)
+                        host = gethostbyaddr((char*)&addr, 4, AF_INET);
+                }
+
+                if (!host)
+                    goto finish;
+
+                s.sin_family = AF_INET;
+                memcpy(&s.sin_addr, host->h_addr, sizeof(struct in_addr));
+                s.sin_port = htons(a.port);
+
+                if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s))))
+                    start_timeout(c);
+            }
+#endif /* HAVE_LIBASYNCNS */
+        }
+    }
+
+finish:
+    pa_xfree(a.path_or_host);
+    return c;
+
+}
+
+/* Return non-zero when the target sockaddr is considered
+   local. "local" means UNIX socket or TCP socket on localhost. Other
+   local IP addresses are not considered local. */
+pa_bool_t pa_socket_client_is_local(pa_socket_client *c) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    return c->local;
+}
diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h
new file mode 100644 (file)
index 0000000..9ceeadd
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef foosocketclienthfoo
+#define foosocketclienthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulsecore/iochannel.h>
+
+struct sockaddr;
+
+typedef struct pa_socket_client pa_socket_client;
+
+typedef void (*pa_socket_client_cb_t)(pa_socket_client *c, pa_iochannel*io, void *userdata);
+
+pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port);
+pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port);
+pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename);
+pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen);
+pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char *a, uint16_t default_port);
+
+pa_socket_client* pa_socket_client_ref(pa_socket_client *c);
+void pa_socket_client_unref(pa_socket_client *c);
+
+void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata);
+
+pa_bool_t pa_socket_client_is_local(pa_socket_client *c);
+
+#endif
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
new file mode 100644 (file)
index 0000000..9885a02
--- /dev/null
@@ -0,0 +1,531 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#ifndef SUN_LEN
+#define SUN_LEN(ptr) \
+    ((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
+#endif
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#ifndef HAVE_INET_NTOP
+#include "inet_ntop.h"
+#endif
+
+#ifndef HAVE_INET_PTON
+#include "inet_pton.h"
+#endif
+
+#include "winsock.h"
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/socket-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/refcnt.h>
+
+#include "socket-server.h"
+
+struct pa_socket_server {
+    PA_REFCNT_DECLARE;
+    int fd;
+    char *filename;
+    char *tcpwrap_service;
+
+    pa_socket_server_on_connection_cb_t on_connection;
+    void *userdata;
+
+    pa_io_event *io_event;
+    pa_mainloop_api *mainloop;
+    enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type;
+};
+
+static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    pa_socket_server *s = userdata;
+    pa_iochannel *io;
+    int nfd;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(s->mainloop == mainloop);
+    pa_assert(s->io_event == e);
+    pa_assert(e);
+    pa_assert(fd >= 0);
+    pa_assert(fd == s->fd);
+
+    pa_socket_server_ref(s);
+
+    if ((nfd = accept(fd, NULL, NULL)) < 0) {
+        pa_log("accept(): %s", pa_cstrerror(errno));
+        goto finish;
+    }
+
+    pa_make_fd_cloexec(nfd);
+
+    if (!s->on_connection) {
+        pa_close(nfd);
+        goto finish;
+    }
+
+#ifdef HAVE_LIBWRAP
+
+    if (s->tcpwrap_service) {
+        struct request_info req;
+
+        request_init(&req, RQ_DAEMON, s->tcpwrap_service, RQ_FILE, nfd, NULL);
+        fromhost(&req);
+        if (!hosts_access(&req)) {
+            pa_log_warn("TCP connection refused by tcpwrap.");
+            pa_close(nfd);
+            goto finish;
+        }
+
+        pa_log_info("TCP connection accepted by tcpwrap.");
+    }
+#endif
+
+    /* There should be a check for socket type here */
+    if (s->type == SOCKET_SERVER_IPV4)
+        pa_make_tcp_socket_low_delay(fd);
+    else
+        pa_make_socket_low_delay(fd);
+
+    pa_assert_se(io = pa_iochannel_new(s->mainloop, nfd, nfd));
+    s->on_connection(s, io, s->userdata);
+
+finish:
+    pa_socket_server_unref(s);
+}
+
+pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
+    pa_socket_server *s;
+
+    pa_assert(m);
+    pa_assert(fd >= 0);
+
+    s = pa_xnew(pa_socket_server, 1);
+    PA_REFCNT_INIT(s);
+    s->fd = fd;
+    s->filename = NULL;
+    s->on_connection = NULL;
+    s->userdata = NULL;
+    s->tcpwrap_service = NULL;
+
+    s->mainloop = m;
+    pa_assert_se(s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s));
+
+    s->type = SOCKET_SERVER_GENERIC;
+
+    return s;
+}
+
+pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_REFCNT_INC(s);
+    return s;
+}
+
+#ifdef HAVE_SYS_UN_H
+
+pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
+    int fd = -1;
+    struct sockaddr_un sa;
+    pa_socket_server *s;
+
+    pa_assert(m);
+    pa_assert(filename);
+
+    if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+        pa_log("socket(): %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_make_fd_cloexec(fd);
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sun_family = AF_UNIX;
+    pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
+
+    pa_make_socket_low_delay(fd);
+
+    if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) {
+        pa_log("bind(): %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    /* Allow access from all clients. Sockets like this one should
+     * always be put inside a directory with proper access rights,
+     * because not all OS check the access rights on the socket
+     * inodes. */
+    chmod(filename, 0777);
+
+    if (listen(fd, 5) < 0) {
+        pa_log("listen(): %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_assert_se(s = pa_socket_server_new(m, fd));
+
+    s->filename = pa_xstrdup(filename);
+    s->type = SOCKET_SERVER_UNIX;
+
+    return s;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return NULL;
+}
+
+#else /* HAVE_SYS_UN_H */
+
+pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
+    return NULL;
+}
+
+#endif /* HAVE_SYS_UN_H */
+
+pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service) {
+    pa_socket_server *ss;
+    int fd = -1;
+    struct sockaddr_in sa;
+    int on = 1;
+
+    pa_assert(m);
+    pa_assert(port);
+
+    if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+        pa_log("socket(PF_INET): %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_make_fd_cloexec(fd);
+
+#ifdef SO_REUSEADDR
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+        pa_log("setsockopt(): %s", pa_cstrerror(errno));
+#endif
+
+    pa_make_tcp_socket_low_delay(fd);
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons(port);
+    sa.sin_addr.s_addr = htonl(address);
+
+    if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+        pa_log("bind(): %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (listen(fd, 5) < 0) {
+        pa_log("listen(): %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if ((ss = pa_socket_server_new(m, fd))) {
+        ss->type = SOCKET_SERVER_IPV4;
+        ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
+    }
+
+    return ss;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return NULL;
+}
+
+pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port, const char *tcpwrap_service) {
+    pa_socket_server *ss;
+    int fd = -1;
+    struct sockaddr_in6 sa;
+    int on;
+
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
+        pa_log("socket(PF_INET6): %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    pa_make_fd_cloexec(fd);
+
+#ifdef IPV6_V6ONLY
+    on = 1;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
+        pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno));
+#endif
+
+#ifdef SO_REUSEADDR
+    on = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+        pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno));
+#endif
+
+    pa_make_tcp_socket_low_delay(fd);
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sin6_family = AF_INET6;
+    sa.sin6_port = htons(port);
+    memcpy(sa.sin6_addr.s6_addr, address, 16);
+
+    if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+        pa_log("bind(): %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if (listen(fd, 5) < 0) {
+        pa_log("listen(): %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if ((ss = pa_socket_server_new(m, fd))) {
+        ss->type = SOCKET_SERVER_IPV6;
+        ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
+    }
+
+    return ss;
+
+fail:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return NULL;
+}
+
+pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    return pa_socket_server_new_ipv4(m, INADDR_LOOPBACK, port, tcpwrap_service);
+}
+
+pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    return pa_socket_server_new_ipv6(m, in6addr_loopback.s6_addr, port, tcpwrap_service);
+}
+
+pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    return pa_socket_server_new_ipv4(m, INADDR_ANY, port, tcpwrap_service);
+}
+
+pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
+    pa_assert(m);
+    pa_assert(port > 0);
+
+    return pa_socket_server_new_ipv6(m, in6addr_any.s6_addr, port, tcpwrap_service);
+}
+
+pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {
+    struct in_addr ipv4;
+
+    pa_assert(m);
+    pa_assert(name);
+    pa_assert(port > 0);
+
+    if (inet_pton(AF_INET, name, &ipv4) > 0)
+        return pa_socket_server_new_ipv4(m, ntohl(ipv4.s_addr), port, tcpwrap_service);
+
+    return NULL;
+}
+
+pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {
+    struct in6_addr ipv6;
+
+    pa_assert(m);
+    pa_assert(name);
+    pa_assert(port > 0);
+
+    if (inet_pton(AF_INET6, name, &ipv6) > 0)
+        return pa_socket_server_new_ipv6(m, ipv6.s6_addr, port, tcpwrap_service);
+
+    return NULL;
+}
+
+static void socket_server_free(pa_socket_server*s) {
+    pa_assert(s);
+
+    if (s->filename) {
+        unlink(s->filename);
+        pa_xfree(s->filename);
+    }
+
+    pa_close(s->fd);
+
+    pa_xfree(s->tcpwrap_service);
+
+    s->mainloop->io_free(s->io_event);
+    pa_xfree(s);
+}
+
+void pa_socket_server_unref(pa_socket_server *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (PA_REFCNT_DEC(s) <= 0)
+        socket_server_free(s);
+}
+
+void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t on_connection, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    s->on_connection = on_connection;
+    s->userdata = userdata;
+}
+
+char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(c);
+    pa_assert(l > 0);
+
+    switch (s->type) {
+        case SOCKET_SERVER_IPV6: {
+            struct sockaddr_in6 sa;
+            socklen_t sa_len = sizeof(sa);
+
+            if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
+                pa_log("getsockname(): %s", pa_cstrerror(errno));
+                return NULL;
+            }
+
+            if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) {
+                char fqdn[256];
+                if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
+                    return NULL;
+
+                pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
+
+            } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
+                char hn[256];
+                if (!pa_get_host_name(hn, sizeof(hn)))
+                    return NULL;
+
+                pa_snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
+            } else {
+                char ip[INET6_ADDRSTRLEN];
+
+                if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) {
+                    pa_log("inet_ntop(): %s", pa_cstrerror(errno));
+                    return NULL;
+                }
+
+                pa_snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
+            }
+
+            return c;
+        }
+
+        case SOCKET_SERVER_IPV4: {
+            struct sockaddr_in sa;
+            socklen_t sa_len = sizeof(sa);
+
+            if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
+                pa_log("getsockname(): %s", pa_cstrerror(errno));
+                return NULL;
+            }
+
+            if (sa.sin_addr.s_addr == INADDR_ANY) {
+                char fqdn[256];
+                if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
+                    return NULL;
+
+                pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
+            } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
+                char hn[256];
+                if (!pa_get_host_name(hn, sizeof(hn)))
+                    return NULL;
+
+                pa_snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
+            } else {
+                char ip[INET_ADDRSTRLEN];
+
+                if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) {
+                    pa_log("inet_ntop(): %s", pa_cstrerror(errno));
+                    return NULL;
+                }
+
+                pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
+            }
+
+            return c;
+        }
+
+        case SOCKET_SERVER_UNIX: {
+            char hn[256];
+
+            if (!s->filename)
+                return NULL;
+
+            if (!pa_get_host_name(hn, sizeof(hn)))
+                return NULL;
+
+            pa_snprintf(c, l, "{%s}unix:%s", hn, s->filename);
+            return c;
+        }
+
+        default:
+            return NULL;
+    }
+}
diff --git a/src/pulsecore/socket-server.h b/src/pulsecore/socket-server.h
new file mode 100644 (file)
index 0000000..1edfb43
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef foosocketserverhfoo
+#define foosocketserverhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+#include <pulse/mainloop-api.h>
+#include <pulsecore/iochannel.h>
+
+/* It is safe to destroy the calling socket_server object from the callback */
+
+typedef struct pa_socket_server pa_socket_server;
+
+pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd);
+pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename);
+pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service);
+pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service);
+
+void pa_socket_server_unref(pa_socket_server*s);
+pa_socket_server* pa_socket_server_ref(pa_socket_server *s);
+
+typedef void (*pa_socket_server_on_connection_cb_t)(pa_socket_server*s, pa_iochannel *io, void *userdata);
+
+void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t connection_cb, void *userdata);
+
+char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l);
+
+#endif
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
new file mode 100644 (file)
index 0000000..f721f69
--- /dev/null
@@ -0,0 +1,321 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2004 Joe Marcus Clarke
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifndef HAVE_INET_NTOP
+#include "inet_ntop.h"
+#endif
+
+#include "winsock.h"
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "socket-util.h"
+
+void pa_socket_peer_to_string(int fd, char *c, size_t l) {
+    struct stat st;
+
+    pa_assert(fd >= 0);
+    pa_assert(c);
+    pa_assert(l > 0);
+
+#ifndef OS_IS_WIN32
+    pa_assert_se(fstat(fd, &st) == 0);
+#endif
+
+#ifndef OS_IS_WIN32
+    if (S_ISSOCK(st.st_mode)) {
+#endif
+        union {
+            struct sockaddr sa;
+            struct sockaddr_in in;
+            struct sockaddr_in6 in6;
+#ifdef HAVE_SYS_UN_H
+            struct sockaddr_un un;
+#endif
+        } sa;
+        socklen_t sa_len = sizeof(sa);
+
+        if (getpeername(fd, &sa.sa, &sa_len) >= 0) {
+
+            if (sa.sa.sa_family == AF_INET) {
+                uint32_t ip = ntohl(sa.in.sin_addr.s_addr);
+
+                pa_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_INET6) {
+                char buf[INET6_ADDRSTRLEN];
+                const char *res;
+
+                res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf));
+                if (res) {
+                    pa_snprintf(c, l, "TCP/IP client from [%s]:%u", buf, ntohs(sa.in6.sin6_port));
+                    return;
+                }
+#ifdef HAVE_SYS_UN_H
+            } else if (sa.sa.sa_family == AF_UNIX) {
+                pa_snprintf(c, l, "UNIX socket client");
+                return;
+#endif
+            }
+        }
+
+#ifndef OS_IS_WIN32
+        pa_snprintf(c, l, "Unknown network client");
+        return;
+    } else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) {
+        pa_snprintf(c, l, "STDIN/STDOUT client");
+        return;
+    }
+#endif /* OS_IS_WIN32 */
+
+    pa_snprintf(c, l, "Unknown client");
+}
+
+void pa_make_socket_low_delay(int fd) {
+
+#ifdef SO_PRIORITY
+    int priority;
+    pa_assert(fd >= 0);
+
+    priority = 6;
+    if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void*)&priority, sizeof(priority)) < 0)
+        pa_log_warn("SO_PRIORITY failed: %s", pa_cstrerror(errno));
+#endif
+}
+
+void pa_make_tcp_socket_low_delay(int fd) {
+    pa_assert(fd >= 0);
+
+    pa_make_socket_low_delay(fd);
+
+#if defined(SOL_TCP) || defined(IPPROTO_TCP)
+    {
+        int on = 1;
+#if defined(SOL_TCP)
+        if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
+#else
+        if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0)
+#endif
+            pa_log_warn("TCP_NODELAY failed: %s", pa_cstrerror(errno));
+    }
+#endif
+
+#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
+    {
+        int tos = IPTOS_LOWDELAY;
+#ifdef SOL_IP
+        if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+#else
+        if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+#endif
+            pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
+    }
+#endif
+}
+
+void pa_make_udp_socket_low_delay(int fd) {
+    pa_assert(fd >= 0);
+
+    pa_make_socket_low_delay(fd);
+
+#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || defined(IPPROTO_IP))
+    {
+        int tos = IPTOS_LOWDELAY;
+#ifdef SOL_IP
+        if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+#else
+        if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0)
+#endif
+            pa_log_warn("IP_TOS failed: %s", pa_cstrerror(errno));
+    }
+#endif
+}
+
+int pa_socket_set_rcvbuf(int fd, size_t l) {
+    pa_assert(fd >= 0);
+
+    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) {
+        pa_log_warn("SO_RCVBUF: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int pa_socket_set_sndbuf(int fd, size_t l) {
+    pa_assert(fd >= 0);
+
+    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) {
+        pa_log("SO_SNDBUF: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+#ifdef HAVE_SYS_UN_H
+
+int pa_unix_socket_is_stale(const char *fn) {
+    struct sockaddr_un sa;
+    int fd = -1, ret = -1;
+
+    pa_assert(fn);
+
+    if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+        pa_log("socket(): %s", pa_cstrerror(errno));
+        goto finish;
+    }
+
+    sa.sun_family = AF_UNIX;
+    strncpy(sa.sun_path, fn, sizeof(sa.sun_path)-1);
+    sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
+
+    if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
+        if (errno == ECONNREFUSED)
+            ret = 1;
+    } else
+        ret = 0;
+
+finish:
+    if (fd >= 0)
+        pa_close(fd);
+
+    return ret;
+}
+
+int pa_unix_socket_remove_stale(const char *fn) {
+    int r;
+
+    pa_assert(fn);
+
+    if ((r = pa_unix_socket_is_stale(fn)) < 0)
+        return errno != ENOENT ? -1 : 0;
+
+    if (!r)
+        return 0;
+
+    /* Yes, here is a race condition. But who cares? */
+    if (unlink(fn) < 0)
+        return -1;
+
+    return 0;
+}
+
+#else /* HAVE_SYS_UN_H */
+
+int pa_unix_socket_is_stale(const char *fn) {
+    return -1;
+}
+
+int pa_unix_socket_remove_stale(const char *fn) {
+    return -1;
+}
+
+#endif /* HAVE_SYS_UN_H */
+
+
+pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa) {
+    pa_assert(sa);
+
+    switch (sa->sa_family) {
+        case AF_UNIX:
+            return TRUE;
+
+        case AF_INET:
+            return ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
+
+        case AF_INET6:
+            return memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
+
+        default:
+            return FALSE;
+    }
+}
+
+pa_bool_t pa_socket_is_local(int fd) {
+
+    union {
+        struct sockaddr sa;
+        struct sockaddr_in in;
+        struct sockaddr_in6 in6;
+#ifdef HAVE_SYS_UN_H
+        struct sockaddr_un un;
+#endif
+    } sa;
+    socklen_t sa_len = sizeof(sa);
+
+    if (getpeername(fd, &sa.sa, &sa_len) < 0)
+        return FALSE;
+
+    return pa_socket_address_is_local(&sa.sa);
+}
similarity index 58%
rename from polyp/socket-util.h
rename to src/pulsecore/socket-util.h
index ae16fb164c505d94f5988850e5ecb253422b8383..7a40285a142376790ab4e20b4e914a8516f5ee10 100644 (file)
@@ -1,33 +1,38 @@
 #ifndef foosocketutilhfoo
 #define foosocketutilhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include <sys/types.h>
+#include <sys/socket.h>
+
+#include <pulsecore/macro.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);
+void pa_make_socket_low_delay(int fd);
+void pa_make_tcp_socket_low_delay(int fd);
+void pa_make_udp_socket_low_delay(int fd);
 
 int pa_socket_set_sndbuf(int fd, size_t l);
 int pa_socket_set_rcvbuf(int fd, size_t l);
@@ -35,4 +40,7 @@ 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);
 
+pa_bool_t pa_socket_address_is_local(const struct sockaddr *sa);
+pa_bool_t pa_socket_is_local(int fd);
+
 #endif
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
new file mode 100644 (file)
index 0000000..8eedf83
--- /dev/null
@@ -0,0 +1,359 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sndfile.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
+
+#include "sound-file-stream.h"
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+
+typedef struct file_stream {
+    pa_msgobject parent;
+    pa_core *core;
+    pa_sink_input *sink_input;
+
+    SNDFILE *sndfile;
+    sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+
+    /* We need this memblockq here to easily fulfill rewind requests
+     * (even beyond the file start!) */
+    pa_memblockq *memblockq;
+} file_stream;
+
+enum {
+    FILE_STREAM_MESSAGE_UNLINK
+};
+
+PA_DECLARE_CLASS(file_stream);
+#define FILE_STREAM(o) (file_stream_cast(o))
+static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
+
+/* Called from main context */
+static void file_stream_unlink(file_stream *u) {
+    pa_assert(u);
+
+    if (!u->sink_input)
+        return;
+
+    pa_sink_input_unlink(u->sink_input);
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    /* Make sure we don't decrease the ref count twice. */
+    file_stream_unref(u);
+}
+
+/* Called from main context */
+static void file_stream_free(pa_object *o) {
+    file_stream *u = FILE_STREAM(o);
+    pa_assert(u);
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    if (u->sndfile)
+        sf_close(u->sndfile);
+
+    pa_xfree(u);
+}
+
+/* Called from main context */
+static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    file_stream *u = FILE_STREAM(o);
+    file_stream_assert_ref(u);
+
+    switch (code) {
+        case FILE_STREAM_MESSAGE_UNLINK:
+            file_stream_unlink(u);
+            break;
+    }
+
+    return 0;
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    file_stream_unlink(u);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT)
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
+/* Called from IO thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return -1;
+
+    for (;;) {
+        pa_memchunk tchunk;
+        size_t fs;
+        void *p;
+        sf_count_t n;
+
+        if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
+            chunk->length = PA_MIN(chunk->length, length);
+            pa_memblockq_drop(u->memblockq, chunk->length);
+            return 0;
+        }
+
+        if (!u->sndfile)
+            break;
+
+        tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
+        tchunk.index = 0;
+
+        p = pa_memblock_acquire(tchunk.memblock);
+
+        if (u->readf_function) {
+            fs = pa_frame_size(&i->sample_spec);
+            n = u->readf_function(u->sndfile, p, length/fs);
+        } else {
+            fs = 1;
+            n = sf_read_raw(u->sndfile, p, length);
+        }
+
+        pa_memblock_release(tchunk.memblock);
+
+        if (n <= 0) {
+            pa_memblock_unref(tchunk.memblock);
+
+            sf_close(u->sndfile);
+            u->sndfile = NULL;
+            break;
+        }
+
+        tchunk.length = n * fs;
+
+        pa_memblockq_push(u->memblockq, &tchunk);
+        pa_memblock_unref(tchunk.memblock);
+    }
+
+    if (pa_sink_input_safe_to_remove(i)) {
+        pa_memblockq_free(u->memblockq);
+        u->memblockq = NULL;
+
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
+    }
+
+    return -1;
+ }
+
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(nbytes > 0);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    pa_log("backwards %lu", (unsigned long) nbytes);
+
+    if (!u->memblockq)
+        return;
+
+    pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    if (!u->memblockq)
+        return;
+
+    pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+}
+
+int pa_play_file(
+        pa_sink *sink,
+        const char *fname,
+        const pa_cvolume *volume) {
+
+    file_stream *u = NULL;
+    SF_INFO sfinfo;
+    pa_sample_spec ss;
+    pa_sink_input_new_data data;
+    int fd;
+
+    pa_assert(sink);
+    pa_assert(fname);
+
+    u = pa_msgobject_new(file_stream);
+    u->parent.parent.free = file_stream_free;
+    u->parent.process_msg = file_stream_process_msg;
+    u->core = sink->core;
+    u->sink_input = NULL;
+    u->sndfile = NULL;
+    u->readf_function = NULL;
+    u->memblockq = NULL;
+
+    memset(&sfinfo, 0, sizeof(sfinfo));
+
+    if ((fd = open(fname, O_RDONLY
+#ifdef O_NOCTTY
+                   |O_NOCTTY
+#endif
+                   )) < 0) {
+        pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    /* FIXME: For now we just use posix_fadvise to avoid page faults
+     * when accessing the file data. Eventually we should move the
+     * file reader into the main event loop and pass the data over the
+     * asyncmsgq. */
+
+#ifdef HAVE_POSIX_FADVISE
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
+        pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
+        goto fail;
+    } else
+        pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
+
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) {
+        pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno));
+        goto fail;
+    } else
+        pa_log_debug("POSIX_FADV_WILLNEED succeeded.");
+#endif
+
+    if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
+        pa_log("Failed to open file %s", fname);
+        pa_close(fd);
+        goto fail;
+    }
+
+    switch (sfinfo.format & 0xFF) {
+        case SF_FORMAT_PCM_16:
+        case SF_FORMAT_PCM_U8:
+        case SF_FORMAT_PCM_S8:
+            ss.format = PA_SAMPLE_S16NE;
+            u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
+            break;
+
+        case SF_FORMAT_ULAW:
+            ss.format = PA_SAMPLE_ULAW;
+            break;
+
+        case SF_FORMAT_ALAW:
+            ss.format = PA_SAMPLE_ALAW;
+            break;
+
+        case SF_FORMAT_FLOAT:
+        default:
+            ss.format = PA_SAMPLE_FLOAT32NE;
+            u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
+            break;
+    }
+
+    ss.rate = sfinfo.samplerate;
+    ss.channels = sfinfo.channels;
+
+    if (!pa_sample_spec_valid(&ss)) {
+        pa_log("Unsupported sample format in file %s", fname);
+        goto fail;
+    }
+
+    pa_sink_input_new_data_init(&data);
+    data.sink = sink;
+    data.driver = __FILE__;
+    pa_sink_input_new_data_set_sample_spec(&data, &ss);
+    pa_sink_input_new_data_set_volume(&data, volume);
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
+    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
+
+    u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+    pa_sink_input_new_data_done(&data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->userdata = u;
+
+    u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
+
+    pa_sink_input_put(u->sink_input);
+
+    /* The reference to u is dangling here, because we want to keep
+     * this stream around until it is fully played. */
+
+    return 0;
+
+fail:
+    if (u)
+        file_stream_unref(u);
+
+    return -1;
+}
similarity index 61%
rename from polyp/sound-file-stream.h
rename to src/pulsecore/sound-file-stream.h
index f5ab8e2c46de7984c038ce40145937dff2932efa..4cc69146f4ef6b1070b7936857059ece5b25c556 100644 (file)
@@ -1,29 +1,29 @@
 #ifndef foosoundfilestreamhfoo
 #define foosoundfilestreamhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-#include "sink.h"
+#include <pulsecore/sink.h>
 
-int pa_play_file(struct pa_sink *sink, const char *fname, pa_volume_t volume);
+int pa_play_file(pa_sink *sink, const char *fname, const pa_cvolume *volume);
 
 #endif
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
new file mode 100644 (file)
index 0000000..3183ede
--- /dev/null
@@ -0,0 +1,206 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sndfile.h>
+
+#include <pulse/sample.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+
+#include "sound-file.h"
+#include "core-scache.h"
+
+int pa_sound_file_load(
+        pa_mempool *pool,
+        const char *fname,
+        pa_sample_spec *ss,
+        pa_channel_map *map,
+        pa_memchunk *chunk) {
+
+    SNDFILE *sf = NULL;
+    SF_INFO sfinfo;
+    int ret = -1;
+    size_t l;
+    sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL;
+    void *ptr = NULL;
+    int fd;
+
+    pa_assert(fname);
+    pa_assert(ss);
+    pa_assert(chunk);
+
+    pa_memchunk_reset(chunk);
+    memset(&sfinfo, 0, sizeof(sfinfo));
+
+    if ((fd = open(fname, O_RDONLY
+#ifdef O_NOCTTY
+                   |O_NOCTTY
+#endif
+                   )) < 0) {
+        pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
+        goto finish;
+    }
+
+#ifdef HAVE_POSIX_FADVISE
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
+        pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
+        goto finish;
+    } else
+        pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
+#endif
+
+    if (!(sf = sf_open_fd(fd, SFM_READ, &sfinfo, 1))) {
+        pa_log("Failed to open file %s", fname);
+        pa_close(fd);
+        goto finish;
+    }
+
+    switch (sfinfo.format & SF_FORMAT_SUBMASK) {
+        case SF_FORMAT_PCM_16:
+        case SF_FORMAT_PCM_U8:
+        case SF_FORMAT_PCM_S8:
+            ss->format = PA_SAMPLE_S16NE;
+            readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
+            break;
+
+        case SF_FORMAT_ULAW:
+            ss->format = PA_SAMPLE_ULAW;
+            break;
+
+        case SF_FORMAT_ALAW:
+            ss->format = PA_SAMPLE_ALAW;
+            break;
+
+        case SF_FORMAT_FLOAT:
+        case SF_FORMAT_DOUBLE:
+        default:
+            ss->format = PA_SAMPLE_FLOAT32NE;
+            readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
+            break;
+    }
+
+    ss->rate = sfinfo.samplerate;
+    ss->channels = sfinfo.channels;
+
+    if (!pa_sample_spec_valid(ss)) {
+        pa_log("Unsupported sample format in file %s", fname);
+        goto finish;
+    }
+
+    if (map)
+        pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
+
+    if ((l = pa_frame_size(ss) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
+        pa_log("File too large");
+        goto finish;
+    }
+
+    chunk->memblock = pa_memblock_new(pool, l);
+    chunk->index = 0;
+    chunk->length = l;
+
+    ptr = pa_memblock_acquire(chunk->memblock);
+
+    if ((readf_function && readf_function(sf, ptr, sfinfo.frames) != sfinfo.frames) ||
+        (!readf_function && sf_read_raw(sf, ptr, l) != (sf_count_t) l)) {
+        pa_log("Premature file end");
+        goto finish;
+    }
+
+    ret = 0;
+
+finish:
+
+    if (sf)
+        sf_close(sf);
+
+    if (ptr)
+        pa_memblock_release(chunk->memblock);
+
+    if (ret != 0 && chunk->memblock)
+        pa_memblock_unref(chunk->memblock);
+
+    return ret;
+}
+
+int pa_sound_file_too_big_to_cache(const char *fname) {
+
+    SNDFILE*sf = NULL;
+    SF_INFO sfinfo;
+    pa_sample_spec ss;
+
+    pa_assert(fname);
+
+    if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) {
+        pa_log("Failed to open file %s", fname);
+        return -1;
+    }
+
+    sf_close(sf);
+
+    switch (sfinfo.format & SF_FORMAT_SUBMASK) {
+        case SF_FORMAT_PCM_16:
+        case SF_FORMAT_PCM_U8:
+        case SF_FORMAT_PCM_S8:
+            ss.format = PA_SAMPLE_S16NE;
+            break;
+
+        case SF_FORMAT_ULAW:
+            ss.format = PA_SAMPLE_ULAW;
+            break;
+
+        case SF_FORMAT_ALAW:
+            ss.format = PA_SAMPLE_ALAW;
+            break;
+
+        case SF_FORMAT_DOUBLE:
+        case SF_FORMAT_FLOAT:
+        default:
+            ss.format = PA_SAMPLE_FLOAT32NE;
+            break;
+    }
+
+    ss.rate = sfinfo.samplerate;
+    ss.channels = sfinfo.channels;
+
+    if (!pa_sample_spec_valid(&ss)) {
+        pa_log("Unsupported sample format in file %s", fname);
+        return -1;
+    }
+
+    if ((pa_frame_size(&ss) * sfinfo.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
+        pa_log("File too large: %s", fname);
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/sound-file.h b/src/pulsecore/sound-file.h
new file mode 100644 (file)
index 0000000..e4d703d
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef soundfilehfoo
+#define soundfilehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/memchunk.h>
+
+int pa_sound_file_load(pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk);
+
+int pa_sound_file_too_big_to_cache(const char *fname);
+
+#endif
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
new file mode 100644 (file)
index 0000000..3d1abe3
--- /dev/null
@@ -0,0 +1,764 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
+
+#include "source-output.h"
+
+#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
+
+static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject);
+
+static void source_output_free(pa_object* mo);
+
+pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) {
+    pa_assert(data);
+
+    memset(data, 0, sizeof(*data));
+    data->resample_method = PA_RESAMPLER_INVALID;
+    data->proplist = pa_proplist_new();
+
+    return data;
+}
+
+void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
+    pa_assert(data);
+
+    if ((data->sample_spec_is_set = !!spec))
+        data->sample_spec = *spec;
+}
+
+void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) {
+    pa_assert(data);
+
+    if ((data->channel_map_is_set = !!map))
+        data->channel_map = *map;
+}
+
+void pa_source_output_new_data_done(pa_source_output_new_data *data) {
+    pa_assert(data);
+
+    pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_source_output *o) {
+    pa_assert(o);
+
+    o->push = NULL;
+    o->process_rewind = NULL;
+    o->update_max_rewind = NULL;
+    o->update_source_requested_latency = NULL;
+    o->update_source_latency_range = NULL;
+    o->attach = NULL;
+    o->detach = NULL;
+    o->suspend = NULL;
+    o->moved = NULL;
+    o->kill = NULL;
+    o->get_latency = NULL;
+    o->state_change = NULL;
+}
+
+/* Called from main context */
+pa_source_output* pa_source_output_new(
+        pa_core *core,
+        pa_source_output_new_data *data,
+        pa_source_output_flags_t flags) {
+
+    pa_source_output *o;
+    pa_resampler *resampler = NULL;
+    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+    pa_assert(core);
+    pa_assert(data);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data) < 0)
+        return NULL;
+
+    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+
+    if (!data->source)
+        data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, TRUE);
+
+    pa_return_null_if_fail(data->source);
+    pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED);
+
+    pa_return_null_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of);
+
+    if (!data->sample_spec_is_set)
+        data->sample_spec = data->source->sample_spec;
+
+    pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
+
+    if (!data->channel_map_is_set) {
+        if (data->source->channel_map.channels == data->sample_spec.channels)
+            data->channel_map = data->source->channel_map;
+        else
+            pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+    }
+
+    pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+    pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+    if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
+        data->sample_spec.format = data->source->sample_spec.format;
+
+    if (flags & PA_SOURCE_OUTPUT_FIX_RATE)
+        data->sample_spec.rate = data->source->sample_spec.rate;
+
+    if (flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
+        data->sample_spec.channels = data->source->sample_spec.channels;
+        data->channel_map = data->source->channel_map;
+    }
+
+    pa_assert(pa_sample_spec_valid(&data->sample_spec));
+    pa_assert(pa_channel_map_valid(&data->channel_map));
+
+    if (data->resample_method == PA_RESAMPLER_INVALID)
+        data->resample_method = core->resample_method;
+
+    pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data) < 0)
+        return NULL;
+
+    if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
+        pa_log("Failed to create source output: too many outputs per source.");
+        return NULL;
+    }
+
+    if ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
+        !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) ||
+        !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) {
+
+        if (!(resampler = pa_resampler_new(
+                      core->mempool,
+                      &data->source->sample_spec, &data->source->channel_map,
+                      &data->sample_spec, &data->channel_map,
+                      data->resample_method,
+                      ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                      ((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                      (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
+            pa_log_warn("Unsupported resampling operation.");
+            return NULL;
+        }
+
+        data->resample_method = pa_resampler_get_method(resampler);
+    }
+
+    o = pa_msgobject_new(pa_source_output);
+    o->parent.parent.free = source_output_free;
+    o->parent.process_msg = pa_source_output_process_msg;
+
+    o->core = core;
+    o->state = PA_SOURCE_OUTPUT_INIT;
+    o->flags = flags;
+    o->proplist = pa_proplist_copy(data->proplist);
+    o->driver = pa_xstrdup(data->driver);
+    o->module = data->module;
+    o->source = data->source;
+    o->client = data->client;
+
+    o->resample_method = data->resample_method;
+    o->sample_spec = data->sample_spec;
+    o->channel_map = data->channel_map;
+
+    o->direct_on_input = data->direct_on_input;
+
+    reset_callbacks(o);
+    o->userdata = NULL;
+
+    o->thread_info.state = o->state;
+    o->thread_info.attached = FALSE;
+    o->thread_info.sample_spec = o->sample_spec;
+    o->thread_info.resampler = resampler;
+    o->thread_info.requested_source_latency = (pa_usec_t) -1;
+    o->thread_info.direct_on_input = o->direct_on_input;
+
+    o->thread_info.delay_memblockq = pa_memblockq_new(
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            0,
+            pa_frame_size(&o->source->sample_spec),
+            0,
+            1,
+            0,
+            &o->source->silence);
+
+    pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
+    pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
+
+    if (o->direct_on_input)
+        pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0);
+
+    pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s",
+                o->index,
+                pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)),
+                o->source->name,
+                pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
+                pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map));
+
+    /* Don't forget to call pa_source_output_put! */
+
+    return o;
+}
+
+/* Called from main context */
+static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) {
+    pa_assert(o);
+
+    if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED)
+        pa_assert_se(o->source->n_corked -- >= 1);
+    else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED)
+        o->source->n_corked++;
+
+    pa_source_update_status(o->source);
+}
+
+/* Called from main context */
+static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
+    pa_assert(o);
+
+    if (o->state == state)
+        return 0;
+
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+    update_n_corked(o, state);
+    o->state = state;
+
+    if (state != PA_SOURCE_OUTPUT_UNLINKED)
+        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
+
+    return 0;
+}
+
+/* Called from main context */
+void pa_source_output_unlink(pa_source_output*o) {
+    pa_bool_t linked;
+    pa_assert(o);
+
+    /* See pa_sink_unlink() for a couple of comments how this function
+     * works */
+
+    pa_source_output_ref(o);
+
+    linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
+
+    if (linked)
+        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
+
+    if (o->direct_on_input)
+        pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
+    pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL);
+    if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
+        pa_source_output_unref(o);
+
+    update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
+    o->state = PA_SOURCE_OUTPUT_UNLINKED;
+
+    if (linked)
+        if (o->source->asyncmsgq)
+            pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+
+    reset_callbacks(o);
+
+    if (linked) {
+        pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
+        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
+    }
+
+    o->source = NULL;
+    pa_source_output_unref(o);
+}
+
+/* Called from main context */
+static void source_output_free(pa_object* mo) {
+    pa_source_output *o = PA_SOURCE_OUTPUT(mo);
+
+    pa_assert(o);
+    pa_assert(pa_source_output_refcnt(o) == 0);
+
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+        pa_source_output_unlink(o);
+
+    pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
+
+    pa_assert(!o->thread_info.attached);
+
+    if (o->thread_info.delay_memblockq)
+        pa_memblockq_free(o->thread_info.delay_memblockq);
+
+    if (o->thread_info.resampler)
+        pa_resampler_free(o->thread_info.resampler);
+
+    if (o->proplist)
+        pa_proplist_free(o->proplist);
+
+    pa_xfree(o->driver);
+    pa_xfree(o);
+}
+
+/* Called from main context */
+void pa_source_output_put(pa_source_output *o) {
+    pa_source_output_state_t state;
+    pa_source_output_assert_ref(o);
+
+    pa_assert(o->state == PA_SOURCE_OUTPUT_INIT);
+
+    /* The following fields must be initialized properly */
+    pa_assert(o->push);
+    pa_assert(o->kill);
+
+    state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
+
+    update_n_corked(o, state);
+    o->state = state;
+
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
+
+    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
+    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
+}
+
+/* Called from main context */
+void pa_source_output_kill(pa_source_output*o) {
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    o->kill(o);
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency) {
+    pa_usec_t r[2] = { 0, 0 };
+
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
+
+    if (o->get_latency)
+        r[0] += o->get_latency(o);
+
+    if (source_latency)
+        *source_latency = r[1];
+
+    return r[0];
+}
+
+/* Called from thread context */
+void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
+    size_t length;
+    size_t limit, mbs = 0;
+
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+    pa_assert(chunk);
+    pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
+
+    if (!o->push || o->thread_info.state == PA_SOURCE_OUTPUT_CORKED)
+        return;
+
+    pa_assert(o->thread_info.state == PA_SOURCE_OUTPUT_RUNNING);
+
+    if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {
+        pa_log_debug("Delay queue overflow!");
+        pa_memblockq_seek(o->thread_info.delay_memblockq, chunk->length, PA_SEEK_RELATIVE);
+    }
+
+    limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
+
+    /* Implement the delay queue */
+    while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
+        pa_memchunk qchunk;
+
+        length -= limit;
+
+        pa_assert_se(pa_memblockq_peek(o->thread_info.delay_memblockq, &qchunk) >= 0);
+
+        if (qchunk.length > length)
+            qchunk.length = length;
+
+        pa_assert(qchunk.length > 0);
+
+        if (!o->thread_info.resampler)
+            o->push(o, &qchunk);
+        else {
+            pa_memchunk rchunk;
+
+            if (mbs == 0)
+                mbs = pa_resampler_max_block_size(o->thread_info.resampler);
+
+            if (qchunk.length > mbs)
+                qchunk.length = mbs;
+
+            pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk);
+
+            if (rchunk.length > 0)
+                o->push(o, &rchunk);
+
+            if (rchunk.memblock)
+                pa_memblock_unref(rchunk.memblock);
+        }
+
+        pa_memblock_unref(qchunk.memblock);
+        pa_memblockq_drop(o->thread_info.delay_memblockq, qchunk.length);
+    }
+}
+
+/* Called from thread context */
+void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) {
+    pa_source_output_assert_ref(o);
+
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
+
+    if (nbytes <= 0)
+        return;
+
+    if (o->process_rewind) {
+        pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0);
+
+        if (o->thread_info.resampler)
+            nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
+
+        pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes);
+
+        if (nbytes > 0)
+            o->process_rewind(o, nbytes);
+
+        if (o->thread_info.resampler)
+            pa_resampler_reset(o->thread_info.resampler);
+
+    } else
+        pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes);
+}
+
+/* Called from thread context */
+void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes  /* in the source's sample spec */) {
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
+    pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
+
+    if (o->update_max_rewind)
+        o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
+}
+
+/* Called from thread context */
+static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
+    pa_source_assert_ref(s);
+
+    if (usec == (pa_usec_t) -1)
+        return usec;
+
+    if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
+        usec = s->thread_info.max_latency;
+
+    if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
+        usec = s->thread_info.min_latency;
+
+    return usec;
+}
+
+/* Called from thread context */
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
+    pa_source_output_assert_ref(o);
+
+    usec = fixup_latency(o->source, usec);
+    o->thread_info.requested_source_latency = usec;
+    pa_source_invalidate_requested_latency(o->source);
+
+    return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
+    pa_source_output_assert_ref(o);
+
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+        pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+    else {
+        /* If this sink input is not realized yet, we have to touch
+         * the thread info data directly */
+
+        usec = fixup_latency(o->source, usec);
+        o->thread_info.requested_source_latency = usec;
+        o->source->thread_info.requested_latency_valid = FALSE;
+    }
+
+    return usec;
+}
+
+/* Called from main context */
+pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
+    pa_usec_t usec = 0;
+
+    pa_source_output_assert_ref(o);
+
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+        pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+    else
+        /* If this sink input is not realized yet, we have to touch
+         * the thread info data directly */
+        usec = o->thread_info.requested_source_latency;
+
+    return usec;
+}
+
+/* Called from main context */
+void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
+}
+
+/* Called from main context */
+int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_return_val_if_fail(o->thread_info.resampler, -1);
+
+    if (o->sample_spec.rate == rate)
+        return 0;
+
+    o->sample_spec.rate = rate;
+
+    pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
+
+    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    return 0;
+}
+
+/* Called from main context */
+void pa_source_output_set_name(pa_source_output *o, const char *name) {
+    const char *old;
+    pa_source_output_assert_ref(o);
+
+    if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME))
+        return;
+
+    old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME);
+
+    if (old && name && !strcmp(old, name))
+        return;
+
+    if (name)
+        pa_proplist_sets(o->proplist, PA_PROP_MEDIA_NAME, name);
+    else
+        pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
+
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
+        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+        pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    }
+}
+
+/* Called from main context */
+pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+
+    return o->resample_method;
+}
+
+/* Called from main context */
+int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
+    pa_source *origin;
+    pa_resampler *new_resampler;
+    pa_source_output_move_hook_data hook_data;
+
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_source_assert_ref(dest);
+
+    origin = o->source;
+
+    if (dest == origin)
+        return 0;
+
+    if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
+        return -1;
+
+    if (o->direct_on_input)
+        return -1;
+
+    if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
+        pa_log_warn("Failed to move source output: too many outputs per source.");
+        return -1;
+    }
+
+    if (o->thread_info.resampler &&
+        pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
+        pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
+
+        /* Try to reuse the old resampler if possible */
+        new_resampler = o->thread_info.resampler;
+
+    else if ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
+             !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) ||
+             !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) {
+
+        /* Okey, we need a new resampler for the new source */
+
+        if (!(new_resampler = pa_resampler_new(
+                      dest->core->mempool,
+                      &dest->sample_spec, &dest->channel_map,
+                      &o->sample_spec, &o->channel_map,
+                      o->resample_method,
+                      ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                      ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                      (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
+            pa_log_warn("Unsupported resampling operation.");
+            return -1;
+        }
+    } else
+        new_resampler = NULL;
+
+    hook_data.source_output = o;
+    hook_data.destination = dest;
+    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], &hook_data);
+
+    /* Okey, let's move it */
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+
+    pa_idxset_remove_by_data(origin->outputs, o, NULL);
+    pa_idxset_put(dest->outputs, o, NULL);
+    o->source = dest;
+
+    if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) {
+        pa_assert_se(origin->n_corked-- >= 1);
+        dest->n_corked++;
+    }
+
+    /* Replace resampler */
+    if (new_resampler != o->thread_info.resampler) {
+        if (o->thread_info.resampler)
+            pa_resampler_free(o->thread_info.resampler);
+        o->thread_info.resampler = new_resampler;
+
+        pa_memblockq_free(o->thread_info.delay_memblockq);
+
+        o->thread_info.delay_memblockq = pa_memblockq_new(
+                0,
+                MEMBLOCKQ_MAXLENGTH,
+                0,
+                pa_frame_size(&o->source->sample_spec),
+                0,
+                1,
+                0,
+                &o->source->silence);
+    }
+
+    pa_source_update_status(origin);
+    pa_source_update_status(dest);
+
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
+
+    if (o->moved)
+        o->moved(o);
+
+    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], o);
+
+    pa_log_debug("Successfully moved source output %i from %s to %s.", o->index, origin->name, dest->name);
+
+    /* Notify everyone */
+    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+
+    return 0;
+}
+
+/* Called from IO thread context */
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
+    pa_source_output_assert_ref(o);
+
+    if (state == o->thread_info.state)
+        return;
+
+    if (o->state_change)
+        o->state_change(o, state);
+
+    o->thread_info.state = state;
+}
+
+/* Called from IO thread context, except when it is not */
+int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) {
+    pa_source_output *o = PA_SOURCE_OUTPUT(mo);
+    pa_source_output_assert_ref(o);
+
+    switch (code) {
+
+        case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: {
+            pa_usec_t *r = userdata;
+            pa_usec_t source_usec = 0;
+
+            r[0] += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec);
+
+            if (o->source->parent.process_msg(PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_GET_LATENCY, &source_usec, 0, NULL) >= 0)
+                r[1] += source_usec;
+
+            return 0;
+        }
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE:
+
+            o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
+            pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
+            return 0;
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
+
+            pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
+            return 0;
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: {
+            pa_usec_t *usec = userdata;
+
+            *usec = pa_source_output_set_requested_latency_within_thread(o, *usec);
+
+            return 0;
+        }
+
+        case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: {
+            pa_usec_t *r = userdata;
+
+            *r = o->thread_info.requested_source_latency;
+            return 0;
+        }
+    }
+
+    return -1;
+}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
new file mode 100644 (file)
index 0000000..61825b2
--- /dev/null
@@ -0,0 +1,240 @@
+#ifndef foopulsesourceoutputhfoo
+#define foopulsesourceoutputhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+typedef struct pa_source_output pa_source_output;
+
+#include <pulse/sample.h>
+#include <pulsecore/source.h>
+#include <pulsecore/memblockq.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/module.h>
+#include <pulsecore/client.h>
+
+typedef enum pa_source_output_state {
+    PA_SOURCE_OUTPUT_INIT,
+    PA_SOURCE_OUTPUT_RUNNING,
+    PA_SOURCE_OUTPUT_CORKED,
+    PA_SOURCE_OUTPUT_UNLINKED
+} pa_source_output_state_t;
+
+static inline pa_bool_t PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_state_t x) {
+    return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED;
+}
+
+typedef enum pa_source_output_flags {
+    PA_SOURCE_OUTPUT_VARIABLE_RATE = 1,
+    PA_SOURCE_OUTPUT_DONT_MOVE = 2,
+    PA_SOURCE_OUTPUT_START_CORKED = 4,
+    PA_SOURCE_OUTPUT_NO_REMAP = 8,
+    PA_SOURCE_OUTPUT_NO_REMIX = 16,
+    PA_SOURCE_OUTPUT_FIX_FORMAT = 32,
+    PA_SOURCE_OUTPUT_FIX_RATE = 64,
+    PA_SOURCE_OUTPUT_FIX_CHANNELS = 128
+} pa_source_output_flags_t;
+
+struct pa_source_output {
+    pa_msgobject parent;
+
+    uint32_t index;
+    pa_core *core;
+
+    pa_source_output_state_t state;
+    pa_source_output_flags_t flags;
+
+    char *driver;                         /* may be NULL */
+    pa_proplist *proplist;
+
+    pa_module *module;                    /* may be NULL */
+    pa_client *client;                    /* may be NULL */
+
+    pa_source *source;
+
+    /* A source output can monitor just a single input of a sink, in which case we find it here */
+    pa_sink_input *direct_on_input;       /* may be NULL */
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+
+    pa_resample_method_t resample_method;
+
+    /* Pushes a new memchunk into the output. Called from IO thread
+     * context. */
+    void (*push)(pa_source_output *o, const pa_memchunk *chunk); /* may NOT be NULL */
+
+    /* Only relevant for monitor sources right now: called when the
+     * recorded stream is rewound. Called from IO context */
+    void (*process_rewind)(pa_source_output *o, size_t nbytes); /* may be NULL */
+
+    /* Called whenever the maximum rewindable size of the source
+     * changes. Called from IO thread context. */
+    void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */
+
+    /* Called whenever the configured latency of the source
+     * changes. Called from IO context. */
+    void (*update_source_requested_latency) (pa_source_output *o); /* may be NULL */
+
+    /* Called whenver the latency range of the source changes. Called
+     * from IO context. */
+    void (*update_source_latency_range) (pa_source_output *o); /* may be NULL */
+
+    /* If non-NULL this function is called when the output is first
+     * connected to a source. Called from IO thread context */
+    void (*attach) (pa_source_output *o);           /* may be NULL */
+
+    /* If non-NULL this function is called when the output is
+     * disconnected from its source. Called from IO thread context */
+    void (*detach) (pa_source_output *o);           /* may be NULL */
+
+    /* If non-NULL called whenever the the source this output is attached
+     * to suspends or resumes. Called from main context */
+    void (*suspend) (pa_source_output *o, pa_bool_t b);   /* may be NULL */
+
+    /* If non-NULL called whenever the the source this output is attached
+     * to changes. Called from main context */
+    void (*moved) (pa_source_output *o);   /* may be NULL */
+
+    /* Supposed to unlink and destroy this stream. Called from main
+     * context. */
+    void (*kill)(pa_source_output* o);              /* may NOT be NULL */
+
+    /* Return the current latency (i.e. length of bufferd audio) of
+    this stream. Called from main context. This is added to what the
+    PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY message sent to the IO thread
+    returns */
+    pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
+
+    /* If non_NULL this function is called from thread context if the
+     * state changes. The old state is found in thread_info.state.  */
+    void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */
+
+    struct {
+        pa_source_output_state_t state;
+
+        pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
+
+        pa_sample_spec sample_spec;
+
+        pa_resampler* resampler;              /* may be NULL */
+
+        /* We maintain a delay memblockq here for source outputs that
+         * don't implement rewind() */
+        pa_memblockq *delay_memblockq;
+
+        /* The requested latency for the source */
+        pa_usec_t requested_source_latency;
+
+        pa_sink_input *direct_on_input;       /* may be NULL */
+    } thread_info;
+
+    void *userdata;
+};
+
+PA_DECLARE_CLASS(pa_source_output);
+#define PA_SOURCE_OUTPUT(o) pa_source_output_cast(o)
+
+enum {
+    PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY,
+    PA_SOURCE_OUTPUT_MESSAGE_SET_RATE,
+    PA_SOURCE_OUTPUT_MESSAGE_SET_STATE,
+    PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY,
+    PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY,
+    PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
+typedef struct pa_source_output_new_data {
+    pa_proplist *proplist;
+    pa_sink_input *direct_on_input;
+
+    const char *driver;
+    pa_module *module;
+    pa_client *client;
+
+    pa_source *source;
+
+    pa_sample_spec sample_spec;
+    pa_bool_t sample_spec_is_set;
+    pa_channel_map channel_map;
+    pa_bool_t channel_map_is_set;
+
+    pa_resample_method_t resample_method;
+} pa_source_output_new_data;
+
+pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
+void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
+void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
+void pa_source_output_new_data_done(pa_source_output_new_data *data);
+
+typedef struct pa_source_output_move_hook_data {
+    pa_source_output *source_output;
+    pa_source *destination;
+} pa_source_output_move_hook_data;
+
+/* To be called by the implementing module only */
+
+pa_source_output* pa_source_output_new(
+        pa_core *core,
+        pa_source_output_new_data *data,
+        pa_source_output_flags_t flags);
+
+void pa_source_output_put(pa_source_output *o);
+void pa_source_output_unlink(pa_source_output*o);
+
+void pa_source_output_set_name(pa_source_output *i, const char *name);
+
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec);
+
+void pa_source_output_cork(pa_source_output *i, pa_bool_t b);
+
+int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
+
+/* Callable by everyone */
+
+/* External code may request disconnection with this funcion */
+void pa_source_output_kill(pa_source_output*o);
+
+pa_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_latency);
+
+pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
+
+int pa_source_output_move_to(pa_source_output *o, pa_source *dest);
+
+#define pa_source_output_get_state(o) ((o)->state)
+
+pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o);
+
+/* To be used exclusively by the source driver thread */
+
+void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes);
+void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes);
+
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state);
+
+int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
+
+#endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
new file mode 100644 (file)
index 0000000..8256a98
--- /dev/null
@@ -0,0 +1,985 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <string.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/source-output.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/log.h>
+#include <pulsecore/sample-util.h>
+
+#include "source.h"
+
+#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+
+static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
+
+static void source_free(pa_object *o);
+
+pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) {
+    pa_assert(data);
+
+    memset(data, 0, sizeof(*data));
+    data->proplist = pa_proplist_new();
+
+    return data;
+}
+
+void pa_source_new_data_set_name(pa_source_new_data *data, const char *name) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    data->name = pa_xstrdup(name);
+}
+
+void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec) {
+    pa_assert(data);
+
+    if ((data->sample_spec_is_set = !!spec))
+        data->sample_spec = *spec;
+}
+
+void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map) {
+    pa_assert(data);
+
+    if ((data->channel_map_is_set = !!map))
+        data->channel_map = *map;
+}
+
+void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume) {
+    pa_assert(data);
+
+    if ((data->volume_is_set = !!volume))
+        data->volume = *volume;
+}
+
+void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) {
+    pa_assert(data);
+
+    data->muted_is_set = TRUE;
+    data->muted = !!mute;
+}
+
+void pa_source_new_data_done(pa_source_new_data *data) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    pa_proplist_free(data->proplist);
+}
+
+/* Called from main context */
+static void reset_callbacks(pa_source *s) {
+    pa_assert(s);
+
+    s->set_state = NULL;
+    s->get_volume = NULL;
+    s->set_volume = NULL;
+    s->get_mute = NULL;
+    s->set_mute = NULL;
+    s->update_requested_latency = NULL;
+}
+
+/* Called from main context */
+pa_source* pa_source_new(
+        pa_core *core,
+        pa_source_new_data *data,
+        pa_source_flags_t flags) {
+
+    pa_source *s;
+    const char *name;
+    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+    pa_assert(core);
+    pa_assert(data);
+    pa_assert(data->name);
+
+    s = pa_msgobject_new(pa_source);
+
+    if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) {
+        pa_xfree(s);
+        return NULL;
+    }
+
+    pa_source_new_data_set_name(data, name);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) {
+        pa_xfree(s);
+        pa_namereg_unregister(core, name);
+        return NULL;
+    }
+
+    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+    pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
+
+    pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
+
+    if (!data->channel_map_is_set)
+        pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+
+    pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
+    pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+
+    if (!data->volume_is_set)
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+
+    pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
+    pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+
+    if (!data->muted_is_set)
+        data->muted = FALSE;
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
+        pa_xfree(s);
+        pa_namereg_unregister(core, name);
+        return NULL;
+    }
+
+    s->parent.parent.free = source_free;
+    s->parent.process_msg = pa_source_process_msg;
+
+    s->core = core;
+    s->state = PA_SOURCE_INIT;
+    s->flags = flags;
+    s->name = pa_xstrdup(name);
+    s->proplist = pa_proplist_copy(data->proplist);
+    s->driver = pa_xstrdup(data->driver);
+    s->module = data->module;
+
+    s->sample_spec = data->sample_spec;
+    s->channel_map = data->channel_map;
+
+    s->outputs = pa_idxset_new(NULL, NULL);
+    s->n_corked = 0;
+    s->monitor_of = NULL;
+
+    s->volume = data->volume;
+    s->muted = data->muted;
+    s->refresh_volume = s->refresh_muted = FALSE;
+
+    reset_callbacks(s);
+    s->userdata = NULL;
+
+    s->asyncmsgq = NULL;
+    s->rtpoll = NULL;
+
+    pa_silence_memchunk_get(
+            &core->silence_cache,
+            core->mempool,
+            &s->silence,
+            &s->sample_spec,
+            0);
+
+    s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
+    s->thread_info.soft_muted = FALSE;
+    s->thread_info.state = s->state;
+    s->thread_info.max_rewind = 0;
+    s->thread_info.requested_latency_valid = FALSE;
+    s->thread_info.requested_latency = 0;
+    s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
+    s->thread_info.max_latency = 0;
+
+    pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
+
+    pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s",
+                s->index,
+                s->name,
+                pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
+                pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
+
+    return s;
+}
+
+/* Called from main context */
+static int source_set_state(pa_source *s, pa_source_state_t state) {
+    int ret;
+    pa_bool_t suspend_change;
+
+    pa_assert(s);
+
+    if (s->state == state)
+        return 0;
+
+    suspend_change =
+        (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
+        (PA_SOURCE_IS_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
+
+    if (s->set_state)
+        if ((ret = s->set_state(s, state)) < 0)
+            return -1;
+
+    if (s->asyncmsgq)
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
+
+    s->state = state;
+
+    if (suspend_change) {
+        pa_source_output *o;
+        uint32_t idx;
+
+        /* We're suspending or resuming, tell everyone about it */
+
+        for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
+            if (o->suspend)
+                o->suspend(o, state == PA_SINK_SUSPENDED);
+    }
+
+    if (state != PA_SOURCE_UNLINKED) /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
+
+    return 0;
+}
+
+/* Called from main context */
+void pa_source_put(pa_source *s) {
+    pa_source_assert_ref(s);
+
+    pa_assert(s->state == PA_SINK_INIT);
+
+    /* The following fields must be initialized properly when calling _put() */
+    pa_assert(s->asyncmsgq);
+    pa_assert(s->rtpoll);
+    pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
+              s->thread_info.min_latency <= s->thread_info.max_latency);
+
+    if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
+        s->flags |= PA_SOURCE_DECIBEL_VOLUME;
+
+        s->thread_info.soft_volume = s->volume;
+        s->thread_info.soft_muted = s->muted;
+    }
+
+    pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s);
+}
+
+/* Called from main context */
+void pa_source_unlink(pa_source *s) {
+    pa_bool_t linked;
+    pa_source_output *o, *j = NULL;
+
+    pa_assert(s);
+
+    /* See pa_sink_unlink() for a couple of comments how this function
+     * works. */
+
+    linked = PA_SOURCE_IS_LINKED(s->state);
+
+    if (linked)
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
+
+    if (s->state != PA_SOURCE_UNLINKED)
+        pa_namereg_unregister(s->core, s->name);
+    pa_idxset_remove_by_data(s->core->sources, s, NULL);
+
+    while ((o = pa_idxset_first(s->outputs, NULL))) {
+        pa_assert(o != j);
+        pa_source_output_kill(o);
+        j = o;
+    }
+
+    if (linked)
+        source_set_state(s, PA_SOURCE_UNLINKED);
+    else
+        s->state = PA_SOURCE_UNLINKED;
+
+    reset_callbacks(s);
+
+    if (linked) {
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], s);
+    }
+}
+
+/* Called from main context */
+static void source_free(pa_object *o) {
+    pa_source_output *so;
+    pa_source *s = PA_SOURCE(o);
+
+    pa_assert(s);
+    pa_assert(pa_source_refcnt(s) == 0);
+
+    if (PA_SOURCE_IS_LINKED(s->state))
+        pa_source_unlink(s);
+
+    pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
+
+    pa_idxset_free(s->outputs, NULL, NULL);
+
+    while ((so = pa_hashmap_steal_first(s->thread_info.outputs)))
+        pa_source_output_unref(so);
+
+    pa_hashmap_free(s->thread_info.outputs, NULL, NULL);
+
+    if (s->silence.memblock)
+        pa_memblock_unref(s->silence.memblock);
+
+    pa_xfree(s->name);
+    pa_xfree(s->driver);
+
+    if (s->proplist)
+        pa_proplist_free(s->proplist);
+
+    pa_xfree(s);
+}
+
+/* Called from main context */
+void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
+    pa_source_assert_ref(s);
+
+    s->asyncmsgq = q;
+}
+
+/* Called from main context */
+void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
+    pa_source_assert_ref(s);
+
+    s->rtpoll = p;
+}
+
+/* Called from main context */
+int pa_source_update_status(pa_source*s) {
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (s->state == PA_SOURCE_SUSPENDED)
+        return 0;
+
+    return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
+}
+
+/* Called from main context */
+int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (suspend)
+        return source_set_state(s, PA_SOURCE_SUSPENDED);
+    else
+        return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
+}
+
+/* Called from IO thread context */
+void pa_source_process_rewind(pa_source *s, size_t nbytes) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+
+    if (nbytes <= 0)
+        return;
+
+    pa_log_debug("Processing rewind...");
+
+    while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+        pa_source_output_assert_ref(o);
+        pa_source_output_process_rewind(o, nbytes);
+    }
+}
+
+/* Called from IO thread context */
+void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+    pa_assert(chunk);
+
+    if (s->thread_info.state != PA_SOURCE_RUNNING)
+        return;
+
+    if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
+        pa_memchunk vchunk = *chunk;
+
+        pa_memblock_ref(vchunk.memblock);
+        pa_memchunk_make_writable(&vchunk, 0);
+
+        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume))
+            pa_silence_memchunk(&vchunk, &s->sample_spec);
+        else
+            pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
+
+        while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+            pa_source_output_assert_ref(o);
+
+            if (!o->thread_info.direct_on_input)
+                pa_source_output_push(o, &vchunk);
+        }
+
+        pa_memblock_unref(vchunk.memblock);
+    } else {
+
+        while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
+            pa_source_output_assert_ref(o);
+
+            if (!o->thread_info.direct_on_input)
+                pa_source_output_push(o, chunk);
+        }
+    }
+}
+
+/* Called from IO thread context */
+void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) {
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+    pa_source_output_assert_ref(o);
+    pa_assert(o->thread_info.direct_on_input);
+    pa_assert(chunk);
+
+    if (s->thread_info.state != PA_SOURCE_RUNNING)
+        return;
+
+    if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
+        pa_memchunk vchunk = *chunk;
+
+        pa_memblock_ref(vchunk.memblock);
+        pa_memchunk_make_writable(&vchunk, 0);
+
+        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume))
+            pa_silence_memchunk(&vchunk, &s->sample_spec);
+        else
+            pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
+
+        pa_source_output_push(o, &vchunk);
+
+        pa_memblock_unref(vchunk.memblock);
+    } else
+        pa_source_output_push(o, chunk);
+}
+
+/* Called from main thread */
+pa_usec_t pa_source_get_latency(pa_source *s) {
+    pa_usec_t usec;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (!PA_SOURCE_IS_OPENED(s->state))
+        return 0;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
+
+    return usec;
+}
+
+/* Called from main thread */
+void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
+    pa_bool_t changed;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(volume);
+
+    changed = !pa_cvolume_equal(volume, &s->volume);
+    s->volume = *volume;
+
+    if (s->set_volume && s->set_volume(s) < 0)
+        s->set_volume = NULL;
+
+    if (!s->set_volume)
+        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL);
+
+    if (changed)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main thread */
+const pa_cvolume *pa_source_get_volume(pa_source *s) {
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (s->refresh_volume) {
+        pa_cvolume old_volume = s->volume;
+
+        if (s->get_volume && s->get_volume(s) < 0)
+            s->get_volume = NULL;
+
+        if (!s->get_volume)
+            pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
+
+        if (!pa_cvolume_equal(&old_volume, &s->volume))
+            pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
+    return &s->volume;
+}
+
+/* Called from main thread */
+void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
+    pa_bool_t changed;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    changed = s->muted != mute;
+    s->muted = mute;
+
+    if (s->set_mute && s->set_mute(s) < 0)
+        s->set_mute = NULL;
+
+    if (!s->set_mute)
+        pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+
+    if (changed)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main thread */
+pa_bool_t pa_source_get_mute(pa_source *s) {
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (s->refresh_muted) {
+        pa_bool_t old_muted = s->muted;
+
+        if (s->get_mute && s->get_mute(s) < 0)
+            s->get_mute = NULL;
+
+        if (!s->get_mute)
+            pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
+
+        if (old_muted != s->muted)
+            pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
+    return s->muted;
+}
+
+/* Called from main thread */
+void pa_source_set_description(pa_source *s, const char *description) {
+    const char *old;
+    pa_source_assert_ref(s);
+
+    if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
+        return;
+
+    old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+    if (old && description && !strcmp(old, description))
+        return;
+
+    if (description)
+        pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
+    else
+        pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
+
+    if (PA_SOURCE_IS_LINKED(s->state)) {
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
+    }
+}
+
+/* Called from main thread */
+unsigned pa_source_linked_by(pa_source *s) {
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    return pa_idxset_size(s->outputs);
+}
+
+/* Called from main thread */
+unsigned pa_source_used_by(pa_source *s) {
+    unsigned ret;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    ret = pa_idxset_size(s->outputs);
+    pa_assert(ret >= s->n_corked);
+
+    return ret - s->n_corked;
+}
+
+/* Called from IO thread, except when it is not */
+int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_source *s = PA_SOURCE(object);
+    pa_source_assert_ref(s);
+
+    switch ((pa_source_message_t) code) {
+
+        case PA_SOURCE_MESSAGE_ADD_OUTPUT: {
+            pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+
+            pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o));
+
+            if (o->direct_on_input) {
+                o->thread_info.direct_on_input = o->direct_on_input;
+                pa_hashmap_put(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index), o);
+            }
+
+            pa_assert(!o->thread_info.attached);
+            o->thread_info.attached = TRUE;
+
+            if (o->attach)
+                o->attach(o);
+
+            pa_source_output_set_state_within_thread(o, o->state);
+
+            pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
+
+            /* We don't just invalidate the requested latency here,
+             * because if we are in a move we might need to fix up the
+             * requested latency. */
+            pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
+            pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+
+            pa_source_output_set_state_within_thread(o, o->state);
+
+            if (o->detach)
+                o->detach(o);
+
+            pa_assert(o->thread_info.attached);
+            o->thread_info.attached = FALSE;
+
+            if (o->thread_info.direct_on_input) {
+                pa_hashmap_remove(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index));
+                o->thread_info.direct_on_input = NULL;
+            }
+
+            if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index)))
+                pa_source_output_unref(o);
+
+            pa_source_invalidate_requested_latency(s);
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SET_VOLUME:
+            s->thread_info.soft_volume = *((pa_cvolume*) userdata);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_SET_MUTE:
+            s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_GET_VOLUME:
+            *((pa_cvolume*) userdata) = s->thread_info.soft_volume;
+            return 0;
+
+        case PA_SOURCE_MESSAGE_GET_MUTE:
+            *((pa_bool_t*) userdata) = s->thread_info.soft_muted;
+            return 0;
+
+        case PA_SOURCE_MESSAGE_SET_STATE:
+            s->thread_info.state = PA_PTR_TO_UINT(userdata);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_DETACH:
+
+            /* Detach all streams */
+            pa_source_detach_within_thread(s);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_ATTACH:
+
+            /* Reattach all streams */
+            pa_source_attach_within_thread(s);
+            return 0;
+
+        case PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY: {
+
+            pa_usec_t *usec = userdata;
+            *usec = pa_source_get_requested_latency_within_thread(s);
+
+            if (*usec == (pa_usec_t) -1)
+                *usec = s->thread_info.max_latency;
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_SET_LATENCY_RANGE: {
+            pa_usec_t *r = userdata;
+
+            pa_source_update_latency_range(s, r[0], r[1]);
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY_RANGE: {
+            pa_usec_t *r = userdata;
+
+            r[0] = s->thread_info.min_latency;
+            r[1] = s->thread_info.max_latency;
+
+            return 0;
+        }
+
+        case PA_SOURCE_MESSAGE_GET_MAX_REWIND:
+
+            *((size_t*) userdata) = s->thread_info.max_rewind;
+            return 0;
+
+        case PA_SOURCE_MESSAGE_GET_LATENCY:
+
+            if (s->monitor_of) {
+                *((pa_usec_t*) userdata) = 0;
+                return 0;
+            }
+
+            /* Implementors need to overwrite this implementation! */
+            return -1;
+
+        case PA_SOURCE_MESSAGE_MAX:
+            ;
+    }
+
+    return -1;
+}
+
+/* Called from main thread */
+int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
+    uint32_t idx;
+    pa_source *source;
+    int ret = 0;
+
+    pa_core_assert_ref(c);
+
+    for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx)))
+        ret -= pa_source_suspend(source, suspend) < 0;
+
+    return ret;
+}
+
+/* Called from main thread */
+void pa_source_detach(pa_source *s) {
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL) == 0);
+}
+
+/* Called from main thread */
+void pa_source_attach(pa_source *s) {
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
+}
+
+/* Called from IO thread */
+void pa_source_detach_within_thread(pa_source *s) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+    while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+        if (o->detach)
+            o->detach(o);
+}
+
+/* Called from IO thread */
+void pa_source_attach_within_thread(pa_source *s) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+    while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+        if (o->attach)
+            o->attach(o);
+}
+
+/* Called from IO thread */
+pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
+    pa_usec_t result = (pa_usec_t) -1;
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+
+    if (s->thread_info.requested_latency_valid)
+        return s->thread_info.requested_latency;
+
+    while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+
+        if (o->thread_info.requested_source_latency != (pa_usec_t) -1 &&
+            (result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
+            result = o->thread_info.requested_source_latency;
+
+    if (result != (pa_usec_t) -1) {
+        if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
+            result = s->thread_info.max_latency;
+
+        if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
+            result = s->thread_info.min_latency;
+    }
+
+    s->thread_info.requested_latency = result;
+    s->thread_info.requested_latency_valid = TRUE;
+
+    return result;
+}
+
+/* Called from main thread */
+pa_usec_t pa_source_get_requested_latency(pa_source *s) {
+    pa_usec_t usec;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (!PA_SOURCE_IS_OPENED(s->state))
+        return 0;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+
+    return usec;
+}
+
+/* Called from IO thread */
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+
+    if (max_rewind == s->thread_info.max_rewind)
+        return;
+
+    s->thread_info.max_rewind = max_rewind;
+
+    if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+        while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+            pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
+    }
+}
+
+void pa_source_invalidate_requested_latency(pa_source *s) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+
+    s->thread_info.requested_latency_valid = FALSE;
+
+    if (s->update_requested_latency)
+        s->update_requested_latency(s);
+
+    while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+        o->update_source_requested_latency(o);
+
+    if (s->monitor_of)
+        pa_sink_invalidate_requested_latency(s->monitor_of);
+}
+
+void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+    pa_source_assert_ref(s);
+
+    /* min_latency == 0:           no limit
+     * min_latency == (size_t) -1: default limit
+     * min_latency anything else:  specified limit
+     *
+     * Similar for max_latency */
+
+    if (min_latency == (pa_usec_t) -1)
+        min_latency = DEFAULT_MIN_LATENCY;
+
+    if (max_latency == (pa_usec_t) -1)
+        max_latency = min_latency;
+
+    pa_assert(!min_latency || !max_latency ||
+              min_latency <= max_latency);
+
+    if (PA_SINK_IS_LINKED(s->state)) {
+        pa_usec_t r[2];
+
+        r[0] = min_latency;
+        r[1] = max_latency;
+
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
+    } else {
+        s->thread_info.min_latency = min_latency;
+        s->thread_info.max_latency = max_latency;
+
+        s->thread_info.requested_latency_valid = FALSE;
+    }
+}
+
+void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
+   pa_source_assert_ref(s);
+   pa_assert(min_latency);
+   pa_assert(max_latency);
+
+   if (PA_SOURCE_IS_LINKED(s->state)) {
+       pa_usec_t r[2] = { 0, 0 };
+
+       pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
+
+       *min_latency = r[0];
+       *max_latency = r[1];
+   } else {
+       *min_latency = s->thread_info.min_latency;
+       *max_latency = s->thread_info.max_latency;
+   }
+}
+
+/* Called from IO thread */
+void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+
+    s->thread_info.min_latency = min_latency;
+    s->thread_info.max_latency = max_latency;
+
+    while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+        if (o->update_source_latency_range)
+            o->update_source_latency_range(o);
+
+    pa_source_invalidate_requested_latency(s);
+}
+
+size_t pa_source_get_max_rewind(pa_source *s) {
+    size_t r;
+    pa_source_assert_ref(s);
+
+    if (!PA_SOURCE_IS_LINKED(s->state))
+        return s->thread_info.max_rewind;
+
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
+
+    return r;
+}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
new file mode 100644 (file)
index 0000000..f4a17e8
--- /dev/null
@@ -0,0 +1,257 @@
+#ifndef foopulsesourcehfoo
+#define foopulsesourcehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+typedef struct pa_source pa_source;
+
+#include <inttypes.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/msgobject.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/source-output.h>
+
+#define PA_MAX_OUTPUTS_PER_SOURCE 32
+
+typedef enum pa_source_state {
+    PA_SOURCE_INIT,
+    PA_SOURCE_RUNNING,
+    PA_SOURCE_SUSPENDED,
+    PA_SOURCE_IDLE,
+    PA_SOURCE_UNLINKED
+} pa_source_state_t;
+
+static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) {
+    return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
+}
+
+static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) {
+    return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
+}
+
+struct pa_source {
+    pa_msgobject parent;
+
+    uint32_t index;
+    pa_core *core;
+    pa_source_state_t state;
+    pa_source_flags_t flags;
+
+    char *name;
+    char *driver;                             /* may be NULL */
+    pa_proplist *proplist;
+
+    pa_module *module;                        /* may be NULL */
+
+    pa_sample_spec sample_spec;
+    pa_channel_map channel_map;
+
+    pa_idxset *outputs;
+    unsigned n_corked;
+    pa_sink *monitor_of;                     /* may be NULL */
+
+    pa_cvolume volume;
+    pa_bool_t muted;
+
+    pa_bool_t refresh_volume:1;
+    pa_bool_t refresh_muted:1;
+
+    pa_asyncmsgq *asyncmsgq;
+    pa_rtpoll *rtpoll;
+
+    pa_memchunk silence;
+
+    /* Called when the main loop requests a state change. Called from
+     * main loop context. If returns -1 the state change will be
+     * inhibited */
+    int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */
+
+    /* Callled when the volume is queried. Called from main loop
+     * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message
+     * will be sent to the IO thread instead. If refresh_volume is
+     * FALSE neither this function is called nor a message is sent. */
+    int (*get_volume)(pa_source *s);         /* dito */
+
+    /* Called when the volume shall be changed. Called from main loop
+     * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message
+     * will be sent to the IO thread instead. */
+    int (*set_volume)(pa_source *s);         /* dito */
+
+    /* Called when the mute setting is queried. Called from main loop
+     * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message
+     * will be sent to the IO thread instead. If refresh_mute is
+     * FALSE neither this function is called nor a message is sent.*/
+    int (*get_mute)(pa_source *s);           /* dito */
+
+    /* Called when the mute setting shall be changed. Called from main
+     * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE
+     * message will be sent to the IO thread instead. */
+    int (*set_mute)(pa_source *s);           /* dito */
+
+    /* Called when a the requested latency is changed. Called from IO
+     * thread context. */
+    void (*update_requested_latency)(pa_source *s); /* dito */
+
+    /* Contains copies of the above data so that the real-time worker
+     * thread can work without access locking */
+    struct {
+        pa_source_state_t state;
+        pa_hashmap *outputs;
+        pa_cvolume soft_volume;
+        pa_bool_t soft_muted:1;
+
+        pa_bool_t requested_latency_valid:1;
+        pa_usec_t requested_latency;
+
+        /* Then number of bytes this source will be rewound for at
+         * max. (Only used on monitor sources) */
+        size_t max_rewind;
+
+        pa_usec_t min_latency; /* we won't go below this latency */
+        pa_usec_t max_latency; /* An upper limit for the latencies */
+    } thread_info;
+
+    void *userdata;
+};
+
+PA_DECLARE_CLASS(pa_source);
+#define PA_SOURCE(s) pa_source_cast(s)
+
+typedef enum pa_source_message {
+    PA_SOURCE_MESSAGE_ADD_OUTPUT,
+    PA_SOURCE_MESSAGE_REMOVE_OUTPUT,
+    PA_SOURCE_MESSAGE_GET_VOLUME,
+    PA_SOURCE_MESSAGE_SET_VOLUME,
+    PA_SOURCE_MESSAGE_GET_MUTE,
+    PA_SOURCE_MESSAGE_SET_MUTE,
+    PA_SOURCE_MESSAGE_GET_LATENCY,
+    PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY,
+    PA_SOURCE_MESSAGE_SET_STATE,
+    PA_SOURCE_MESSAGE_ATTACH,
+    PA_SOURCE_MESSAGE_DETACH,
+    PA_SOURCE_MESSAGE_SET_LATENCY_RANGE,
+    PA_SOURCE_MESSAGE_GET_LATENCY_RANGE,
+    PA_SOURCE_MESSAGE_GET_MAX_REWIND,
+    PA_SOURCE_MESSAGE_MAX
+} pa_source_message_t;
+
+typedef struct pa_source_new_data {
+    char *name;
+    pa_bool_t namereg_fail;
+    pa_proplist *proplist;
+
+    const char *driver;
+    pa_module *module;
+
+    pa_sample_spec sample_spec;
+    pa_bool_t sample_spec_is_set;
+    pa_channel_map channel_map;
+    pa_bool_t channel_map_is_set;
+
+    pa_cvolume volume;
+    pa_bool_t volume_is_set;
+    pa_bool_t muted;
+    pa_bool_t muted_is_set;
+} pa_source_new_data;
+
+pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data);
+void pa_source_new_data_set_name(pa_source_new_data *data, const char *name);
+void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec);
+void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map);
+void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume);
+void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute);
+void pa_source_new_data_done(pa_source_new_data *data);
+
+/* To be called exclusively by the source driver, from main context */
+
+pa_source* pa_source_new(
+        pa_core *core,
+        pa_source_new_data *data,
+        pa_source_flags_t flags);
+
+void pa_source_put(pa_source *s);
+void pa_source_unlink(pa_source *s);
+
+void pa_source_set_description(pa_source *s, const char *description);
+void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q);
+void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p);
+
+void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
+void pa_source_detach(pa_source *s);
+void pa_source_attach(pa_source *s);
+
+/* May be called by everyone, from main context */
+
+/* The returned value is supposed to be in the time domain of the sound card! */
+pa_usec_t pa_source_get_latency(pa_source *s);
+pa_usec_t pa_source_get_requested_latency(pa_source *s);
+void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency);
+
+size_t pa_source_get_max_rewind(pa_source *s);
+
+int pa_source_update_status(pa_source*s);
+int pa_source_suspend(pa_source *s, pa_bool_t suspend);
+int pa_source_suspend_all(pa_core *c, pa_bool_t suspend);
+
+void pa_source_set_volume(pa_source *source, const pa_cvolume *volume);
+const pa_cvolume *pa_source_get_volume(pa_source *source);
+void pa_source_set_mute(pa_source *source, pa_bool_t mute);
+pa_bool_t pa_source_get_mute(pa_source *source);
+
+unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */
+unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */
+#define pa_source_get_state(s) ((pa_source_state_t) (s)->state)
+
+/* To be called exclusively by the source driver, from IO context */
+
+void pa_source_post(pa_source*s, const pa_memchunk *chunk);
+void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk);
+void pa_source_process_rewind(pa_source *s, size_t nbytes);
+
+int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa_memchunk *chunk);
+
+void pa_source_attach_within_thread(pa_source *s);
+void pa_source_detach_within_thread(pa_source *s);
+
+pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s);
+
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
+void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+
+/* To be called exclusively by source output drivers, from IO context */
+
+void pa_source_invalidate_requested_latency(pa_source *s);
+
+#endif
diff --git a/src/pulsecore/speex/Makefile b/src/pulsecore/speex/Makefile
new file mode 100644 (file)
index 0000000..316beb7
--- /dev/null
@@ -0,0 +1,13 @@
+# This is a dirty trick just to ease compilation with emacs
+#
+# This file is not intended to be distributed or anything
+#
+# So: don't touch it, even better ignore it!
+
+all:
+       $(MAKE) -C ../..
+
+clean:
+       $(MAKE) -C ../.. clean
+
+.PHONY: all clean
diff --git a/src/pulsecore/speex/arch.h b/src/pulsecore/speex/arch.h
new file mode 100644 (file)
index 0000000..9987c8f
--- /dev/null
@@ -0,0 +1,241 @@
+/* Copyright (C) 2003 Jean-Marc Valin */
+/**
+   @file arch.h
+   @brief Various architecture definitions Speex
+*/
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   - Neither the name of the Xiph.org Foundation nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef ARCH_H
+#define ARCH_H
+
+#ifndef SPEEX_VERSION
+#define SPEEX_MAJOR_VERSION 1         /**< Major Speex version. */
+#define SPEEX_MINOR_VERSION 1         /**< Minor Speex version. */
+#define SPEEX_MICRO_VERSION 15        /**< Micro Speex version. */
+#define SPEEX_EXTRA_VERSION ""        /**< Extra Speex version. */
+#define SPEEX_VERSION "speex-1.2beta3"  /**< Speex version string. */
+#endif
+
+/* A couple test to catch stupid option combinations */
+#ifdef FIXED_POINT
+
+#ifdef FLOATING_POINT
+#error You cannot compile as floating point and fixed point at the same time
+#endif
+#ifdef _USE_SSE
+#error SSE is only for floating-point
+#endif
+#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
+#error Make up your mind. What CPU do you have?
+#endif
+#ifdef VORBIS_PSYCHO
+#error Vorbis-psy model currently not implemented in fixed-point
+#endif
+
+#else
+
+#ifndef FLOATING_POINT
+#error You now need to define either FIXED_POINT or FLOATING_POINT
+#endif
+#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
+#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
+#endif
+#ifdef FIXED_POINT_DEBUG
+#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
+#endif
+
+
+#endif
+
+#ifndef OUTSIDE_SPEEX
+#include "speex/speex_types.h"
+#endif
+
+#define ABS(x) ((x) < 0 ? (-(x)) : (x))      /**< Absolute integer value. */
+#define ABS16(x) ((x) < 0 ? (-(x)) : (x))    /**< Absolute 16-bit value.  */
+#define MIN16(a,b) ((a) < (b) ? (a) : (b))   /**< Maximum 16-bit value.   */
+#define MAX16(a,b) ((a) > (b) ? (a) : (b))   /**< Maximum 16-bit value.   */
+#define ABS32(x) ((x) < 0 ? (-(x)) : (x))    /**< Absolute 32-bit value.  */
+#define MIN32(a,b) ((a) < (b) ? (a) : (b))   /**< Maximum 32-bit value.   */
+#define MAX32(a,b) ((a) > (b) ? (a) : (b))   /**< Maximum 32-bit value.   */
+
+#ifdef FIXED_POINT
+
+typedef spx_int16_t spx_word16_t;
+typedef spx_int32_t   spx_word32_t;
+typedef spx_word32_t spx_mem_t;
+typedef spx_word16_t spx_coef_t;
+typedef spx_word16_t spx_lsp_t;
+typedef spx_word32_t spx_sig_t;
+
+#define Q15ONE 32767
+
+#define LPC_SCALING  8192
+#define SIG_SCALING  16384
+#define LSP_SCALING  8192.
+#define GAMMA_SCALING 32768.
+#define GAIN_SCALING 64
+#define GAIN_SCALING_1 0.015625
+
+#define LPC_SHIFT    13
+#define LSP_SHIFT    13
+#define SIG_SHIFT    14
+#define GAIN_SHIFT   6
+
+#define VERY_SMALL 0
+#define VERY_LARGE32 ((spx_word32_t)2147483647)
+#define VERY_LARGE16 ((spx_word16_t)32767)
+#define Q15_ONE ((spx_word16_t)32767)
+
+
+#ifdef FIXED_DEBUG
+#include "fixed_debug.h"
+#else
+
+#include "fixed_generic.h"
+
+#ifdef ARM5E_ASM
+#include "fixed_arm5e.h"
+#elif defined (ARM4_ASM)
+#include "fixed_arm4.h"
+#elif defined (ARM5E_ASM)
+#include "fixed_arm5e.h"
+#elif defined (BFIN_ASM)
+#include "fixed_bfin.h"
+#endif
+
+#endif
+
+
+#else
+
+typedef float spx_mem_t;
+typedef float spx_coef_t;
+typedef float spx_lsp_t;
+typedef float spx_sig_t;
+typedef float spx_word16_t;
+typedef float spx_word32_t;
+
+#define Q15ONE 1.0f
+#define LPC_SCALING  1.f
+#define SIG_SCALING  1.f
+#define LSP_SCALING  1.f
+#define GAMMA_SCALING 1.f
+#define GAIN_SCALING 1.f
+#define GAIN_SCALING_1 1.f
+
+
+#define VERY_SMALL 1e-15f
+#define VERY_LARGE32 1e15f
+#define VERY_LARGE16 1e15f
+#define Q15_ONE ((spx_word16_t)1.f)
+
+#define QCONST16(x,bits) (x)
+#define QCONST32(x,bits) (x)
+
+#define NEG16(x) (-(x))
+#define NEG32(x) (-(x))
+#define EXTRACT16(x) (x)
+#define EXTEND32(x) (x)
+#define SHR16(a,shift) (a)
+#define SHL16(a,shift) (a)
+#define SHR32(a,shift) (a)
+#define SHL32(a,shift) (a)
+#define PSHR16(a,shift) (a)
+#define PSHR32(a,shift) (a)
+#define VSHR32(a,shift) (a)
+#define SATURATE16(x,a) (x)
+#define SATURATE32(x,a) (x)
+
+#define PSHR(a,shift)       (a)
+#define SHR(a,shift)       (a)
+#define SHL(a,shift)       (a)
+#define SATURATE(x,a) (x)
+
+#define ADD16(a,b) ((a)+(b))
+#define SUB16(a,b) ((a)-(b))
+#define ADD32(a,b) ((a)+(b))
+#define SUB32(a,b) ((a)-(b))
+#define MULT16_16_16(a,b)     ((a)*(b))
+#define MULT16_16(a,b)     ((spx_word32_t)(a)*(spx_word32_t)(b))
+#define MAC16_16(c,a,b)     ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
+
+#define MULT16_32_Q11(a,b)     ((a)*(b))
+#define MULT16_32_Q13(a,b)     ((a)*(b))
+#define MULT16_32_Q14(a,b)     ((a)*(b))
+#define MULT16_32_Q15(a,b)     ((a)*(b))
+#define MULT16_32_P15(a,b)     ((a)*(b))
+
+#define MAC16_32_Q11(c,a,b)     ((c)+(a)*(b))
+#define MAC16_32_Q15(c,a,b)     ((c)+(a)*(b))
+
+#define MAC16_16_Q11(c,a,b)     ((c)+(a)*(b))
+#define MAC16_16_Q13(c,a,b)     ((c)+(a)*(b))
+#define MAC16_16_P13(c,a,b)     ((c)+(a)*(b))
+#define MULT16_16_Q11_32(a,b)     ((a)*(b))
+#define MULT16_16_Q13(a,b)     ((a)*(b))
+#define MULT16_16_Q14(a,b)     ((a)*(b))
+#define MULT16_16_Q15(a,b)     ((a)*(b))
+#define MULT16_16_P15(a,b)     ((a)*(b))
+#define MULT16_16_P13(a,b)     ((a)*(b))
+#define MULT16_16_P14(a,b)     ((a)*(b))
+
+#define DIV32_16(a,b)     (((spx_word32_t)(a))/(spx_word16_t)(b))
+#define PDIV32_16(a,b)     (((spx_word32_t)(a))/(spx_word16_t)(b))
+#define DIV32(a,b)     (((spx_word32_t)(a))/(spx_word32_t)(b))
+#define PDIV32(a,b)     (((spx_word32_t)(a))/(spx_word32_t)(b))
+
+
+#endif
+
+
+#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
+
+/* 2 on TI C5x DSP */
+#define BYTES_PER_CHAR 2
+#define BITS_PER_CHAR 16
+#define LOG2_BITS_PER_CHAR 4
+
+#else
+
+#define BYTES_PER_CHAR 1
+#define BITS_PER_CHAR 8
+#define LOG2_BITS_PER_CHAR 3
+
+#endif
+
+
+
+#ifdef FIXED_DEBUG
+long long spx_mips=0;
+#endif
+
+
+#endif
diff --git a/src/pulsecore/speex/fixed_generic.h b/src/pulsecore/speex/fixed_generic.h
new file mode 100644 (file)
index 0000000..547e22c
--- /dev/null
@@ -0,0 +1,106 @@
+/* Copyright (C) 2003 Jean-Marc Valin */
+/**
+   @file fixed_generic.h
+   @brief Generic fixed-point operations
+*/
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   - Neither the name of the Xiph.org Foundation nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef FIXED_GENERIC_H
+#define FIXED_GENERIC_H
+
+#define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
+#define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
+
+#define NEG16(x) (-(x))
+#define NEG32(x) (-(x))
+#define EXTRACT16(x) ((spx_word16_t)(x))
+#define EXTEND32(x) ((spx_word32_t)(x))
+#define SHR16(a,shift) ((a) >> (shift))
+#define SHL16(a,shift) ((a) << (shift))
+#define SHR32(a,shift) ((a) >> (shift))
+#define SHL32(a,shift) ((a) << (shift))
+#define PSHR16(a,shift) (SHR16((a)+((1<<((shift))>>1)),shift))
+#define PSHR32(a,shift) (SHR32((a)+((1<<((shift))>>1)),shift))
+#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift)))
+#define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+#define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+
+#define SHR(a,shift) ((a) >> (shift))
+#define SHL(a,shift) ((spx_word32_t)(a) << (shift))
+#define PSHR(a,shift) (SHR((a)+((1<<((shift))>>1)),shift))
+#define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
+
+
+#define ADD16(a,b) ((spx_word16_t)((spx_word16_t)(a)+(spx_word16_t)(b)))
+#define SUB16(a,b) ((spx_word16_t)(a)-(spx_word16_t)(b))
+#define ADD32(a,b) ((spx_word32_t)(a)+(spx_word32_t)(b))
+#define SUB32(a,b) ((spx_word32_t)(a)-(spx_word32_t)(b))
+
+
+/* result fits in 16 bits */
+#define MULT16_16_16(a,b)     ((((spx_word16_t)(a))*((spx_word16_t)(b))))
+
+/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */
+#define MULT16_16(a,b)     (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b)))
+
+#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b))))
+#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12))
+#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13))
+#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14))
+
+#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))
+#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)))
+
+#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
+#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
+#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)))
+
+
+#define MAC16_16_Q11(c,a,b)     (ADD32((c),SHR(MULT16_16((a),(b)),11)))
+#define MAC16_16_Q13(c,a,b)     (ADD32((c),SHR(MULT16_16((a),(b)),13)))
+#define MAC16_16_P13(c,a,b)     (ADD32((c),SHR(ADD32(4096,MULT16_16((a),(b))),13)))
+
+#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11))
+#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13))
+#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14))
+#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15))
+
+#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13))
+#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14))
+#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15))
+
+#define MUL_16_32_R15(a,bh,bl) ADD32(MULT16_16((a),(bh)), SHR(MULT16_16((a),(bl)),15))
+
+#define DIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a))/((spx_word16_t)(b))))
+#define PDIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word16_t)(b))))
+#define DIV32(a,b) (((spx_word32_t)(a))/((spx_word32_t)(b)))
+#define PDIV32(a,b) (((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word32_t)(b)))
+
+#endif
diff --git a/src/pulsecore/speex/resample.c b/src/pulsecore/speex/resample.c
new file mode 100644 (file)
index 0000000..1e59200
--- /dev/null
@@ -0,0 +1,1121 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+
+   File: resample.c
+   Arbitrary resampling code
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+   1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+   2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+   POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+   The design goals of this code are:
+      - Very fast algorithm
+      - SIMD-friendly algorithm
+      - Low memory requirement
+      - Good *perceptual* quality (and not best SNR)
+
+   Warning: This resampler is relatively new. Although I think I got rid of
+   all the major bugs and I don't expect the API to change anymore, there
+   may be something I've missed. So use with caution.
+
+   This algorithm is based on this original resampling algorithm:
+   Smith, Julius O. Digital Audio Resampling Home Page
+   Center for Computer Research in Music and Acoustics (CCRMA),
+   Stanford University, 2007.
+   Web published at http://www-ccrma.stanford.edu/~jos/resample/.
+
+   There is one main difference, though. This resampler uses cubic
+   interpolation instead of linear interpolation in the above paper. This
+   makes the table much smaller and makes it possible to compute that table
+   on a per-stream basis. In turn, being able to tweak the table for each
+   stream makes it possible to both reduce complexity on simple ratios
+   (e.g. 2/3), and get rid of the rounding operations in the inner loop.
+   The latter both reduces CPU time and makes the algorithm more SIMD-friendly.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef OUTSIDE_SPEEX
+#include <stdlib.h>
+static void *speex_alloc (int size) {return calloc(size,1);}
+static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);}
+static void speex_free (void *ptr) {free(ptr);}
+#include "speex_resampler.h"
+#include "arch.h"
+#else /* OUTSIDE_SPEEX */
+
+#include "speex/speex_resampler.h"
+#include "arch.h"
+#include "os_support.h"
+#endif /* OUTSIDE_SPEEX */
+
+#include <math.h>
+
+#ifndef M_PI
+#define M_PI 3.14159263
+#endif
+
+#ifdef FIXED_POINT
+#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
+#else
+#define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x))))
+#endif
+
+/*#define float double*/
+#define FILTER_SIZE 64
+#define OVERSAMPLE 8
+
+#define IMAX(a,b) ((a) > (b) ? (a) : (b))
+#define IMIN(a,b) ((a) < (b) ? (a) : (b))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *);
+
+struct SpeexResamplerState_ {
+   spx_uint32_t in_rate;
+   spx_uint32_t out_rate;
+   spx_uint32_t num_rate;
+   spx_uint32_t den_rate;
+
+   int    quality;
+   spx_uint32_t nb_channels;
+   spx_uint32_t filt_len;
+   spx_uint32_t mem_alloc_size;
+   int          int_advance;
+   int          frac_advance;
+   float  cutoff;
+   spx_uint32_t oversample;
+   int          initialised;
+   int          started;
+
+   /* These are per-channel */
+   spx_int32_t  *last_sample;
+   spx_uint32_t *samp_frac_num;
+   spx_uint32_t *magic_samples;
+
+   spx_word16_t *mem;
+   spx_word16_t *sinc_table;
+   spx_uint32_t sinc_table_length;
+   resampler_basic_func resampler_ptr;
+
+   int    in_stride;
+   int    out_stride;
+} ;
+
+static double kaiser12_table[68] = {
+   0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076,
+   0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014,
+   0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601,
+   0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014,
+   0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490,
+   0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546,
+   0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178,
+   0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947,
+   0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058,
+   0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438,
+   0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734,
+   0.00001000, 0.00000000};
+/*
+static double kaiser12_table[36] = {
+   0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741,
+   0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762,
+   0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274,
+   0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466,
+   0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291,
+   0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000};
+*/
+static double kaiser10_table[36] = {
+   0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446,
+   0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347,
+   0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962,
+   0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451,
+   0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739,
+   0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000};
+
+static double kaiser8_table[36] = {
+   0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200,
+   0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126,
+   0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272,
+   0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758,
+   0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490,
+   0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000};
+
+static double kaiser6_table[36] = {
+   0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003,
+   0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565,
+   0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561,
+   0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058,
+   0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600,
+   0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000};
+
+struct FuncDef {
+   double *table;
+   int oversample;
+};
+
+static struct FuncDef _KAISER12 = {kaiser12_table, 64};
+#define KAISER12 (&_KAISER12)
+/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
+#define KAISER12 (&_KAISER12)*/
+static struct FuncDef _KAISER10 = {kaiser10_table, 32};
+#define KAISER10 (&_KAISER10)
+static struct FuncDef _KAISER8 = {kaiser8_table, 32};
+#define KAISER8 (&_KAISER8)
+static struct FuncDef _KAISER6 = {kaiser6_table, 32};
+#define KAISER6 (&_KAISER6)
+
+struct QualityMapping {
+   int base_length;
+   int oversample;
+   float downsample_bandwidth;
+   float upsample_bandwidth;
+   struct FuncDef *window_func;
+};
+
+
+/* This table maps conversion quality to internal parameters. There are two
+   reasons that explain why the up-sampling bandwidth is larger than the
+   down-sampling bandwidth:
+   1) When up-sampling, we can assume that the spectrum is already attenuated
+      close to the Nyquist rate (from an A/D or a previous resampling filter)
+   2) Any aliasing that occurs very close to the Nyquist rate will be masked
+      by the sinusoids/noise just below the Nyquist rate (guaranteed only for
+      up-sampling).
+*/
+static const struct QualityMapping quality_map[11] = {
+   {  8,  4, 0.830f, 0.860f, KAISER6 }, /* Q0 */
+   { 16,  4, 0.850f, 0.880f, KAISER6 }, /* Q1 */
+   { 32,  4, 0.882f, 0.910f, KAISER6 }, /* Q2 */  /* 82.3% cutoff ( ~60 dB stop) 6  */
+   { 48,  8, 0.895f, 0.917f, KAISER8 }, /* Q3 */  /* 84.9% cutoff ( ~80 dB stop) 8  */
+   { 64,  8, 0.921f, 0.940f, KAISER8 }, /* Q4 */  /* 88.7% cutoff ( ~80 dB stop) 8  */
+   { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */  /* 89.1% cutoff (~100 dB stop) 10 */
+   { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */  /* 91.5% cutoff (~100 dB stop) 10 */
+   {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */  /* 93.1% cutoff (~100 dB stop) 10 */
+   {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */  /* 94.5% cutoff (~100 dB stop) 10 */
+   {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */  /* 95.5% cutoff (~100 dB stop) 10 */
+   {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */
+};
+/*8,24,40,56,80,104,128,160,200,256,320*/
+static double compute_func(float x, struct FuncDef *func)
+{
+   float y, frac;
+   double interp[4];
+   int ind;
+   y = x*func->oversample;
+   ind = (int)floor(y);
+   frac = (y-ind);
+   /* CSE with handle the repeated powers */
+   interp[3] =  -0.1666666667*frac + 0.1666666667*(frac*frac*frac);
+   interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac);
+   /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
+   interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac);
+   /* Just to make sure we don't have rounding problems */
+   interp[1] = 1.f-interp[3]-interp[2]-interp[0];
+
+   /*sum = frac*accum[1] + (1-frac)*accum[2];*/
+   return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3];
+}
+
+#if 0
+#include <stdio.h>
+int main(int argc, char **argv)
+{
+   int i;
+   for (i=0;i<256;i++)
+   {
+      printf ("%f\n", compute_func(i/256., KAISER12));
+   }
+   return 0;
+}
+#endif
+
+#ifdef FIXED_POINT
+/* The slow way of computing a sinc for the table. Should improve that some day */
+static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func)
+{
+   /*fprintf (stderr, "%f ", x);*/
+   float xx = x * cutoff;
+   if (fabs(x)<1e-6f)
+      return WORD2INT(32768.*cutoff);
+   else if (fabs(x) > .5f*N)
+      return 0;
+   /*FIXME: Can it really be any slower than this? */
+   return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func));
+}
+#else
+/* The slow way of computing a sinc for the table. Should improve that some day */
+static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func)
+{
+   /*fprintf (stderr, "%f ", x);*/
+   float xx = x * cutoff;
+   if (fabs(x)<1e-6)
+      return cutoff;
+   else if (fabs(x) > .5*N)
+      return 0;
+   /*FIXME: Can it really be any slower than this? */
+   return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func);
+}
+#endif
+
+#ifdef FIXED_POINT
+static void cubic_coef(spx_word16_t x, spx_word16_t interp[4])
+{
+   /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
+   but I know it's MMSE-optimal on a sinc */
+   spx_word16_t x2, x3;
+   x2 = MULT16_16_P15(x, x);
+   x3 = MULT16_16_P15(x, x2);
+   interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15);
+   interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1));
+   interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15);
+   /* Just to make sure we don't have rounding problems */
+   interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3];
+   if (interp[2]<32767)
+      interp[2]+=1;
+}
+#else
+static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4])
+{
+   /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
+   but I know it's MMSE-optimal on a sinc */
+   interp[0] =  -0.16667f*frac + 0.16667f*frac*frac*frac;
+   interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac;
+   /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
+   interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac;
+   /* Just to make sure we don't have rounding problems */
+   interp[2] = 1.-interp[0]-interp[1]-interp[3];
+}
+#endif
+
+static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+   int N = st->filt_len;
+   int out_sample = 0;
+   spx_word16_t *mem;
+   int last_sample = st->last_sample[channel_index];
+   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+   mem = st->mem + channel_index * st->mem_alloc_size;
+   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+   {
+      int j;
+      spx_word32_t sum=0;
+
+      /* We already have all the filter coefficients pre-computed in the table */
+      const spx_word16_t *ptr;
+      /* Do the memory part */
+      for (j=0;last_sample-N+1+j < 0;j++)
+      {
+         sum += MULT16_16(mem[last_sample+j],st->sinc_table[samp_frac_num*st->filt_len+j]);
+      }
+
+      /* Do the new part */
+      ptr = in+st->in_stride*(last_sample-N+1+j);
+      for (;j<N;j++)
+      {
+         sum += MULT16_16(*ptr,st->sinc_table[samp_frac_num*st->filt_len+j]);
+         ptr += st->in_stride;
+      }
+
+      *out = PSHR32(sum,15);
+      out += st->out_stride;
+      out_sample++;
+      last_sample += st->int_advance;
+      samp_frac_num += st->frac_advance;
+      if (samp_frac_num >= st->den_rate)
+      {
+         samp_frac_num -= st->den_rate;
+         last_sample++;
+      }
+   }
+   st->last_sample[channel_index] = last_sample;
+   st->samp_frac_num[channel_index] = samp_frac_num;
+   return out_sample;
+}
+
+#ifdef FIXED_POINT
+#else
+/* This is the same as the previous function, except with a double-precision accumulator */
+static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+   int N = st->filt_len;
+   int out_sample = 0;
+   spx_word16_t *mem;
+   int last_sample = st->last_sample[channel_index];
+   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+   mem = st->mem + channel_index * st->mem_alloc_size;
+   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+   {
+      int j;
+      double sum=0;
+
+      /* We already have all the filter coefficients pre-computed in the table */
+      const spx_word16_t *ptr;
+      /* Do the memory part */
+      for (j=0;last_sample-N+1+j < 0;j++)
+      {
+         sum += MULT16_16(mem[last_sample+j],(double)st->sinc_table[samp_frac_num*st->filt_len+j]);
+      }
+
+      /* Do the new part */
+      ptr = in+st->in_stride*(last_sample-N+1+j);
+      for (;j<N;j++)
+      {
+         sum += MULT16_16(*ptr,(double)st->sinc_table[samp_frac_num*st->filt_len+j]);
+         ptr += st->in_stride;
+      }
+
+      *out = sum;
+      out += st->out_stride;
+      out_sample++;
+      last_sample += st->int_advance;
+      samp_frac_num += st->frac_advance;
+      if (samp_frac_num >= st->den_rate)
+      {
+         samp_frac_num -= st->den_rate;
+         last_sample++;
+      }
+   }
+   st->last_sample[channel_index] = last_sample;
+   st->samp_frac_num[channel_index] = samp_frac_num;
+   return out_sample;
+}
+#endif
+
+static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+   int N = st->filt_len;
+   int out_sample = 0;
+   spx_word16_t *mem;
+   int last_sample = st->last_sample[channel_index];
+   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+   mem = st->mem + channel_index * st->mem_alloc_size;
+   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+   {
+      int j;
+      spx_word32_t sum=0;
+
+      /* We need to interpolate the sinc filter */
+      spx_word32_t accum[4] = {0.f,0.f, 0.f, 0.f};
+      spx_word16_t interp[4];
+      const spx_word16_t *ptr;
+      int offset;
+      spx_word16_t frac;
+      offset = samp_frac_num*st->oversample/st->den_rate;
+#ifdef FIXED_POINT
+      frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate);
+#else
+      frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate;
+#endif
+         /* This code is written like this to make it easy to optimise with SIMD.
+      For most DSPs, it would be best to split the loops in two because most DSPs
+      have only two accumulators */
+      for (j=0;last_sample-N+1+j < 0;j++)
+      {
+         spx_word16_t curr_mem = mem[last_sample+j];
+         accum[0] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+         accum[1] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+         accum[2] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset]);
+         accum[3] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+      }
+      ptr = in+st->in_stride*(last_sample-N+1+j);
+      /* Do the new part */
+      for (;j<N;j++)
+      {
+         spx_word16_t curr_in = *ptr;
+         ptr += st->in_stride;
+         accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+         accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+         accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
+         accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+      }
+      cubic_coef(frac, interp);
+      sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
+
+      *out = PSHR32(sum,15);
+      out += st->out_stride;
+      out_sample++;
+      last_sample += st->int_advance;
+      samp_frac_num += st->frac_advance;
+      if (samp_frac_num >= st->den_rate)
+      {
+         samp_frac_num -= st->den_rate;
+         last_sample++;
+      }
+   }
+   st->last_sample[channel_index] = last_sample;
+   st->samp_frac_num[channel_index] = samp_frac_num;
+   return out_sample;
+}
+
+#ifdef FIXED_POINT
+#else
+/* This is the same as the previous function, except with a double-precision accumulator */
+static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+   int N = st->filt_len;
+   int out_sample = 0;
+   spx_word16_t *mem;
+   int last_sample = st->last_sample[channel_index];
+   spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
+   mem = st->mem + channel_index * st->mem_alloc_size;
+   while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
+   {
+      int j;
+      spx_word32_t sum=0;
+
+      /* We need to interpolate the sinc filter */
+      double accum[4] = {0.f,0.f, 0.f, 0.f};
+      float interp[4];
+      const spx_word16_t *ptr;
+      float alpha = ((float)samp_frac_num)/st->den_rate;
+      int offset = samp_frac_num*st->oversample/st->den_rate;
+      float frac = alpha*st->oversample - offset;
+         /* This code is written like this to make it easy to optimise with SIMD.
+      For most DSPs, it would be best to split the loops in two because most DSPs
+      have only two accumulators */
+      for (j=0;last_sample-N+1+j < 0;j++)
+      {
+         double curr_mem = mem[last_sample+j];
+         accum[0] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+         accum[1] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+         accum[2] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset]);
+         accum[3] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+      }
+      ptr = in+st->in_stride*(last_sample-N+1+j);
+      /* Do the new part */
+      for (;j<N;j++)
+      {
+         double curr_in = *ptr;
+         ptr += st->in_stride;
+         accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
+         accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
+         accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
+         accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
+      }
+      cubic_coef(frac, interp);
+      sum = interp[0]*accum[0] + interp[1]*accum[1] + interp[2]*accum[2] + interp[3]*accum[3];
+
+      *out = PSHR32(sum,15);
+      out += st->out_stride;
+      out_sample++;
+      last_sample += st->int_advance;
+      samp_frac_num += st->frac_advance;
+      if (samp_frac_num >= st->den_rate)
+      {
+         samp_frac_num -= st->den_rate;
+         last_sample++;
+      }
+   }
+   st->last_sample[channel_index] = last_sample;
+   st->samp_frac_num[channel_index] = samp_frac_num;
+   return out_sample;
+}
+#endif
+
+static void update_filter(SpeexResamplerState *st)
+{
+   spx_uint32_t old_length;
+
+   old_length = st->filt_len;
+   st->oversample = quality_map[st->quality].oversample;
+   st->filt_len = quality_map[st->quality].base_length;
+
+   if (st->num_rate > st->den_rate)
+   {
+      /* down-sampling */
+      st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate;
+      /* FIXME: divide the numerator and denominator by a certain amount if they're too large */
+      st->filt_len = st->filt_len*st->num_rate / st->den_rate;
+      /* Round down to make sure we have a multiple of 4 */
+      st->filt_len &= (~0x3);
+      if (2*st->den_rate < st->num_rate)
+         st->oversample >>= 1;
+      if (4*st->den_rate < st->num_rate)
+         st->oversample >>= 1;
+      if (8*st->den_rate < st->num_rate)
+         st->oversample >>= 1;
+      if (16*st->den_rate < st->num_rate)
+         st->oversample >>= 1;
+      if (st->oversample < 1)
+         st->oversample = 1;
+   } else {
+      /* up-sampling */
+      st->cutoff = quality_map[st->quality].upsample_bandwidth;
+   }
+
+   /* Choose the resampling type that requires the least amount of memory */
+   if (st->den_rate <= st->oversample)
+   {
+      spx_uint32_t i;
+      if (!st->sinc_table)
+         st->sinc_table = (spx_word16_t *)speex_alloc(st->filt_len*st->den_rate*sizeof(spx_word16_t));
+      else if (st->sinc_table_length < st->filt_len*st->den_rate)
+      {
+         st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,st->filt_len*st->den_rate*sizeof(spx_word16_t));
+         st->sinc_table_length = st->filt_len*st->den_rate;
+      }
+      for (i=0;i<st->den_rate;i++)
+      {
+         spx_int32_t j;
+         for (j=0;j<st->filt_len;j++)
+         {
+            st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func);
+         }
+      }
+#ifdef FIXED_POINT
+      st->resampler_ptr = resampler_basic_direct_single;
+#else
+      if (st->quality>8)
+         st->resampler_ptr = resampler_basic_direct_double;
+      else
+         st->resampler_ptr = resampler_basic_direct_single;
+#endif
+      /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/
+   } else {
+      spx_int32_t i;
+      if (!st->sinc_table)
+         st->sinc_table = (spx_word16_t *)speex_alloc((st->filt_len*st->oversample+8)*sizeof(spx_word16_t));
+      else if (st->sinc_table_length < st->filt_len*st->oversample+8)
+      {
+         st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,(st->filt_len*st->oversample+8)*sizeof(spx_word16_t));
+         st->sinc_table_length = st->filt_len*st->oversample+8;
+      }
+      for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++)
+         st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func);
+#ifdef FIXED_POINT
+      st->resampler_ptr = resampler_basic_interpolate_single;
+#else
+      if (st->quality>8)
+         st->resampler_ptr = resampler_basic_interpolate_double;
+      else
+         st->resampler_ptr = resampler_basic_interpolate_single;
+#endif
+      /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/
+   }
+   st->int_advance = st->num_rate/st->den_rate;
+   st->frac_advance = st->num_rate%st->den_rate;
+
+
+   /* Here's the place where we update the filter memory to take into account
+      the change in filter length. It's probably the messiest part of the code
+      due to handling of lots of corner cases. */
+   if (!st->mem)
+   {
+      spx_uint32_t i;
+      st->mem = (spx_word16_t*)speex_alloc(st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
+      for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
+         st->mem[i] = 0;
+      st->mem_alloc_size = st->filt_len-1;
+      /*speex_warning("init filter");*/
+   } else if (!st->started)
+   {
+      spx_uint32_t i;
+      st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
+      for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
+         st->mem[i] = 0;
+      st->mem_alloc_size = st->filt_len-1;
+      /*speex_warning("reinit filter");*/
+   } else if (st->filt_len > old_length)
+   {
+      spx_int32_t i;
+      /* Increase the filter length */
+      /*speex_warning("increase filter size");*/
+      int old_alloc_size = st->mem_alloc_size;
+      if (st->filt_len-1 > st->mem_alloc_size)
+      {
+         st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
+         st->mem_alloc_size = st->filt_len-1;
+      }
+      for (i=st->nb_channels-1;i>=0;i--)
+      {
+         spx_int32_t j;
+         spx_uint32_t olen = old_length;
+         /*if (st->magic_samples[i])*/
+         {
+            /* Try and remove the magic samples as if nothing had happened */
+
+            /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
+            olen = old_length + 2*st->magic_samples[i];
+            for (j=old_length-2+st->magic_samples[i];j>=0;j--)
+               st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
+            for (j=0;j<st->magic_samples[i];j++)
+               st->mem[i*st->mem_alloc_size+j] = 0;
+            st->magic_samples[i] = 0;
+         }
+         if (st->filt_len > olen)
+         {
+            /* If the new filter length is still bigger than the "augmented" length */
+            /* Copy data going backward */
+            for (j=0;j<olen-1;j++)
+               st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
+            /* Then put zeros for lack of anything better */
+            for (;j<st->filt_len-1;j++)
+               st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
+            /* Adjust last_sample */
+            st->last_sample[i] += (st->filt_len - olen)/2;
+         } else {
+            /* Put back some of the magic! */
+            st->magic_samples[i] = (olen - st->filt_len)/2;
+            for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
+               st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
+         }
+      }
+   } else if (st->filt_len < old_length)
+   {
+      spx_uint32_t i;
+      /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic"
+         samples so they can be used directly as input the next time(s) */
+      for (i=0;i<st->nb_channels;i++)
+      {
+         spx_uint32_t j;
+         spx_uint32_t old_magic = st->magic_samples[i];
+         st->magic_samples[i] = (old_length - st->filt_len)/2;
+         /* We must copy some of the memory that's no longer used */
+         /* Copy data going backward */
+         for (j=0;j<st->filt_len-1+st->magic_samples[i]+old_magic;j++)
+            st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
+         st->magic_samples[i] += old_magic;
+      }
+   }
+
+}
+
+SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
+{
+   return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err);
+}
+
+SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
+{
+   spx_uint32_t i;
+   SpeexResamplerState *st;
+   if (quality > 10 || quality < 0)
+   {
+      if (err)
+         *err = RESAMPLER_ERR_INVALID_ARG;
+      return NULL;
+   }
+   st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState));
+   st->initialised = 0;
+   st->started = 0;
+   st->in_rate = 0;
+   st->out_rate = 0;
+   st->num_rate = 0;
+   st->den_rate = 0;
+   st->quality = -1;
+   st->sinc_table_length = 0;
+   st->mem_alloc_size = 0;
+   st->filt_len = 0;
+   st->mem = 0;
+   st->resampler_ptr = 0;
+
+   st->cutoff = 1.f;
+   st->nb_channels = nb_channels;
+   st->in_stride = 1;
+   st->out_stride = 1;
+
+   /* Per channel data */
+   st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(int));
+   st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int));
+   st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int));
+   for (i=0;i<nb_channels;i++)
+   {
+      st->last_sample[i] = 0;
+      st->magic_samples[i] = 0;
+      st->samp_frac_num[i] = 0;
+   }
+
+   speex_resampler_set_quality(st, quality);
+   speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate);
+
+
+   update_filter(st);
+
+   st->initialised = 1;
+   if (err)
+      *err = RESAMPLER_ERR_SUCCESS;
+
+   return st;
+}
+
+void speex_resampler_destroy(SpeexResamplerState *st)
+{
+   speex_free(st->mem);
+   speex_free(st->sinc_table);
+   speex_free(st->last_sample);
+   speex_free(st->magic_samples);
+   speex_free(st->samp_frac_num);
+   speex_free(st);
+}
+
+
+
+static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
+{
+   int j=0;
+   int N = st->filt_len;
+   int out_sample = 0;
+   spx_word16_t *mem;
+   spx_uint32_t tmp_out_len = 0;
+   mem = st->mem + channel_index * st->mem_alloc_size;
+   st->started = 1;
+
+   /* Handle the case where we have samples left from a reduction in filter length */
+   if (st->magic_samples[channel_index])
+   {
+      int istride_save;
+      spx_uint32_t tmp_in_len;
+      spx_uint32_t tmp_magic;
+
+      istride_save = st->in_stride;
+      tmp_in_len = st->magic_samples[channel_index];
+      tmp_out_len = *out_len;
+      /* magic_samples needs to be set to zero to avoid infinite recursion */
+      tmp_magic = st->magic_samples[channel_index];
+      st->magic_samples[channel_index] = 0;
+      st->in_stride = 1;
+      speex_resampler_process_native(st, channel_index, mem+N-1, &tmp_in_len, out, &tmp_out_len);
+      st->in_stride = istride_save;
+      /*speex_warning_int("extra samples:", tmp_out_len);*/
+      /* If we couldn't process all "magic" input samples, save the rest for next time */
+      if (tmp_in_len < tmp_magic)
+      {
+         spx_uint32_t i;
+         st->magic_samples[channel_index] = tmp_magic-tmp_in_len;
+         for (i=0;i<st->magic_samples[channel_index];i++)
+            mem[N-1+i]=mem[N-1+i+tmp_in_len];
+      }
+      out += tmp_out_len*st->out_stride;
+      *out_len -= tmp_out_len;
+   }
+
+   /* Call the right resampler through the function ptr */
+   out_sample = st->resampler_ptr(st, channel_index, in, in_len, out, out_len);
+
+   if (st->last_sample[channel_index] < (spx_int32_t)*in_len)
+      *in_len = st->last_sample[channel_index];
+   *out_len = out_sample+tmp_out_len;
+   st->last_sample[channel_index] -= *in_len;
+
+   for (j=0;j<N-1-(spx_int32_t)*in_len;j++)
+      mem[j] = mem[j+*in_len];
+   for (;j<N-1;j++)
+      mem[j] = in[st->in_stride*(j+*in_len-N+1)];
+
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+#define FIXED_STACK_ALLOC 1024
+
+#ifdef FIXED_POINT
+int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+{
+   spx_uint32_t i;
+   int istride_save, ostride_save;
+#ifdef VAR_ARRAYS
+   spx_word16_t x[*in_len];
+   spx_word16_t y[*out_len];
+   /*VARDECL(spx_word16_t *x);
+   VARDECL(spx_word16_t *y);
+   ALLOC(x, *in_len, spx_word16_t);
+   ALLOC(y, *out_len, spx_word16_t);*/
+   istride_save = st->in_stride;
+   ostride_save = st->out_stride;
+   for (i=0;i<*in_len;i++)
+      x[i] = WORD2INT(in[i*st->in_stride]);
+   st->in_stride = st->out_stride = 1;
+   speex_resampler_process_native(st, channel_index, x, in_len, y, out_len);
+   st->in_stride = istride_save;
+   st->out_stride = ostride_save;
+   for (i=0;i<*out_len;i++)
+      out[i*st->out_stride] = y[i];
+#else
+   spx_word16_t x[FIXED_STACK_ALLOC];
+   spx_word16_t y[FIXED_STACK_ALLOC];
+   spx_uint32_t ilen=*in_len, olen=*out_len;
+   istride_save = st->in_stride;
+   ostride_save = st->out_stride;
+   while (ilen && olen)
+   {
+      spx_uint32_t ichunk, ochunk;
+      ichunk = ilen;
+      ochunk = olen;
+      if (ichunk>FIXED_STACK_ALLOC)
+         ichunk=FIXED_STACK_ALLOC;
+      if (ochunk>FIXED_STACK_ALLOC)
+         ochunk=FIXED_STACK_ALLOC;
+      for (i=0;i<ichunk;i++)
+         x[i] = WORD2INT(in[i*st->in_stride]);
+      st->in_stride = st->out_stride = 1;
+      speex_resampler_process_native(st, channel_index, x, &ichunk, y, &ochunk);
+      st->in_stride = istride_save;
+      st->out_stride = ostride_save;
+      for (i=0;i<ochunk;i++)
+         out[i*st->out_stride] = y[i];
+      out += ochunk;
+      in += ichunk;
+      ilen -= ichunk;
+      olen -= ochunk;
+   }
+   *in_len -= ilen;
+   *out_len -= olen;
+#endif
+   return RESAMPLER_ERR_SUCCESS;
+}
+int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+{
+   return speex_resampler_process_native(st, channel_index, in, in_len, out, out_len);
+}
+#else
+int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+{
+   return speex_resampler_process_native(st, channel_index, in, in_len, out, out_len);
+}
+int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+{
+   spx_uint32_t i;
+   int istride_save, ostride_save;
+#ifdef VAR_ARRAYS
+   spx_word16_t x[*in_len];
+   spx_word16_t y[*out_len];
+   /*VARDECL(spx_word16_t *x);
+   VARDECL(spx_word16_t *y);
+   ALLOC(x, *in_len, spx_word16_t);
+   ALLOC(y, *out_len, spx_word16_t);*/
+   istride_save = st->in_stride;
+   ostride_save = st->out_stride;
+   for (i=0;i<*in_len;i++)
+      x[i] = in[i*st->in_stride];
+   st->in_stride = st->out_stride = 1;
+   speex_resampler_process_native(st, channel_index, x, in_len, y, out_len);
+   st->in_stride = istride_save;
+   st->out_stride = ostride_save;
+   for (i=0;i<*out_len;i++)
+      out[i*st->out_stride] = WORD2INT(y[i]);
+#else
+   spx_word16_t x[FIXED_STACK_ALLOC];
+   spx_word16_t y[FIXED_STACK_ALLOC];
+   spx_uint32_t ilen=*in_len, olen=*out_len;
+   istride_save = st->in_stride;
+   ostride_save = st->out_stride;
+   while (ilen && olen)
+   {
+      spx_uint32_t ichunk, ochunk;
+      ichunk = ilen;
+      ochunk = olen;
+      if (ichunk>FIXED_STACK_ALLOC)
+         ichunk=FIXED_STACK_ALLOC;
+      if (ochunk>FIXED_STACK_ALLOC)
+         ochunk=FIXED_STACK_ALLOC;
+      for (i=0;i<ichunk;i++)
+         x[i] = in[i*st->in_stride];
+      st->in_stride = st->out_stride = 1;
+      speex_resampler_process_native(st, channel_index, x, &ichunk, y, &ochunk);
+      st->in_stride = istride_save;
+      st->out_stride = ostride_save;
+      for (i=0;i<ochunk;i++)
+         out[i*st->out_stride] = WORD2INT(y[i]);
+      out += ochunk;
+      in += ichunk;
+      ilen -= ichunk;
+      olen -= ochunk;
+   }
+   *in_len -= ilen;
+   *out_len -= olen;
+#endif
+   return RESAMPLER_ERR_SUCCESS;
+}
+#endif
+
+int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
+{
+   spx_uint32_t i;
+   int istride_save, ostride_save;
+   spx_uint32_t bak_len = *out_len;
+   istride_save = st->in_stride;
+   ostride_save = st->out_stride;
+   st->in_stride = st->out_stride = st->nb_channels;
+   for (i=0;i<st->nb_channels;i++)
+   {
+      *out_len = bak_len;
+      speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len);
+   }
+   st->in_stride = istride_save;
+   st->out_stride = ostride_save;
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+
+int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
+{
+   spx_uint32_t i;
+   int istride_save, ostride_save;
+   spx_uint32_t bak_len = *out_len;
+   istride_save = st->in_stride;
+   ostride_save = st->out_stride;
+   st->in_stride = st->out_stride = st->nb_channels;
+   for (i=0;i<st->nb_channels;i++)
+   {
+      *out_len = bak_len;
+      speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len);
+   }
+   st->in_stride = istride_save;
+   st->out_stride = ostride_save;
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate)
+{
+   return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate);
+}
+
+void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate)
+{
+   *in_rate = st->in_rate;
+   *out_rate = st->out_rate;
+}
+
+int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate)
+{
+   spx_uint32_t fact;
+   spx_uint32_t old_den;
+   spx_uint32_t i;
+   if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den)
+      return RESAMPLER_ERR_SUCCESS;
+
+   old_den = st->den_rate;
+   st->in_rate = in_rate;
+   st->out_rate = out_rate;
+   st->num_rate = ratio_num;
+   st->den_rate = ratio_den;
+   /* FIXME: This is terribly inefficient, but who cares (at least for now)? */
+   for (fact=2;fact<=IMIN(st->num_rate, st->den_rate);fact++)
+   {
+      while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0))
+      {
+         st->num_rate /= fact;
+         st->den_rate /= fact;
+      }
+   }
+
+   if (old_den > 0)
+   {
+      for (i=0;i<st->nb_channels;i++)
+      {
+         st->samp_frac_num[i]=st->samp_frac_num[i]*st->den_rate/old_den;
+         /* Safety net */
+         if (st->samp_frac_num[i] >= st->den_rate)
+            st->samp_frac_num[i] = st->den_rate-1;
+      }
+   }
+
+   if (st->initialised)
+      update_filter(st);
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den)
+{
+   *ratio_num = st->num_rate;
+   *ratio_den = st->den_rate;
+}
+
+int speex_resampler_set_quality(SpeexResamplerState *st, int quality)
+{
+   if (quality > 10 || quality < 0)
+      return RESAMPLER_ERR_INVALID_ARG;
+   if (st->quality == quality)
+      return RESAMPLER_ERR_SUCCESS;
+   st->quality = quality;
+   if (st->initialised)
+      update_filter(st);
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+void speex_resampler_get_quality(SpeexResamplerState *st, int *quality)
+{
+   *quality = st->quality;
+}
+
+void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride)
+{
+   st->in_stride = stride;
+}
+
+void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride)
+{
+   *stride = st->in_stride;
+}
+
+void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride)
+{
+   st->out_stride = stride;
+}
+
+void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride)
+{
+   *stride = st->out_stride;
+}
+
+int speex_resampler_skip_zeros(SpeexResamplerState *st)
+{
+   spx_uint32_t i;
+   for (i=0;i<st->nb_channels;i++)
+      st->last_sample[i] = st->filt_len/2;
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+int speex_resampler_reset_mem(SpeexResamplerState *st)
+{
+   spx_uint32_t i;
+   for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
+      st->mem[i] = 0;
+   return RESAMPLER_ERR_SUCCESS;
+}
+
+const char *speex_resampler_strerror(int err)
+{
+   switch (err)
+   {
+      case RESAMPLER_ERR_SUCCESS:
+         return "Success.";
+      case RESAMPLER_ERR_ALLOC_FAILED:
+         return "Memory allocation failed.";
+      case RESAMPLER_ERR_BAD_STATE:
+         return "Bad resampler state.";
+      case RESAMPLER_ERR_INVALID_ARG:
+         return "Invalid argument.";
+      case RESAMPLER_ERR_PTR_OVERLAP:
+         return "Input and output buffers overlap.";
+      default:
+         return "Unknown error. Bad error code or strange version mismatch.";
+   }
+}
diff --git a/src/pulsecore/speex/speex_resampler.h b/src/pulsecore/speex/speex_resampler.h
new file mode 100644 (file)
index 0000000..8629eeb
--- /dev/null
@@ -0,0 +1,328 @@
+/* Copyright (C) 2007 Jean-Marc Valin
+
+   File: speex_resampler.h
+   Resampling code
+
+   The design goals of this code are:
+      - Very fast algorithm
+      - Low memory requirement
+      - Good *perceptual* quality (and not best SNR)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+   1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+   2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+   POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef SPEEX_RESAMPLER_H
+#define SPEEX_RESAMPLER_H
+
+#ifdef OUTSIDE_SPEEX
+
+/********* WARNING: MENTAL SANITY ENDS HERE *************/
+
+/* If the resampler is defined outside of Speex, we change the symbol names so that
+   there won't be any clash if linking with Speex later on. */
+
+/* #define RANDOM_PREFIX your software name here */
+#ifndef RANDOM_PREFIX
+#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes"
+#endif
+
+#define CAT_PREFIX2(a,b) a ## b
+#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b)
+
+#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init)
+#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac)
+#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy)
+#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float)
+#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int)
+#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float)
+#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int)
+#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate)
+#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate)
+#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac)
+#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio)
+#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality)
+#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality)
+#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride)
+#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride)
+#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride)
+#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride)
+#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
+#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
+#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
+
+#define spx_int16_t short
+#define spx_int32_t int
+#define spx_uint16_t unsigned short
+#define spx_uint32_t unsigned int
+
+#else /* OUTSIDE_SPEEX */
+
+#include "speex/speex_types.h"
+
+#endif /* OUTSIDE_SPEEX */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPEEX_RESAMPLER_QUALITY_MAX 10
+#define SPEEX_RESAMPLER_QUALITY_MIN 0
+#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4
+#define SPEEX_RESAMPLER_QUALITY_VOIP 3
+#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
+
+enum {
+   RESAMPLER_ERR_SUCCESS         = 0,
+   RESAMPLER_ERR_ALLOC_FAILED    = 1,
+   RESAMPLER_ERR_BAD_STATE       = 2,
+   RESAMPLER_ERR_INVALID_ARG     = 3,
+   RESAMPLER_ERR_PTR_OVERLAP     = 4,
+
+   RESAMPLER_ERR_MAX_ERROR
+};
+
+struct SpeexResamplerState_;
+typedef struct SpeexResamplerState_ SpeexResamplerState;
+
+/** Create a new resampler with integer input and output rates.
+ * @param nb_channels Number of channels to be processed
+ * @param in_rate Input sampling rate (integer number of Hz).
+ * @param out_rate Output sampling rate (integer number of Hz).
+ * @param quality Resampling quality between 0 and 10, where 0 has poor quality
+ * and 10 has very high quality.
+ * @return Newly created resampler state
+ * @retval NULL Error: not enough memory
+ */
+SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
+                                          spx_uint32_t in_rate,
+                                          spx_uint32_t out_rate,
+                                          int quality,
+                                          int *err);
+
+/** Create a new resampler with fractional input/output rates. The sampling
+ * rate ratio is an arbitrary rational number with both the numerator and
+ * denominator being 32-bit integers.
+ * @param nb_channels Number of channels to be processed
+ * @param ratio_num Numerator of the sampling rate ratio
+ * @param ratio_den Denominator of the sampling rate ratio
+ * @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
+ * @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
+ * @param quality Resampling quality between 0 and 10, where 0 has poor quality
+ * and 10 has very high quality.
+ * @return Newly created resampler state
+ * @retval NULL Error: not enough memory
+ */
+SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels,
+                                               spx_uint32_t ratio_num,
+                                               spx_uint32_t ratio_den,
+                                               spx_uint32_t in_rate,
+                                               spx_uint32_t out_rate,
+                                               int quality,
+                                               int *err);
+
+/** Destroy a resampler state.
+ * @param st Resampler state
+ */
+void speex_resampler_destroy(SpeexResamplerState *st);
+
+/** Resample a float array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param channel_index Index of the channel to process for the multi-channel
+ * base (0 otherwise)
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the
+ * number of samples processed
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written
+ */
+int speex_resampler_process_float(SpeexResamplerState *st,
+                                   spx_uint32_t channel_index,
+                                   const float *in,
+                                   spx_uint32_t *in_len,
+                                   float *out,
+                                   spx_uint32_t *out_len);
+
+/** Resample an int array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param channel_index Index of the channel to process for the multi-channel
+ * base (0 otherwise)
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written
+ */
+int speex_resampler_process_int(SpeexResamplerState *st,
+                                 spx_uint32_t channel_index,
+                                 const spx_int16_t *in,
+                                 spx_uint32_t *in_len,
+                                 spx_int16_t *out,
+                                 spx_uint32_t *out_len);
+
+/** Resample an interleaved float array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed. This is all per-channel.
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written.
+ * This is all per-channel.
+ */
+int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
+                                               const float *in,
+                                               spx_uint32_t *in_len,
+                                               float *out,
+                                               spx_uint32_t *out_len);
+
+/** Resample an interleaved int array. The input and output buffers must *not* overlap.
+ * @param st Resampler state
+ * @param in Input buffer
+ * @param in_len Number of input samples in the input buffer. Returns the number
+ * of samples processed. This is all per-channel.
+ * @param out Output buffer
+ * @param out_len Size of the output buffer. Returns the number of samples written.
+ * This is all per-channel.
+ */
+int speex_resampler_process_interleaved_int(SpeexResamplerState *st,
+                                             const spx_int16_t *in,
+                                             spx_uint32_t *in_len,
+                                             spx_int16_t *out,
+                                             spx_uint32_t *out_len);
+
+/** Set (change) the input/output sampling rates (integer value).
+ * @param st Resampler state
+ * @param in_rate Input sampling rate (integer number of Hz).
+ * @param out_rate Output sampling rate (integer number of Hz).
+ */
+int speex_resampler_set_rate(SpeexResamplerState *st,
+                              spx_uint32_t in_rate,
+                              spx_uint32_t out_rate);
+
+/** Get the current input/output sampling rates (integer value).
+ * @param st Resampler state
+ * @param in_rate Input sampling rate (integer number of Hz) copied.
+ * @param out_rate Output sampling rate (integer number of Hz) copied.
+ */
+void speex_resampler_get_rate(SpeexResamplerState *st,
+                              spx_uint32_t *in_rate,
+                              spx_uint32_t *out_rate);
+
+/** Set (change) the input/output sampling rates and resampling ratio
+ * (fractional values in Hz supported).
+ * @param st Resampler state
+ * @param ratio_num Numerator of the sampling rate ratio
+ * @param ratio_den Denominator of the sampling rate ratio
+ * @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
+ * @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
+ */
+int speex_resampler_set_rate_frac(SpeexResamplerState *st,
+                                   spx_uint32_t ratio_num,
+                                   spx_uint32_t ratio_den,
+                                   spx_uint32_t in_rate,
+                                   spx_uint32_t out_rate);
+
+/** Get the current resampling ratio. This will be reduced to the least
+ * common denominator.
+ * @param st Resampler state
+ * @param ratio_num Numerator of the sampling rate ratio copied
+ * @param ratio_den Denominator of the sampling rate ratio copied
+ */
+void speex_resampler_get_ratio(SpeexResamplerState *st,
+                               spx_uint32_t *ratio_num,
+                               spx_uint32_t *ratio_den);
+
+/** Set (change) the conversion quality.
+ * @param st Resampler state
+ * @param quality Resampling quality between 0 and 10, where 0 has poor
+ * quality and 10 has very high quality.
+ */
+int speex_resampler_set_quality(SpeexResamplerState *st,
+                                 int quality);
+
+/** Get the conversion quality.
+ * @param st Resampler state
+ * @param quality Resampling quality between 0 and 10, where 0 has poor
+ * quality and 10 has very high quality.
+ */
+void speex_resampler_get_quality(SpeexResamplerState *st,
+                                 int *quality);
+
+/** Set (change) the input stride.
+ * @param st Resampler state
+ * @param stride Input stride
+ */
+void speex_resampler_set_input_stride(SpeexResamplerState *st,
+                                      spx_uint32_t stride);
+
+/** Get the input stride.
+ * @param st Resampler state
+ * @param stride Input stride copied
+ */
+void speex_resampler_get_input_stride(SpeexResamplerState *st,
+                                      spx_uint32_t *stride);
+
+/** Set (change) the output stride.
+ * @param st Resampler state
+ * @param stride Output stride
+ */
+void speex_resampler_set_output_stride(SpeexResamplerState *st,
+                                      spx_uint32_t stride);
+
+/** Get the output stride.
+ * @param st Resampler state copied
+ * @param stride Output stride
+ */
+void speex_resampler_get_output_stride(SpeexResamplerState *st,
+                                      spx_uint32_t *stride);
+
+/** Make sure that the first samples to go out of the resamplers don't have
+ * leading zeros. This is only useful before starting to use a newly created
+ * resampler. It is recommended to use that when resampling an audio file, as
+ * it will generate a file with the same length. For real-time processing,
+ * it is probably easier not to use this call (so that the output duration
+ * is the same for the first frame).
+ * @param st Resampler state
+ */
+int speex_resampler_skip_zeros(SpeexResamplerState *st);
+
+/** Reset a resampler so a new (unrelated) stream can be processed.
+ * @param st Resampler state
+ */
+int speex_resampler_reset_mem(SpeexResamplerState *st);
+
+/** Returns the English meaning for an error code
+ * @param err Error code
+ * @return English string
+ */
+const char *speex_resampler_strerror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/pulsecore/speexwrap.h b/src/pulsecore/speexwrap.h
new file mode 100644 (file)
index 0000000..617e4af
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef foopulsespeexwraphfoo
+#define foopulsespeexwraphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* We define a minimal version of speex_resampler.h however define one
+ * version for fixed and another one for float. Yes, somewhat ugly */
+
+#define spx_int16_t short
+#define spx_int32_t int
+#define spx_uint16_t unsigned short
+#define spx_uint32_t unsigned int
+
+typedef struct SpeexResamplerState_ SpeexResamplerState;
+
+SpeexResamplerState *paspfx_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate,  int quality, int *err);
+void paspfx_resampler_destroy(SpeexResamplerState *st);
+int paspfx_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in,  spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len);
+int paspfx_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate);
+int paspfx_resampler_reset_mem(SpeexResamplerState *st);
+
+SpeexResamplerState *paspfl_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate,  int quality, int *err);
+void paspfl_resampler_destroy(SpeexResamplerState *st);
+int paspfl_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in,  spx_uint32_t *in_len, float *out, spx_uint32_t *out_len);
+int paspfl_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate);
+int paspfl_resampler_reset_mem(SpeexResamplerState *st);
+
+#endif
diff --git a/src/pulsecore/start-child.c b/src/pulsecore/start-child.c
new file mode 100644 (file)
index 0000000..1661383
--- /dev/null
@@ -0,0 +1,160 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+
+#include "start-child.h"
+
+int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid) {
+    pid_t child;
+    int pipe_fds[2] = { -1, -1 };
+
+    if (pipe(pipe_fds) < 0) {
+        pa_log("pipe() failed: %s", pa_cstrerror(errno));
+        goto fail;
+    }
+
+    if ((child = fork()) == (pid_t) -1) {
+        pa_log("fork() failed: %s", pa_cstrerror(errno));
+        goto fail;
+
+    } else if (child != 0) {
+
+        /* Parent */
+        pa_assert_se(pa_close(pipe_fds[1]) == 0);
+
+        if (pid)
+            *pid = child;
+
+        return pipe_fds[0];
+    } else {
+#ifdef __linux__
+        DIR* d;
+#endif
+        int max_fd, i;
+
+        /* child */
+
+        pa_reset_priority();
+
+        pa_assert_se(pa_close(pipe_fds[0]) == 0);
+        pa_assert_se(dup2(pipe_fds[1], 1) == 1);
+
+        if (pipe_fds[1] != 1)
+            pa_assert_se(pa_close(pipe_fds[1]) == 0);
+
+        pa_close(0);
+        pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+
+        pa_close(2);
+        pa_assert_se(open("/dev/null", O_WRONLY) == 2);
+
+#ifdef __linux__
+
+        if ((d = opendir("/proc/self/fd/"))) {
+
+            struct dirent *de;
+
+            while ((de = readdir(d))) {
+                char *e = NULL;
+                int fd;
+
+                if (de->d_name[0] == '.')
+                    continue;
+
+                errno = 0;
+                fd = strtol(de->d_name, &e, 10);
+                pa_assert(errno == 0 && e && *e == 0);
+
+                if (fd >= 3 && dirfd(d) != fd)
+                    pa_close(fd);
+            }
+
+            closedir(d);
+        } else {
+
+#endif
+
+            max_fd = 1024;
+
+#ifdef HAVE_SYS_RESOURCE_H
+            {
+                struct rlimit r;
+                if (getrlimit(RLIMIT_NOFILE, &r) == 0)
+                    max_fd = r.rlim_max;
+            }
+#endif
+
+            for (i = 3; i < max_fd; i++)
+                pa_close(i);
+
+#ifdef __linux__
+        }
+#endif
+
+#ifdef PR_SET_PDEATHSIG
+        /* On Linux we can use PR_SET_PDEATHSIG to have the helper
+        process killed when the daemon dies abnormally. On non-Linux
+        machines the client will die as soon as it writes data to
+        stdout again (SIGPIPE) */
+
+        prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
+#endif
+
+#ifdef SIGPIPE
+        /* Make sure that SIGPIPE kills the child process */
+        signal(SIGPIPE, SIG_DFL);
+#endif
+
+#ifdef SIGTERM
+        /* Make sure that SIGTERM kills the child process */
+        signal(SIGTERM, SIG_DFL);
+#endif
+
+        execl(name, name, argv1, NULL);
+        _exit(1);
+    }
+
+fail:
+    pa_close_pipe(pipe_fds);
+
+    return -1;
+}
diff --git a/src/pulsecore/start-child.h b/src/pulsecore/start-child.h
new file mode 100644 (file)
index 0000000..0b5ff66
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef foopulsestartchildhfoo
+#define foopulsestartchildhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <sys/types.h>
+#include <unistd.h>
+
+int pa_start_child_for_read(const char *name, const char *argv1, pid_t *pid);
+
+#endif
similarity index 64%
rename from polyp/strbuf.c
rename to src/pulsecore/strbuf.c
index 1aa710ea118f2f82c43cd2e4e08790e025aaeb1f..b59b6f495b305e797af86c413c6cc629657824e9 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include <sys/types.h>
 #include <stdlib.h>
-#include <assert.h>
 #include <string.h>
 #include <stdarg.h>
 #include <stdio.h>
-#include <xmalloc.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
 
 #include "strbuf.h"
 
@@ -39,22 +40,26 @@ struct chunk {
     size_t length;
 };
 
-#define CHUNK_TO_TEXT(c) ((char*) (c) + sizeof(struct chunk))
+#define CHUNK_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(struct chunk)))
 
 struct pa_strbuf {
     size_t length;
     struct chunk *head, *tail;
 };
 
-struct pa_strbuf *pa_strbuf_new(void) {
-    struct pa_strbuf *sb = pa_xmalloc(sizeof(struct pa_strbuf));
+pa_strbuf *pa_strbuf_new(void) {
+    pa_strbuf *sb;
+
+    sb = pa_xnew(pa_strbuf, 1);
     sb->length = 0;
     sb->head = sb->tail = NULL;
+
     return sb;
 }
 
-void pa_strbuf_free(struct pa_strbuf *sb) {
-    assert(sb);
+void pa_strbuf_free(pa_strbuf *sb) {
+    pa_assert(sb);
+
     while (sb->head) {
         struct chunk *c = sb->head;
         sb->head = sb->head->next;
@@ -66,15 +71,16 @@ void pa_strbuf_free(struct pa_strbuf *sb) {
 
 /* Make a C string from the string buffer. The caller has to free
  * string with pa_xfree(). */
-char *pa_strbuf_tostring(struct pa_strbuf *sb) {
+char *pa_strbuf_tostring(pa_strbuf *sb) {
     char *t, *e;
     struct chunk *c;
-    assert(sb);
 
-    e = t = pa_xmalloc(sb->length+1);
+    pa_assert(sb);
+
+    e = t = pa_xnew(char, sb->length+1);
 
     for (c = sb->head; c; c = c->next) {
-        assert((size_t) (e-t) <= sb->length);
+        pa_assert((size_t) (e-t) <= sb->length);
         memcpy(e, CHUNK_TO_TEXT(c), c->length);
         e += c->length;
     }
@@ -82,35 +88,41 @@ char *pa_strbuf_tostring(struct pa_strbuf *sb) {
     /* Trailing NUL */
     *e = 0;
 
-    assert(e == t+sb->length);
-    
+    pa_assert(e == t+sb->length);
+
     return t;
 }
 
 /* Combination of pa_strbuf_free() and pa_strbuf_tostring() */
-char *pa_strbuf_tostring_free(struct pa_strbuf *sb) {
+char *pa_strbuf_tostring_free(pa_strbuf *sb) {
     char *t;
-    assert(sb);
+
+    pa_assert(sb);
     t = pa_strbuf_tostring(sb);
     pa_strbuf_free(sb);
+
     return t;
 }
 
 /* Append a string to the string buffer */
-void pa_strbuf_puts(struct pa_strbuf *sb, const char *t) {
-    assert(sb && t);
+void pa_strbuf_puts(pa_strbuf *sb, const char *t) {
+
+    pa_assert(sb);
+    pa_assert(t);
+
     pa_strbuf_putsn(sb, t, strlen(t));
 }
 
 /* Append a new chunk to the linked list */
-static void append(struct pa_strbuf *sb, struct chunk *c) {
-    assert(sb && c);
+static void append(pa_strbuf *sb, struct chunk *c) {
+    pa_assert(sb);
+    pa_assert(c);
 
     if (sb->tail) {
-        assert(sb->head);
+        pa_assert(sb->head);
         sb->tail->next = c;
     } else {
-        assert(!sb->head);
+        pa_assert(!sb->head);
         sb->head = c;
     }
 
@@ -120,14 +132,16 @@ static void append(struct pa_strbuf *sb, struct chunk *c) {
 }
 
 /* Append up to l bytes of a string to the string buffer */
-void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) {
+void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {
     struct chunk *c;
-    assert(sb && t);
-    
+
+    pa_assert(sb);
+    pa_assert(t);
+
     if (!l)
        return;
-   
-    c = pa_xmalloc(sizeof(struct chunk)+l);
+
+    c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l);
     c->length = l;
     memcpy(CHUNK_TO_TEXT(c), t, l);
 
@@ -136,22 +150,24 @@ void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) {
 
 /* Append a printf() style formatted string to the string buffer. */
 /* The following is based on an example from the GNU libc documentation */
-int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) {
+int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
     int size = 100;
     struct chunk *c = NULL;
 
-    assert(sb);
-    
+    pa_assert(sb);
+    pa_assert(format);
+
     for(;;) {
         va_list ap;
         int r;
 
-        c = pa_xrealloc(c, sizeof(struct chunk)+size);
+        c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size);
 
         va_start(ap, format);
         r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap);
+        CHUNK_TO_TEXT(c)[size-1] = 0;
         va_end(ap);
-        
+
         if (r > -1 && r < size) {
             c->length = r;
             append(sb, c);
@@ -159,7 +175,7 @@ int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) {
         }
 
         if (r > -1)    /* glibc 2.1 */
-            size = r+1; 
+            size = r+1;
         else           /* glibc 2.0 */
             size *= 2;
     }
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
new file mode 100644 (file)
index 0000000..24c876d
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef foostrbufhfoo
+#define foostrbufhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/gccmacro.h>
+
+typedef struct pa_strbuf pa_strbuf;
+
+pa_strbuf *pa_strbuf_new(void);
+void pa_strbuf_free(pa_strbuf *sb);
+char *pa_strbuf_tostring(pa_strbuf *sb);
+char *pa_strbuf_tostring_free(pa_strbuf *sb);
+
+int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...)  PA_GCC_PRINTF_ATTR(2,3);
+void pa_strbuf_puts(pa_strbuf *sb, const char *t);
+void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
+
+#endif
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
new file mode 100644 (file)
index 0000000..f587a2f
--- /dev/null
@@ -0,0 +1,161 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/strbuf.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+#include "strlist.h"
+
+struct pa_strlist {
+    pa_strlist *next;
+};
+
+#define ITEM_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(pa_strlist)))
+
+pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) {
+    pa_strlist *n;
+    size_t size;
+
+    pa_assert(s);
+    size = strlen(s);
+    n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);
+    memcpy(ITEM_TO_TEXT(n), s, size + 1);
+    n->next = l;
+
+    return  n;
+}
+
+char *pa_strlist_tostring(pa_strlist *l) {
+    int first = 1;
+    pa_strbuf *b;
+
+    b = pa_strbuf_new();
+    for (; l; l = l->next) {
+        if (!first)
+            pa_strbuf_puts(b, " ");
+        first = 0;
+        pa_strbuf_puts(b, ITEM_TO_TEXT(l));
+    }
+
+    return pa_strbuf_tostring_free(b);
+}
+
+pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s) {
+    pa_strlist *ret = l, *prev = NULL;
+
+    pa_assert(l);
+    pa_assert(s);
+
+    while (l) {
+        if (!strcmp(ITEM_TO_TEXT(l), s)) {
+            pa_strlist *n = l->next;
+
+            if (!prev) {
+                pa_assert(ret == l);
+                ret = n;
+            } else
+                prev->next = n;
+
+            pa_xfree(l);
+
+            l = n;
+
+        } else {
+            prev = l;
+            l = l->next;
+        }
+    }
+
+    return ret;
+}
+
+void pa_strlist_free(pa_strlist *l) {
+    while (l) {
+        pa_strlist *c = l;
+        l = l->next;
+        pa_xfree(c);
+    }
+}
+
+pa_strlist* pa_strlist_pop(pa_strlist *l, char **s) {
+    pa_strlist *r;
+
+    pa_assert(s);
+
+    if (!l) {
+        *s = NULL;
+        return NULL;
+    }
+
+    *s = pa_xstrdup(ITEM_TO_TEXT(l));
+    r = l->next;
+    pa_xfree(l);
+    return r;
+}
+
+pa_strlist* pa_strlist_parse(const char *s) {
+    pa_strlist *head = NULL, *p = NULL;
+    const char *state = NULL;
+    char *r;
+
+    while ((r = pa_split_spaces(s, &state))) {
+        pa_strlist *n;
+        size_t size = strlen(r);
+
+        n = pa_xmalloc(PA_ALIGN(sizeof(pa_strlist)) + size + 1);
+        n->next = NULL;
+        memcpy(ITEM_TO_TEXT(n), r, size+1);
+        pa_xfree(r);
+
+        if (p)
+            p->next = n;
+        else
+            head = n;
+
+        p = n;
+    }
+
+    return head;
+}
+
+pa_strlist *pa_strlist_reverse(pa_strlist *l) {
+    pa_strlist *r = NULL;
+
+    while (l) {
+        pa_strlist *n;
+
+        n = l->next;
+        l->next = r;
+        r = l;
+        l = n;
+    }
+
+    return r;
+}
similarity index 62%
rename from polyp/strlist.h
rename to src/pulsecore/strlist.h
index 533f5533532bc205dbaf78fa985429202c6803f0..1cb7537aa111f940c2e4c55743eeb1b6332a0570 100644 (file)
@@ -1,47 +1,50 @@
 #ifndef foostrlisthfoo
 #define foostrlisthfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-struct pa_strlist;
+typedef struct pa_strlist pa_strlist;
 
 /* Add the specified server string to the list, return the new linked list head */
-struct pa_strlist* pa_strlist_prepend(struct pa_strlist *l, const char *s);
+pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s);
 
 /* Remove the specified string from the list, return the new linked list head */
-struct pa_strlist* pa_strlist_remove(struct pa_strlist *l, const char *s);
+pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s);
 
 /* Make a whitespace separated string of all server stringes. Returned memory has to be freed with pa_xfree() */
-char *pa_strlist_tostring(struct pa_strlist *l);
+char *pa_strlist_tostring(pa_strlist *l);
 
 /* Free the entire list */
-void pa_strlist_free(struct pa_strlist *l);
+void pa_strlist_free(pa_strlist *l);
 
 /* Return the next entry in the list in *string and remove it from
  * the list. Returns the new list head. The memory *string points to
  * has to be freed with pa_xfree() */
-struct pa_strlist* pa_strlist_pop(struct pa_strlist *l, char **s);
+pa_strlist* pa_strlist_pop(pa_strlist *l, char **s);
 
 /* Parse a whitespace separated server list */
-struct pa_strlist* pa_strlist_parse(const char *s);
+pa_strlist* pa_strlist_parse(const char *s);
+
+/* Reverse string list */
+pa_strlist *pa_strlist_reverse(pa_strlist *l);
 
 #endif
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
new file mode 100644 (file)
index 0000000..b0ed59e
--- /dev/null
@@ -0,0 +1,753 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <stdarg.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/winsock.h>
+#include <pulsecore/macro.h>
+
+#include "tagstruct.h"
+
+#define MAX_TAG_SIZE (64*1024)
+
+struct pa_tagstruct {
+    uint8_t *data;
+    size_t length, allocated;
+    size_t rindex;
+
+    pa_bool_t dynamic;
+};
+
+pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
+    pa_tagstruct*t;
+
+    pa_assert(!data || (data && length));
+
+    t = pa_xnew(pa_tagstruct, 1);
+    t->data = (uint8_t*) data;
+    t->allocated = t->length = data ? length : 0;
+    t->rindex = 0;
+    t->dynamic = !data;
+
+    return t;
+}
+
+void pa_tagstruct_free(pa_tagstruct*t) {
+    pa_assert(t);
+
+    if (t->dynamic)
+        pa_xfree(t->data);
+    pa_xfree(t);
+}
+
+uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
+    uint8_t *p;
+
+    pa_assert(t);
+    pa_assert(t->dynamic);
+    pa_assert(l);
+
+    p = t->data;
+    *l = t->length;
+    pa_xfree(t);
+    return p;
+}
+
+static void extend(pa_tagstruct*t, size_t l) {
+    pa_assert(t);
+    pa_assert(t->dynamic);
+
+    if (t->length+l <= t->allocated)
+        return;
+
+    t->data = pa_xrealloc(t->data, t->allocated = t->length+l+100);
+}
+
+void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
+    size_t l;
+    pa_assert(t);
+
+    if (s) {
+        l = strlen(s)+2;
+        extend(t, l);
+        t->data[t->length] = PA_TAG_STRING;
+        strcpy((char*) (t->data+t->length+1), s);
+        t->length += l;
+    } else {
+        extend(t, 1);
+        t->data[t->length] = PA_TAG_STRING_NULL;
+        t->length += 1;
+    }
+}
+
+void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
+    pa_assert(t);
+
+    extend(t, 5);
+    t->data[t->length] = PA_TAG_U32;
+    i = htonl(i);
+    memcpy(t->data+t->length+1, &i, 4);
+    t->length += 5;
+}
+
+void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
+    pa_assert(t);
+
+    extend(t, 2);
+    t->data[t->length] = PA_TAG_U8;
+    *(t->data+t->length+1) = c;
+    t->length += 2;
+}
+
+void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
+    uint32_t rate;
+
+    pa_assert(t);
+    pa_assert(ss);
+
+    extend(t, 7);
+    t->data[t->length] = PA_TAG_SAMPLE_SPEC;
+    t->data[t->length+1] = (uint8_t) ss->format;
+    t->data[t->length+2] = ss->channels;
+    rate = htonl(ss->rate);
+    memcpy(t->data+t->length+3, &rate, 4);
+    t->length += 7;
+}
+
+void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
+    uint32_t tmp;
+
+    pa_assert(t);
+    pa_assert(p);
+
+    extend(t, 5+length);
+    t->data[t->length] = PA_TAG_ARBITRARY;
+    tmp = htonl(length);
+    memcpy(t->data+t->length+1, &tmp, 4);
+    if (length)
+        memcpy(t->data+t->length+5, p, length);
+    t->length += 5+length;
+}
+
+void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) {
+    pa_assert(t);
+
+    extend(t, 1);
+    t->data[t->length] = b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE;
+    t->length += 1;
+}
+
+void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
+    uint32_t tmp;
+    pa_assert(t);
+
+    extend(t, 9);
+    t->data[t->length] = PA_TAG_TIMEVAL;
+    tmp = htonl(tv->tv_sec);
+    memcpy(t->data+t->length+1, &tmp, 4);
+    tmp = htonl(tv->tv_usec);
+    memcpy(t->data+t->length+5, &tmp, 4);
+    t->length += 9;
+}
+
+void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
+    uint32_t tmp;
+
+    pa_assert(t);
+
+    extend(t, 9);
+    t->data[t->length] = PA_TAG_USEC;
+    tmp = htonl((uint32_t) (u >> 32));
+    memcpy(t->data+t->length+1, &tmp, 4);
+    tmp = htonl((uint32_t) u);
+    memcpy(t->data+t->length+5, &tmp, 4);
+    t->length += 9;
+}
+
+void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
+    uint32_t tmp;
+
+    pa_assert(t);
+
+    extend(t, 9);
+    t->data[t->length] = PA_TAG_U64;
+    tmp = htonl((uint32_t) (u >> 32));
+    memcpy(t->data+t->length+1, &tmp, 4);
+    tmp = htonl((uint32_t) u);
+    memcpy(t->data+t->length+5, &tmp, 4);
+    t->length += 9;
+}
+
+void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
+    uint32_t tmp;
+
+    pa_assert(t);
+
+    extend(t, 9);
+    t->data[t->length] = PA_TAG_S64;
+    tmp = htonl((uint32_t) ((uint64_t) u >> 32));
+    memcpy(t->data+t->length+1, &tmp, 4);
+    tmp = htonl((uint32_t) ((uint64_t) u));
+    memcpy(t->data+t->length+5, &tmp, 4);
+    t->length += 9;
+}
+
+void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {
+    unsigned i;
+
+    pa_assert(t);
+    extend(t, 2 + map->channels);
+
+    t->data[t->length++] = PA_TAG_CHANNEL_MAP;
+    t->data[t->length++] = map->channels;
+
+    for (i = 0; i < map->channels; i ++)
+        t->data[t->length++] = (uint8_t) map->map[i];
+}
+
+void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
+    unsigned i;
+    pa_volume_t vol;
+
+    pa_assert(t);
+    extend(t, 2 + cvolume->channels * sizeof(pa_volume_t));
+
+    t->data[t->length++] = PA_TAG_CVOLUME;
+    t->data[t->length++] = cvolume->channels;
+
+    for (i = 0; i < cvolume->channels; i ++) {
+        vol = htonl(cvolume->values[i]);
+        memcpy(t->data + t->length, &vol, sizeof(pa_volume_t));
+        t->length += sizeof(pa_volume_t);
+    }
+}
+
+void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
+    void *state = NULL;
+    pa_assert(t);
+    pa_assert(p);
+
+    extend(t, 1);
+
+    t->data[t->length++] = PA_TAG_PROPLIST;
+
+    for (;;) {
+        const char *k;
+        const void *d;
+        size_t l;
+
+        if (!(k = pa_proplist_iterate(p, &state)))
+            break;
+
+        pa_tagstruct_puts(t, k);
+        pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0);
+        pa_tagstruct_putu32(t, (uint32_t) l);
+        pa_tagstruct_put_arbitrary(t, d, l);
+    }
+
+    pa_tagstruct_puts(t, NULL);
+}
+
+int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
+    int error = 0;
+    size_t n;
+    char *c;
+
+    pa_assert(t);
+    pa_assert(s);
+
+    if (t->rindex+1 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] == PA_TAG_STRING_NULL) {
+        t->rindex++;
+        *s = NULL;
+        return 0;
+    }
+
+    if (t->rindex+2 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_STRING)
+        return -1;
+
+    error = 1;
+    for (n = 0, c = (char*) (t->data+t->rindex+1); t->rindex+1+n < t->length; n++, c++)
+        if (!*c) {
+            error = 0;
+            break;
+        }
+
+    if (error)
+        return -1;
+
+    *s = (char*) (t->data+t->rindex+1);
+
+    t->rindex += n+2;
+    return 0;
+}
+
+int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {
+    pa_assert(t);
+    pa_assert(i);
+
+    if (t->rindex+5 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_U32)
+        return -1;
+
+    memcpy(i, t->data+t->rindex+1, 4);
+    *i = ntohl(*i);
+    t->rindex += 5;
+    return 0;
+}
+
+int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {
+    pa_assert(t);
+    pa_assert(c);
+
+    if (t->rindex+2 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_U8)
+        return -1;
+
+    *c = t->data[t->rindex+1];
+    t->rindex +=2;
+    return 0;
+}
+
+int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
+    pa_assert(t);
+    pa_assert(ss);
+
+    if (t->rindex+7 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_SAMPLE_SPEC)
+        return -1;
+
+    ss->format = t->data[t->rindex+1];
+    ss->channels = t->data[t->rindex+2];
+    memcpy(&ss->rate, t->data+t->rindex+3, 4);
+    ss->rate = ntohl(ss->rate);
+
+    t->rindex += 7;
+    return 0;
+}
+
+int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
+    uint32_t len;
+
+    pa_assert(t);
+    pa_assert(p);
+
+    if (t->rindex+5+length > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_ARBITRARY)
+        return -1;
+
+    memcpy(&len, t->data+t->rindex+1, 4);
+    if (ntohl(len) != length)
+        return -1;
+
+    *p = t->data+t->rindex+5;
+    t->rindex += 5+length;
+    return 0;
+}
+
+int pa_tagstruct_eof(pa_tagstruct*t) {
+    pa_assert(t);
+
+    return t->rindex >= t->length;
+}
+
+const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
+    pa_assert(t);
+    pa_assert(t->dynamic);
+    pa_assert(l);
+
+    *l = t->length;
+    return t->data;
+}
+
+int pa_tagstruct_get_boolean(pa_tagstruct*t, pa_bool_t *b) {
+    pa_assert(t);
+    pa_assert(b);
+
+    if (t->rindex+1 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE)
+        *b = TRUE;
+    else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE)
+        *b = FALSE;
+    else
+        return -1;
+
+    t->rindex +=1;
+    return 0;
+}
+
+int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
+
+    pa_assert(t);
+    pa_assert(tv);
+
+    if (t->rindex+9 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_TIMEVAL)
+        return -1;
+
+    memcpy(&tv->tv_sec, t->data+t->rindex+1, 4);
+    tv->tv_sec = ntohl(tv->tv_sec);
+    memcpy(&tv->tv_usec, t->data+t->rindex+5, 4);
+    tv->tv_usec = ntohl(tv->tv_usec);
+    t->rindex += 9;
+    return 0;
+}
+
+int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {
+    uint32_t tmp;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t->rindex+9 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_USEC)
+        return -1;
+
+    memcpy(&tmp, t->data+t->rindex+1, 4);
+    *u = (pa_usec_t) ntohl(tmp) << 32;
+    memcpy(&tmp, t->data+t->rindex+5, 4);
+    *u |= (pa_usec_t) ntohl(tmp);
+    t->rindex +=9;
+    return 0;
+}
+
+int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
+    uint32_t tmp;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t->rindex+9 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_U64)
+        return -1;
+
+    memcpy(&tmp, t->data+t->rindex+1, 4);
+    *u = (uint64_t) ntohl(tmp) << 32;
+    memcpy(&tmp, t->data+t->rindex+5, 4);
+    *u |= (uint64_t) ntohl(tmp);
+    t->rindex +=9;
+    return 0;
+}
+
+int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
+    uint32_t tmp;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    if (t->rindex+9 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_S64)
+        return -1;
+
+    memcpy(&tmp, t->data+t->rindex+1, 4);
+    *u = (int64_t) ((uint64_t) ntohl(tmp) << 32);
+    memcpy(&tmp, t->data+t->rindex+5, 4);
+    *u |= (int64_t) ntohl(tmp);
+    t->rindex +=9;
+    return 0;
+}
+
+int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
+    unsigned i;
+
+    pa_assert(t);
+    pa_assert(map);
+
+    if (t->rindex+2 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_CHANNEL_MAP)
+        return -1;
+
+    if ((map->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
+        return -1;
+
+    if (t->rindex+2+map->channels > t->length)
+        return -1;
+
+    for (i = 0; i < map->channels; i ++)
+        map->map[i] = (int8_t) t->data[t->rindex + 2 + i];
+
+    t->rindex += 2 + map->channels;
+    return 0;
+}
+
+int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
+    unsigned i;
+    pa_volume_t vol;
+
+    pa_assert(t);
+    pa_assert(cvolume);
+
+    if (t->rindex+2 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_CVOLUME)
+        return -1;
+
+    if ((cvolume->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
+        return -1;
+
+    if (t->rindex+2+cvolume->channels*sizeof(pa_volume_t) > t->length)
+        return -1;
+
+    for (i = 0; i < cvolume->channels; i ++) {
+        memcpy(&vol, t->data + t->rindex + 2 + i * sizeof(pa_volume_t), sizeof(pa_volume_t));
+        cvolume->values[i] = (pa_volume_t) ntohl(vol);
+    }
+
+    t->rindex += 2 + cvolume->channels * sizeof(pa_volume_t);
+    return 0;
+}
+
+int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) {
+    size_t saved_rindex;
+
+    pa_assert(t);
+    pa_assert(p);
+
+    if (t->rindex+1 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_PROPLIST)
+        return -1;
+
+    saved_rindex = t->rindex;
+    t->rindex++;
+
+    for (;;) {
+        const char *k;
+        const void *d;
+        uint32_t length;
+
+        if (pa_tagstruct_gets(t, &k) < 0)
+            goto fail;
+
+        if (!k)
+            break;
+
+        if (pa_tagstruct_getu32(t, &length) < 0)
+            goto fail;
+
+        if (length > MAX_TAG_SIZE)
+            goto fail;
+
+        if (pa_tagstruct_get_arbitrary(t, &d, length) < 0)
+            goto fail;
+
+        if (pa_proplist_set(p, k, d, length) < 0)
+            goto fail;
+    }
+
+    return 0;
+
+fail:
+    t->rindex = saved_rindex;
+    return -1;
+}
+
+void pa_tagstruct_put(pa_tagstruct *t, ...) {
+    va_list va;
+    pa_assert(t);
+
+    va_start(va, t);
+
+    for (;;) {
+        int tag = va_arg(va, int);
+
+        if (tag == PA_TAG_INVALID)
+            break;
+
+        switch (tag) {
+            case PA_TAG_STRING:
+            case PA_TAG_STRING_NULL:
+                pa_tagstruct_puts(t, va_arg(va, char*));
+                break;
+
+            case PA_TAG_U32:
+                pa_tagstruct_putu32(t, va_arg(va, uint32_t));
+                break;
+
+            case PA_TAG_U8:
+                pa_tagstruct_putu8(t, (uint8_t) va_arg(va, int));
+                break;
+
+            case PA_TAG_U64:
+                pa_tagstruct_putu64(t, va_arg(va, uint64_t));
+                break;
+
+            case PA_TAG_SAMPLE_SPEC:
+                pa_tagstruct_put_sample_spec(t, va_arg(va, pa_sample_spec*));
+                break;
+
+            case PA_TAG_ARBITRARY: {
+                void *p = va_arg(va, void*);
+                size_t size = va_arg(va, size_t);
+                pa_tagstruct_put_arbitrary(t, p, size);
+                break;
+            }
+
+            case PA_TAG_BOOLEAN_TRUE:
+            case PA_TAG_BOOLEAN_FALSE:
+                pa_tagstruct_put_boolean(t, va_arg(va, int));
+                break;
+
+            case PA_TAG_TIMEVAL:
+                pa_tagstruct_put_timeval(t, va_arg(va, struct timeval*));
+                break;
+
+            case PA_TAG_USEC:
+                pa_tagstruct_put_usec(t, va_arg(va, pa_usec_t));
+                break;
+
+            case PA_TAG_CHANNEL_MAP:
+                pa_tagstruct_put_channel_map(t, va_arg(va, pa_channel_map *));
+                break;
+
+            case PA_TAG_CVOLUME:
+                pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *));
+                break;
+
+            case PA_TAG_PROPLIST:
+                pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *));
+                break;
+
+            default:
+                pa_assert_not_reached();
+        }
+    }
+
+    va_end(va);
+}
+
+int pa_tagstruct_get(pa_tagstruct *t, ...) {
+    va_list va;
+    int ret = 0;
+
+    pa_assert(t);
+
+    va_start(va, t);
+    while (ret == 0) {
+        int tag = va_arg(va, int);
+
+        if (tag == PA_TAG_INVALID)
+            break;
+
+        switch (tag) {
+            case PA_TAG_STRING:
+            case PA_TAG_STRING_NULL:
+                ret = pa_tagstruct_gets(t, va_arg(va, const char**));
+                break;
+
+            case PA_TAG_U32:
+                ret = pa_tagstruct_getu32(t, va_arg(va, uint32_t*));
+                break;
+
+            case PA_TAG_U8:
+                ret = pa_tagstruct_getu8(t, va_arg(va, uint8_t*));
+                break;
+
+            case PA_TAG_U64:
+                ret = pa_tagstruct_getu64(t, va_arg(va, uint64_t*));
+                break;
+
+            case PA_TAG_SAMPLE_SPEC:
+                ret = pa_tagstruct_get_sample_spec(t, va_arg(va, pa_sample_spec*));
+                break;
+
+            case PA_TAG_ARBITRARY: {
+                const void **p = va_arg(va, const void**);
+                size_t size = va_arg(va, size_t);
+                ret = pa_tagstruct_get_arbitrary(t, p, size);
+                break;
+            }
+
+            case PA_TAG_BOOLEAN_TRUE:
+            case PA_TAG_BOOLEAN_FALSE:
+                ret = pa_tagstruct_get_boolean(t, va_arg(va, pa_bool_t*));
+                break;
+
+            case PA_TAG_TIMEVAL:
+                ret = pa_tagstruct_get_timeval(t, va_arg(va, struct timeval*));
+                break;
+
+            case PA_TAG_USEC:
+                ret = pa_tagstruct_get_usec(t, va_arg(va, pa_usec_t*));
+                break;
+
+            case PA_TAG_CHANNEL_MAP:
+                ret = pa_tagstruct_get_channel_map(t, va_arg(va, pa_channel_map *));
+                break;
+
+            case PA_TAG_CVOLUME:
+                ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
+                break;
+
+            case PA_TAG_PROPLIST:
+                ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *));
+                break;
+
+            default:
+                pa_assert_not_reached();
+        }
+
+    }
+
+    va_end(va);
+    return ret;
+}
diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
new file mode 100644 (file)
index 0000000..e7d0705
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef footagstructhfoo
+#define footagstructhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulse/volume.h>
+#include <pulse/proplist.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
+
+typedef struct pa_tagstruct pa_tagstruct;
+
+enum {
+    PA_TAG_INVALID = 0,
+    PA_TAG_STRING = 't',
+    PA_TAG_STRING_NULL = 'N',
+    PA_TAG_U32 = 'L',
+    PA_TAG_U8 = 'B',
+    PA_TAG_U64 = 'R',
+    PA_TAG_S64 = 'r',
+    PA_TAG_SAMPLE_SPEC = 'a',
+    PA_TAG_ARBITRARY = 'x',
+    PA_TAG_BOOLEAN_TRUE = '1',
+    PA_TAG_BOOLEAN_FALSE = '0',
+    PA_TAG_BOOLEAN = PA_TAG_BOOLEAN_TRUE,
+    PA_TAG_TIMEVAL = 'T',
+    PA_TAG_USEC = 'U'  /* 64bit unsigned */,
+    PA_TAG_CHANNEL_MAP = 'm',
+    PA_TAG_CVOLUME = 'v',
+    PA_TAG_PROPLIST = 'P'
+};
+
+pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length);
+void pa_tagstruct_free(pa_tagstruct*t);
+uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l);
+
+int pa_tagstruct_eof(pa_tagstruct*t);
+const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l);
+
+void pa_tagstruct_put(pa_tagstruct *t, ...);
+
+void pa_tagstruct_puts(pa_tagstruct*t, const char *s);
+void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c);
+void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i);
+void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t i);
+void pa_tagstruct_puts64(pa_tagstruct*t, int64_t i);
+void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss);
+void pa_tagstruct_put_arbitrary(pa_tagstruct*t, const void *p, size_t length);
+void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b);
+void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv);
+void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u);
+void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map);
+void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume);
+void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p);
+
+int pa_tagstruct_get(pa_tagstruct *t, ...);
+
+int pa_tagstruct_gets(pa_tagstruct*t, const char **s);
+int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c);
+int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i);
+int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *i);
+int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *i);
+int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss);
+int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length);
+int pa_tagstruct_get_boolean(pa_tagstruct *t, pa_bool_t *b);
+int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv);
+int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u);
+int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map);
+int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v);
+int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p);
+
+
+#endif
diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c
new file mode 100644 (file)
index 0000000..34f92a7
--- /dev/null
@@ -0,0 +1,127 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/atomic.h>
+#include <pulsecore/once.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/semaphore.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/flist.h>
+
+#include "thread-mq.h"
+
+PA_STATIC_TLS_DECLARE_NO_FREE(thread_mq);
+
+static void asyncmsgq_read_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
+    pa_thread_mq *q = userdata;
+    pa_asyncmsgq *aq;
+
+    pa_assert(pa_asyncmsgq_read_fd(q->outq) == fd);
+    pa_assert(events == PA_IO_EVENT_INPUT);
+
+    pa_asyncmsgq_ref(aq = q->outq);
+    pa_asyncmsgq_write_after_poll(aq);
+
+    for (;;) {
+        pa_msgobject *object;
+        int code;
+        void *data;
+        int64_t offset;
+        pa_memchunk chunk;
+
+        /* Check whether there is a message for us to process */
+        while (pa_asyncmsgq_get(aq, &object, &code, &data, &offset, &chunk, 0) == 0) {
+            int ret;
+
+            ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
+            pa_asyncmsgq_done(aq, ret);
+        }
+
+        if (pa_asyncmsgq_read_before_poll(aq) == 0)
+            break;
+    }
+
+    pa_asyncmsgq_unref(aq);
+}
+
+static void asyncmsgq_write_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
+    pa_thread_mq *q = userdata;
+
+    pa_assert(pa_asyncmsgq_write_fd(q->inq) == fd);
+    pa_assert(events == PA_IO_EVENT_INPUT);
+
+    pa_asyncmsgq_write_after_poll(q->inq);
+    pa_asyncmsgq_write_before_poll(q->inq);
+}
+
+void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll) {
+    pa_assert(q);
+    pa_assert(mainloop);
+
+    q->mainloop = mainloop;
+    pa_assert_se(q->inq = pa_asyncmsgq_new(0));
+    pa_assert_se(q->outq = pa_asyncmsgq_new(0));
+
+    pa_assert_se(pa_asyncmsgq_read_before_poll(q->outq) == 0);
+    pa_assert_se(q->read_event = mainloop->io_new(mainloop, pa_asyncmsgq_read_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_read_cb, q));
+
+    pa_asyncmsgq_write_before_poll(q->inq);
+    pa_assert_se(q->write_event = mainloop->io_new(mainloop, pa_asyncmsgq_write_fd(q->inq), PA_IO_EVENT_INPUT, asyncmsgq_write_cb, q));
+
+    pa_rtpoll_item_new_asyncmsgq_read(rtpoll, PA_RTPOLL_EARLY, q->inq);
+    pa_rtpoll_item_new_asyncmsgq_write(rtpoll, PA_RTPOLL_LATE, q->outq);
+}
+
+void pa_thread_mq_done(pa_thread_mq *q) {
+    pa_assert(q);
+
+    q->mainloop->io_free(q->read_event);
+    q->mainloop->io_free(q->write_event);
+    q->read_event = q->write_event = NULL;
+
+    pa_asyncmsgq_unref(q->inq);
+    pa_asyncmsgq_unref(q->outq);
+    q->inq = q->outq = NULL;
+
+    q->mainloop = NULL;
+}
+
+void pa_thread_mq_install(pa_thread_mq *q) {
+    pa_assert(q);
+
+    pa_assert(!(PA_STATIC_TLS_GET(thread_mq)));
+    PA_STATIC_TLS_SET(thread_mq, q);
+}
+
+pa_thread_mq *pa_thread_mq_get(void) {
+    return PA_STATIC_TLS_GET(thread_mq);
+}
diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h
new file mode 100644 (file)
index 0000000..3b5e0e7
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef foopulsethreadmqhfoo
+#define foopulsethreadmqhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/rtpoll.h>
+
+/* Two way communication between a thread and a mainloop. Before the
+ * thread is started a pa_pthread_mq should be initialized and than
+ * attached to the thread using pa_thread_mq_install(). */
+
+typedef struct pa_thread_mq {
+    pa_mainloop_api *mainloop;
+    pa_asyncmsgq *inq, *outq;
+    pa_io_event *read_event, *write_event;
+} pa_thread_mq;
+
+void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll);
+void pa_thread_mq_done(pa_thread_mq *q);
+
+/* Install the specified pa_thread_mq object for the current thread */
+void pa_thread_mq_install(pa_thread_mq *q);
+
+/* Return the pa_thread_mq object that is set for the current thread */
+pa_thread_mq *pa_thread_mq_get(void);
+
+#endif
diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c
new file mode 100644 (file)
index 0000000..20ed16d
--- /dev/null
@@ -0,0 +1,195 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pthread.h>
+#include <sched.h>
+#include <errno.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/once.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
+
+#include "thread.h"
+
+struct pa_thread {
+    pthread_t id;
+    pa_thread_func_t thread_func;
+    void *userdata;
+    pa_atomic_t running;
+};
+
+struct pa_tls {
+    pthread_key_t key;
+};
+
+static void thread_free_cb(void *p) {
+    pa_thread *t = p;
+
+    pa_assert(t);
+
+    if (!t->thread_func)
+        /* This is a foreign thread, we need to free the struct */
+        pa_xfree(t);
+}
+
+PA_STATIC_TLS_DECLARE(current_thread, thread_free_cb);
+
+static void* internal_thread_func(void *userdata) {
+    pa_thread *t = userdata;
+    pa_assert(t);
+
+    t->id = pthread_self();
+
+    PA_STATIC_TLS_SET(current_thread, t);
+
+    pa_atomic_inc(&t->running);
+    t->thread_func(t->userdata);
+    pa_atomic_sub(&t->running, 2);
+
+    return NULL;
+}
+
+pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {
+    pa_thread *t;
+
+    pa_assert(thread_func);
+
+    t = pa_xnew(pa_thread, 1);
+    t->thread_func = thread_func;
+    t->userdata = userdata;
+    pa_atomic_store(&t->running, 0);
+
+    if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) {
+        pa_xfree(t);
+        return NULL;
+    }
+
+    pa_atomic_inc(&t->running);
+
+    return t;
+}
+
+int pa_thread_is_running(pa_thread *t) {
+    pa_assert(t);
+
+    /* Unfortunately there is no way to tell whether a "foreign"
+     * thread is still running. See
+     * http://udrepper.livejournal.com/16844.html for more
+     * information */
+    pa_assert(t->thread_func);
+
+    return pa_atomic_load(&t->running) > 0;
+}
+
+void pa_thread_free(pa_thread *t) {
+    pa_assert(t);
+
+    pa_thread_join(t);
+    pa_xfree(t);
+}
+
+int pa_thread_join(pa_thread *t) {
+    pa_assert(t);
+
+    return pthread_join(t->id, NULL);
+}
+
+pa_thread* pa_thread_self(void) {
+    pa_thread *t;
+
+    if ((t = PA_STATIC_TLS_GET(current_thread)))
+        return t;
+
+    /* This is a foreign thread, let's create a pthread structure to
+     * make sure that we can always return a sensible pointer */
+
+    t = pa_xnew(pa_thread, 1);
+    t->id = pthread_self();
+    t->thread_func = NULL;
+    t->userdata = NULL;
+    pa_atomic_store(&t->running, 2);
+
+    PA_STATIC_TLS_SET(current_thread, t);
+
+    return t;
+}
+
+void* pa_thread_get_data(pa_thread *t) {
+    pa_assert(t);
+
+    return t->userdata;
+}
+
+void pa_thread_set_data(pa_thread *t, void *userdata) {
+    pa_assert(t);
+
+    t->userdata = userdata;
+}
+
+void pa_thread_yield(void) {
+#ifdef HAVE_PTHREAD_YIELD
+    pthread_yield();
+#else
+    pa_assert_se(sched_yield() == 0);
+#endif
+}
+
+pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
+    pa_tls *t;
+
+    t = pa_xnew(pa_tls, 1);
+
+    if (pthread_key_create(&t->key, free_cb) < 0) {
+        pa_xfree(t);
+        return NULL;
+    }
+
+    return t;
+}
+
+void pa_tls_free(pa_tls *t) {
+    pa_assert(t);
+
+    pa_assert_se(pthread_key_delete(t->key) == 0);
+    pa_xfree(t);
+}
+
+void *pa_tls_get(pa_tls *t) {
+    pa_assert(t);
+
+    return pthread_getspecific(t->key);
+}
+
+void *pa_tls_set(pa_tls *t, void *userdata) {
+    void *r;
+
+    r = pthread_getspecific(t->key);
+    pa_assert_se(pthread_setspecific(t->key, userdata) == 0);
+    return r;
+}
+
diff --git a/src/pulsecore/thread-win32.c b/src/pulsecore/thread-win32.c
new file mode 100644 (file)
index 0000000..c40d334
--- /dev/null
@@ -0,0 +1,214 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <windows.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/log.h>
+#include <pulsecore/once.h>
+
+#include "thread.h"
+
+struct pa_thread {
+    HANDLE thread;
+    pa_thread_func_t thread_func;
+    void *userdata;
+};
+
+struct pa_tls {
+    DWORD index;
+    pa_free_cb_t free_func;
+};
+
+struct pa_tls_monitor {
+    HANDLE thread;
+    pa_free_cb_t free_func;
+    void *data;
+};
+
+static pa_tls *thread_tls;
+static pa_once thread_tls_once = PA_ONCE_INIT;
+static pa_tls *monitor_tls;
+
+static void thread_tls_once_func(void) {
+    thread_tls = pa_tls_new(NULL);
+    assert(thread_tls);
+}
+
+static DWORD WINAPI internal_thread_func(LPVOID param) {
+    pa_thread *t = param;
+    assert(t);
+
+    pa_run_once(&thread_tls_once, thread_tls_once_func);
+    pa_tls_set(thread_tls, t);
+
+    t->thread_func(t->userdata);
+
+    return 0;
+}
+
+pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata) {
+    pa_thread *t;
+
+    assert(thread_func);
+
+    t = pa_xnew(pa_thread, 1);
+    t->thread_func = thread_func;
+    t->userdata = userdata;
+
+    t->thread = CreateThread(NULL, 0, internal_thread_func, t, 0, NULL);
+
+    if (!t->thread) {
+        pa_xfree(t);
+        return NULL;
+    }
+
+    return t;
+}
+
+int pa_thread_is_running(pa_thread *t) {
+    DWORD code;
+
+    assert(t);
+
+    if (!GetExitCodeThread(t->thread, &code))
+        return 0;
+
+    return code == STILL_ACTIVE;
+}
+
+void pa_thread_free(pa_thread *t) {
+    assert(t);
+
+    pa_thread_join(t);
+    CloseHandle(t->thread);
+    pa_xfree(t);
+}
+
+int pa_thread_join(pa_thread *t) {
+    assert(t);
+
+    if (WaitForSingleObject(t->thread, INFINITE) == WAIT_FAILED)
+        return -1;
+
+    return 0;
+}
+
+pa_thread* pa_thread_self(void) {
+    pa_run_once(&thread_tls_once, thread_tls_once_func);
+    return pa_tls_get(thread_tls);
+}
+
+void pa_thread_yield(void) {
+    Sleep(0);
+}
+
+static DWORD WINAPI monitor_thread_func(LPVOID param) {
+    struct pa_tls_monitor *m = param;
+    assert(m);
+
+    WaitForSingleObject(m->thread, INFINITE);
+
+    CloseHandle(m->thread);
+
+    m->free_func(m->data);
+
+    pa_xfree(m);
+
+    return 0;
+}
+
+pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
+    pa_tls *t;
+
+    t = pa_xnew(pa_tls, 1);
+    t->index = TlsAlloc();
+    t->free_func = free_cb;
+
+    if (t->index == TLS_OUT_OF_INDEXES) {
+        pa_xfree(t);
+        return NULL;
+    }
+
+    return t;
+}
+
+void pa_tls_free(pa_tls *t) {
+    assert(t);
+
+    TlsFree(t->index);
+    pa_xfree(t);
+}
+
+void *pa_tls_get(pa_tls *t) {
+    assert(t);
+
+    return TlsGetValue(t->index);
+}
+
+void *pa_tls_set(pa_tls *t, void *userdata) {
+    void *r;
+
+    assert(t);
+
+    r = TlsGetValue(t->index);
+
+    TlsSetValue(t->index, userdata);
+
+    if (t->free_func) {
+        struct pa_tls_monitor *m;
+
+        PA_ONCE_BEGIN {
+            monitor_tls = pa_tls_new(NULL);
+            assert(monitor_tls);
+            pa_tls_set(monitor_tls, NULL);
+        } PA_ONCE_END;
+
+        m = pa_tls_get(monitor_tls);
+        if (!m) {
+            HANDLE thread;
+
+            m = pa_xnew(struct pa_tls_monitor, 1);
+
+            DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+                GetCurrentProcess(), &m->thread, 0, FALSE,
+                DUPLICATE_SAME_ACCESS);
+
+            m->free_func = t->free_func;
+
+            pa_tls_set(monitor_tls, m);
+
+            thread = CreateThread(NULL, 0, monitor_thread_func, m, 0, NULL);
+            assert(thread);
+            CloseHandle(thread);
+        }
+
+        m->data = userdata;
+    }
+
+    return r;
+}
diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h
new file mode 100644 (file)
index 0000000..f3aca13
--- /dev/null
@@ -0,0 +1,110 @@
+#ifndef foopulsethreadhfoo
+#define foopulsethreadhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/def.h>
+#include <pulsecore/once.h>
+
+#ifndef PACKAGE
+#error "Please include config.h before including this file!"
+#endif
+
+typedef struct pa_thread pa_thread;
+
+typedef void (*pa_thread_func_t) (void *userdata);
+
+pa_thread* pa_thread_new(pa_thread_func_t thread_func, void *userdata);
+void pa_thread_free(pa_thread *t);
+int pa_thread_join(pa_thread *t);
+int pa_thread_is_running(pa_thread *t);
+pa_thread *pa_thread_self(void);
+void pa_thread_yield(void);
+
+void* pa_thread_get_data(pa_thread *t);
+void pa_thread_set_data(pa_thread *t, void *userdata);
+
+typedef struct pa_tls pa_tls;
+
+pa_tls* pa_tls_new(pa_free_cb_t free_cb);
+void pa_tls_free(pa_tls *t);
+void * pa_tls_get(pa_tls *t);
+void *pa_tls_set(pa_tls *t, void *userdata);
+
+#define PA_STATIC_TLS_DECLARE(name, free_cb)                            \
+    static struct {                                                     \
+        pa_once once;                                                   \
+        pa_tls *tls;                                                    \
+    } name##_tls = {                                                    \
+        .once = PA_ONCE_INIT,                                           \
+        .tls = NULL                                                     \
+    };                                                                  \
+    static void name##_tls_init(void) {                                 \
+        name##_tls.tls = pa_tls_new(free_cb);                           \
+    }                                                                   \
+    static inline pa_tls* name##_tls_obj(void) {                        \
+        pa_run_once(&name##_tls.once, name##_tls_init);                 \
+        return name##_tls.tls;                                          \
+    }                                                                   \
+    static void name##_tls_destructor(void) PA_GCC_DESTRUCTOR;          \
+    static void name##_tls_destructor(void) {                           \
+        static void (*_free_cb)(void*) = free_cb;                       \
+        if (!name##_tls.tls)                                            \
+            return;                                                     \
+        if (_free_cb) {                                                 \
+            void *p;                                                    \
+            if ((p = pa_tls_get(name##_tls.tls)))                       \
+                _free_cb(p);                                            \
+        }                                                               \
+        pa_tls_free(name##_tls.tls);                                    \
+    }                                                                   \
+    static inline void* name##_tls_get(void) {                          \
+        return pa_tls_get(name##_tls_obj());                            \
+    }                                                                   \
+    static inline void* name##_tls_set(void *p) {                       \
+        return pa_tls_set(name##_tls_obj(), p);                         \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+
+#ifdef HAVE_TLS_BUILTIN
+/* An optimized version of the above that requires no dynamic
+ * allocation if the compiler supports __thread */
+#define PA_STATIC_TLS_DECLARE_NO_FREE(name)                             \
+    static __thread void *name##_tls = NULL;                            \
+    static inline void* name##_tls_get(void) {                          \
+        return name##_tls;                                              \
+    }                                                                   \
+    static inline void* name##_tls_set(void *p) {                       \
+        void *r = name##_tls;                                           \
+        name##_tls = p;                                                 \
+        return r;                                                       \
+    }                                                                   \
+    struct __stupid_useless_struct_to_allow_trailing_semicolon
+#else
+#define PA_STATIC_TLS_DECLARE_NO_FREE(name) PA_STATIC_TLS_DECLARE(name, NULL)
+#endif
+
+#define PA_STATIC_TLS_GET(name) (name##_tls_get())
+#define PA_STATIC_TLS_SET(name, p) (name##_tls_set(p))
+
+#endif
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
new file mode 100644 (file)
index 0000000..fe5a4f1
--- /dev/null
@@ -0,0 +1,459 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <pulse/sample.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+
+#include "time-smoother.h"
+
+#define HISTORY_MAX 64
+
+/*
+ * Implementation of a time smoothing algorithm to synchronize remote
+ * clocks to a local one. Evens out noise, adjusts to clock skew and
+ * allows cheap estimations of the remote time while clock updates may
+ * be seldom and recieved in non-equidistant intervals.
+ *
+ * Basically, we estimate the gradient of received clock samples in a
+ * certain history window (of size 'history_time') with linear
+ * regression. With that info we estimate the remote time in
+ * 'adjust_time' ahead and smoothen our current estimation function
+ * towards that point with a 3rd order polynomial interpolation with
+ * fitting derivatives. (more or less a b-spline)
+ *
+ * The larger 'history_time' is chosen the better we will surpress
+ * noise -- but we'll adjust to clock skew slower..
+ *
+ * The larger 'adjust_time' is chosen the smoother our estimation
+ * function will be -- but we'll adjust to clock skew slower, too.
+ *
+ * If 'monotonic' is TRUE the resulting estimation function is
+ * guaranteed to be monotonic.
+ */
+
+struct pa_smoother {
+    pa_usec_t adjust_time, history_time;
+
+    pa_usec_t time_offset;
+
+    pa_usec_t px, py;     /* Point p, where we want to reach stability */
+    double dp;            /* Gradient we want at point p */
+
+    pa_usec_t ex, ey;     /* Point e, which we estimated before and need to smooth to */
+    double de;            /* Gradient we estimated for point e */
+    pa_usec_t ry;         /* The original y value for ex */
+
+                          /* History of last measurements */
+    pa_usec_t history_x[HISTORY_MAX], history_y[HISTORY_MAX];
+    unsigned history_idx, n_history;
+
+    /* To even out for monotonicity */
+    pa_usec_t last_y, last_x;
+
+    /* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */
+    double a, b, c;
+    pa_bool_t abc_valid;
+
+    pa_bool_t monotonic:1;
+    pa_bool_t paused:1;
+
+    pa_usec_t pause_time;
+
+    unsigned min_history;
+};
+
+pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic, unsigned min_history) {
+    pa_smoother *s;
+
+    pa_assert(adjust_time > 0);
+    pa_assert(history_time > 0);
+    pa_assert(min_history >= 2);
+    pa_assert(min_history <= HISTORY_MAX);
+
+    s = pa_xnew(pa_smoother, 1);
+    s->adjust_time = adjust_time;
+    s->history_time = history_time;
+    s->time_offset = 0;
+    s->monotonic = monotonic;
+
+    s->px = s->py = 0;
+    s->dp = 1;
+
+    s->ex = s->ey = s->ry = 0;
+    s->de = 1;
+
+    s->history_idx = 0;
+    s->n_history = 0;
+
+    s->last_y = s->last_x = 0;
+
+    s->abc_valid = FALSE;
+
+    s->paused = FALSE;
+
+    s->min_history = min_history;
+
+    return s;
+}
+
+void pa_smoother_free(pa_smoother* s) {
+    pa_assert(s);
+
+    pa_xfree(s);
+}
+
+#define REDUCE(x)                               \
+    do {                                        \
+        x = (x) % HISTORY_MAX;                  \
+    } while(FALSE)
+
+#define REDUCE_INC(x)                           \
+    do {                                        \
+        x = ((x)+1) % HISTORY_MAX;              \
+    } while(FALSE)
+
+
+static void drop_old(pa_smoother *s, pa_usec_t x) {
+
+    /* Drop items from history which are too old, but make sure to
+     * always keep min_history in the history */
+
+    while (s->n_history > s->min_history) {
+
+        if (s->history_x[s->history_idx] + s->history_time >= x)
+            /* This item is still valid, and thus all following ones
+             * are too, so let's quit this loop */
+            break;
+
+        /* Item is too old, let's drop it */
+        REDUCE_INC(s->history_idx);
+
+        s->n_history --;
+    }
+}
+
+static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
+    unsigned j, i;
+    pa_assert(s);
+
+    /* First try to update an existing history entry */
+    i = s->history_idx;
+    for (j = s->n_history; j > 0; j--) {
+
+        if (s->history_x[i] == x) {
+            s->history_y[i] = y;
+            return;
+        }
+
+        REDUCE_INC(i);
+    }
+
+    /* Drop old entries */
+    drop_old(s, x);
+
+    /* Calculate position for new entry */
+    j = s->history_idx + s->n_history;
+    REDUCE(j);
+
+    /* Fill in entry */
+    s->history_x[j] = x;
+    s->history_y[j] = y;
+
+    /* Adjust counter */
+    s->n_history ++;
+
+    /* And make sure we don't store more entries than fit in */
+    if (s->n_history > HISTORY_MAX) {
+        s->history_idx += s->n_history - HISTORY_MAX;
+        REDUCE(s->history_idx);
+        s->n_history = HISTORY_MAX;
+    }
+}
+
+static double avg_gradient(pa_smoother *s, pa_usec_t x) {
+    unsigned i, j, c = 0;
+    int64_t ax = 0, ay = 0, k, t;
+    double r;
+
+    /* Too few measurements, assume gradient of 1 */
+    if (s->n_history < s->min_history)
+        return 1;
+
+    /* First, calculate average of all measurements */
+    i = s->history_idx;
+    for (j = s->n_history; j > 0; j--) {
+
+        ax += s->history_x[i];
+        ay += s->history_y[i];
+        c++;
+
+        REDUCE_INC(i);
+    }
+
+    pa_assert(c >= s->min_history);
+    ax /= c;
+    ay /= c;
+
+    /* Now, do linear regression */
+    k = t = 0;
+
+    i = s->history_idx;
+    for (j = s->n_history; j > 0; j--) {
+        int64_t dx, dy;
+
+        dx = (int64_t) s->history_x[i] - ax;
+        dy = (int64_t) s->history_y[i] - ay;
+
+        k += dx*dy;
+        t += dx*dx;
+
+        REDUCE_INC(i);
+    }
+
+    r = (double) k / t;
+
+    return (s->monotonic && r < 0) ? 0 : r;
+}
+
+static void calc_abc(pa_smoother *s) {
+    pa_usec_t ex, ey, px, py;
+    int64_t kx, ky;
+    double de, dp;
+
+    pa_assert(s);
+
+    if (s->abc_valid)
+        return;
+
+    /* We have two points: (ex|ey) and (px|py) with two gradients at
+     * these points de and dp. We do a polynomial
+     * interpolation of degree 3 with these 6 values */
+
+    ex = s->ex; ey = s->ey;
+    px = s->px; py = s->py;
+    de = s->de; dp = s->dp;
+
+    pa_assert(ex < px);
+
+    /* To increase the dynamic range and symplify calculation, we
+     * move these values to the origin */
+    kx = (int64_t) px - (int64_t) ex;
+    ky = (int64_t) py - (int64_t) ey;
+
+    /* Calculate a, b, c for y=ax^3+bx^2+cx */
+    s->c = de;
+    s->b = (((double) (3*ky)/kx - dp - 2*de)) / kx;
+    s->a = (dp/kx - 2*s->b - de/kx) / (3*kx);
+
+    s->abc_valid = TRUE;
+}
+
+static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
+    pa_assert(s);
+    pa_assert(y);
+
+    if (x >= s->px) {
+        int64_t t;
+
+        /* The requested point is right of the point where we wanted
+         * to be on track again, thus just linearly estimate */
+
+        t = (int64_t) s->py + (int64_t) (s->dp * (x - s->px));
+
+        if (t < 0)
+            t = 0;
+
+        *y = (pa_usec_t) t;
+
+        if (deriv)
+            *deriv = s->dp;
+
+    } else {
+
+        /* Ok, we're not yet on track, thus let's interpolate, and
+         * make sure that the first derivative is smooth */
+
+        calc_abc(s);
+
+        /* Move to origin */
+        x -= s->ex;
+
+        /* Horner scheme */
+        *y = (pa_usec_t) ((double) x * (s->c + (double) x * (s->b + (double) x * s->a)));
+
+        /* Move back from origin */
+        *y += s->ey;
+
+        /* Horner scheme */
+        if (deriv)
+            *deriv = s->c + ((double) x * (s->b*2 + (double) x * s->a*3));
+    }
+
+    /* Guarantee monotonicity */
+    if (s->monotonic) {
+
+        if (deriv && *deriv < 0)
+            *deriv = 0;
+    }
+}
+
+void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
+    pa_usec_t ney;
+    double nde;
+    pa_bool_t is_new;
+
+    pa_assert(s);
+
+    /* Fix up x value */
+    if (s->paused)
+        x = s->pause_time;
+
+    x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+    is_new = x >= s->ex;
+
+    if (is_new) {
+        /* First, we calculate the position we'd estimate for x, so that
+         * we can adjust our position smoothly from this one */
+        estimate(s, x, &ney, &nde);
+        s->ex = x; s->ey = ney; s->de = nde;
+
+        s->ry = y;
+    }
+
+    /* Then, we add the new measurement to our history */
+    add_to_history(s, x, y);
+
+    /* And determine the average gradient of the history */
+    s->dp = avg_gradient(s, x);
+
+    /* And calculate when we want to be on track again */
+    s->px = s->ex + s->adjust_time;
+    s->py = s->ry + s->dp *s->adjust_time;
+
+    s->abc_valid = FALSE;
+
+/*     pa_log_debug("put(%llu | %llu) = %llu", (unsigned long long)  (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+}
+
+pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
+    pa_usec_t y;
+
+    pa_assert(s);
+
+    /* Fix up x value */
+    if (s->paused)
+        x = s->pause_time;
+
+    x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+    estimate(s, x, &y, NULL);
+
+    if (s->monotonic) {
+
+        /* Make sure the querier doesn't jump forth and back. */
+        pa_assert(x >= s->last_x);
+        s->last_x = x;
+
+        if (y < s->last_y)
+            y = s->last_y;
+        else
+            s->last_y = y;
+    }
+
+/*     pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+
+    return y;
+}
+
+void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
+    pa_assert(s);
+
+    s->time_offset = offset;
+
+/*     pa_log_debug("offset(%llu)", (unsigned long long) offset); */
+}
+
+void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
+    pa_assert(s);
+
+    if (s->paused)
+        return;
+
+/*     pa_log_debug("pause(%llu)", (unsigned long long)  x); */
+
+    s->paused = TRUE;
+    s->pause_time = x;
+}
+
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
+    pa_assert(s);
+
+    if (!s->paused)
+        return;
+
+    pa_assert(x >= s->pause_time);
+
+/*     pa_log_debug("resume(%llu)", (unsigned long long) x); */
+
+    s->paused = FALSE;
+    s->time_offset += x - s->pause_time;
+}
+
+pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) {
+    pa_usec_t ney;
+    double nde;
+
+    pa_assert(s);
+
+    /* Fix up x value */
+    if (s->paused)
+        x = s->pause_time;
+
+    x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
+
+    estimate(s, x, &ney, &nde);
+
+    /* Play safe and take the larger gradient, so that we wakeup
+     * earlier when this is used for sleeping */
+    if (s->dp > nde)
+        nde = s->dp;
+
+/*     pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */
+
+    return (pa_usec_t) ((double) y_delay / nde);
+}
+
+void pa_smoother_reset(pa_smoother *s) {
+    pa_assert(s);
+
+    s->n_history = 0;
+    s->abc_valid = FALSE;
+
+}
diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h
new file mode 100644 (file)
index 0000000..2051e64
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef foopulsetimesmootherhfoo
+#define foopulsetimesmootherhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2007 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/macro.h>
+#include <pulse/sample.h>
+
+typedef struct pa_smoother pa_smoother;
+
+pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic, unsigned min_history);
+void pa_smoother_free(pa_smoother* s);
+
+/* Adds a new value to our dataset. x = local/system time, y = remote time */
+void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y);
+
+/* Returns an interpolated value based on the dataset. x = local/system time, return value = remote time */
+pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x);
+
+/* Translates a time span from the remote time domain to the local one. x = local/system time when to estimate, y_delay = remote time span */
+pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay);
+
+void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t x_offset);
+
+void pa_smoother_pause(pa_smoother *s, pa_usec_t x);
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x);
+
+void pa_smoother_reset(pa_smoother *s);
+
+#endif
similarity index 52%
rename from polyp/tokenizer.c
rename to src/pulsecore/tokenizer.c
index 8ccbc84f1fd63a6832be9a0215ce58d9bc8a1cd8..d1e0836ba2b0a910adc3a087303699ab0d853c00 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #endif
 
 #include <string.h>
-#include <assert.h>
 #include <stdlib.h>
 
-#include "tokenizer.h"
-#include "dynarray.h"
-#include "xmalloc.h"
+#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
 
-struct pa_tokenizer {
-    struct pa_dynarray *dynarray;
-};
+#include <pulsecore/dynarray.h>
+#include <pulsecore/macro.h>
 
-static void token_free(void *p, void *userdata) {
+#include "tokenizer.h"
+
+static void token_free(void *p, PA_GCC_UNUSED void *userdata) {
     pa_xfree(p);
 }
 
-static void parse(struct pa_dynarray*a, const char *s, unsigned args) {
+static void parse(pa_dynarray*a, const char *s, unsigned args) {
     int infty = 0;
     const char delimiter[] = " \t\n\r";
     const char *p;
-    assert(a && s);
+
+    pa_assert(a);
+    pa_assert(s);
 
     if (args == 0)
         infty = 1;
@@ -64,24 +65,24 @@ static void parse(struct pa_dynarray*a, const char *s, unsigned args) {
     }
 }
 
-struct pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) {
-    struct pa_tokenizer *t;
-    
-    t = pa_xmalloc(sizeof(struct pa_tokenizer));
-    t->dynarray = pa_dynarray_new();
-    assert(t->dynarray);
+pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) {
+    pa_dynarray *a;
 
-    parse(t->dynarray, s, args);
-    return t;
+    a = pa_dynarray_new();
+    parse(a, s, args);
+    return (pa_tokenizer*) a;
 }
 
-void pa_tokenizer_free(struct pa_tokenizer *t) {
-    assert(t);
-    pa_dynarray_free(t->dynarray, token_free, NULL);
-    pa_xfree(t);
+void pa_tokenizer_free(pa_tokenizer *t) {
+    pa_dynarray *a = (pa_dynarray*) t;
+
+    pa_assert(a);
+    pa_dynarray_free(a, token_free, NULL);
 }
 
-const char *pa_tokenizer_get(struct pa_tokenizer *t, unsigned i) {
-    assert(t);
-    return pa_dynarray_get(t->dynarray, i);
+const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) {
+    pa_dynarray *a = (pa_dynarray*) t;
+
+    pa_assert(a);
+    return pa_dynarray_get(a, i);
 }
similarity index 55%
rename from polyp/tokenizer.h
rename to src/pulsecore/tokenizer.h
index 0b1c5022adb25d494026719484f067225c7071be..d51cd73ed1d9f9981ff718850bd1859124b66bf6 100644 (file)
@@ -1,32 +1,32 @@
 #ifndef footokenizerhfoo
 #define footokenizerhfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
-struct pa_tokenizer;
+typedef struct pa_tokenizer pa_tokenizer;
 
-struct pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args);
-void pa_tokenizer_free(struct pa_tokenizer *t);
+pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args);
+void pa_tokenizer_free(pa_tokenizer *t);
 
-const char *pa_tokenizer_get(struct pa_tokenizer *t, unsigned i);
+const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i);
 
 #endif
diff --git a/src/pulsecore/winsock.h b/src/pulsecore/winsock.h
new file mode 100644 (file)
index 0000000..0352bf4
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef foowinsockhfoo
+#define foowinsockhfoo
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+
+#define ESHUTDOWN       WSAESHUTDOWN
+#define ECONNRESET      WSAECONNRESET
+#define ECONNABORTED    WSAECONNABORTED
+#define ENETRESET       WSAENETRESET
+#define EINPROGRESS     WSAEINPROGRESS
+#define EAFNOSUPPORT    WSAEAFNOSUPPORT
+#define ETIMEDOUT       WSAETIMEDOUT
+#define ECONNREFUSED    WSAECONNREFUSED
+#define EHOSTUNREACH    WSAEHOSTUNREACH
+#define EWOULDBLOCK     WSAEWOULDBLOCK
+
+typedef long suseconds_t;
+
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#endif
similarity index 83%
rename from polyp/x11prop.c
rename to src/pulsecore/x11prop.c
index bbe3e32c15ac312eb2bfde785b1b1ca17cbae035..9e75f63a96fc698d3f4cdd1829f23b666a7da90d 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
 
-  polypaudio is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
 
-  polypaudio is distributed in the hope that it will be useful, but
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include "x11prop.h"
 
-
 void pa_x11_set_prop(Display *d, const char *name, const char *data) {
     Atom a = XInternAtom(d, name, False);
-    XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (unsigned char*) data, strlen(data)+1);
+    XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, strlen(data)+1);
 }
 
 void pa_x11_del_prop(Display *d, const char *name) {
@@ -48,7 +47,7 @@ char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l) {
     unsigned long nbytes_after;
     unsigned char *prop = NULL;
     char *ret = NULL;
-    
+
     Atom a = XInternAtom(d, name, False);
     if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (l+2)/4, False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success)
         goto finish;
@@ -65,6 +64,6 @@ finish:
 
     if (prop)
         XFree(prop);
-    
+
     return ret;
 }
similarity index 75%
rename from polyp/x11prop.h
rename to src/pulsecore/x11prop.h
index 5531c6403bd832333e2c8cb4eba58280cfd2931f..c5998d3e68537f3874b34759aa735cf6797176bf 100644 (file)
@@ -1,23 +1,23 @@
 #ifndef foox11prophfoo
 #define foox11prophfoo
 
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
 
-  polypaudio is free software; you can redistribute it and/or modify
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
 
-  polypaudio is distributed in the hope that it will be useful, but
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c
new file mode 100644 (file)
index 0000000..00b6a15
--- /dev/null
@@ -0,0 +1,300 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/xmalloc.h>
+
+#include <pulsecore/llist.h>
+#include <pulsecore/log.h>
+#include <pulsecore/props.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "x11wrap.h"
+
+typedef struct pa_x11_internal pa_x11_internal;
+
+struct pa_x11_internal {
+    PA_LLIST_FIELDS(pa_x11_internal);
+    pa_x11_wrapper *wrapper;
+    pa_io_event* io_event;
+    int fd;
+};
+
+struct pa_x11_wrapper {
+    PA_REFCNT_DECLARE;
+    pa_core *core;
+
+    char *property_name;
+    Display *display;
+
+    pa_defer_event* defer_event;
+    pa_io_event* io_event;
+
+    PA_LLIST_HEAD(pa_x11_client, clients);
+    PA_LLIST_HEAD(pa_x11_internal, internals);
+};
+
+struct pa_x11_client {
+    PA_LLIST_FIELDS(pa_x11_client);
+    pa_x11_wrapper *wrapper;
+    pa_x11_event_cb_t event_cb;
+    pa_x11_kill_cb_t kill_cb;
+    void *userdata;
+};
+
+/* Dispatch all pending X11 events */
+static void work(pa_x11_wrapper *w) {
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    pa_x11_wrapper_ref(w);
+
+    while (XPending(w->display)) {
+        pa_x11_client *c, *n;
+        XEvent e;
+        XNextEvent(w->display, &e);
+
+        for (c = w->clients; c; c = n) {
+            n = c->next;
+
+            if (c->event_cb)
+                if (c->event_cb(w, &e, c->userdata) != 0)
+                    break;
+        }
+    }
+
+    pa_x11_wrapper_unref(w);
+}
+
+/* IO notification event for the X11 display connection */
+static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+    pa_x11_wrapper *w = userdata;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(fd >= 0);
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    work(w);
+}
+
+/* Deferred notification event. Called once each main loop iteration */
+static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
+    pa_x11_wrapper *w = userdata;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    m->defer_enable(e, 0);
+
+    work(w);
+}
+
+/* IO notification event for X11 internal connections */
+static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
+    pa_x11_wrapper *w = userdata;
+
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(fd >= 0);
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    XProcessInternalConnection(w->display, fd);
+
+    work(w);
+}
+
+/* Add a new IO source for the specified X11 internal connection */
+static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) {
+    pa_x11_internal *i;
+    pa_assert(fd >= 0);
+
+    i = pa_xnew(pa_x11_internal, 1);
+    i->wrapper = w;
+    i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w);
+    i->fd = fd;
+
+    PA_LLIST_PREPEND(pa_x11_internal, w->internals, i);
+    return i;
+}
+
+/* Remove an IO source for an X11 internal connection */
+static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) {
+    pa_assert(i);
+
+    PA_LLIST_REMOVE(pa_x11_internal, w->internals, i);
+    w->core->mainloop->io_free(i->io_event);
+    pa_xfree(i);
+}
+
+/* Implementation of XConnectionWatchProc */
+static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) {
+    pa_x11_wrapper *w = (pa_x11_wrapper*) userdata;
+
+    pa_assert(display);
+    pa_assert(w);
+    pa_assert(fd >= 0);
+
+    if (opening)
+        *watch_data = (XPointer) x11_internal_add(w, fd);
+    else
+        x11_internal_remove(w, (pa_x11_internal*) *watch_data);
+}
+
+static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {
+    pa_x11_wrapper*w;
+    Display *d;
+
+    if (!(d = XOpenDisplay(name))) {
+        pa_log("XOpenDisplay() failed");
+        return NULL;
+    }
+
+    w = pa_xnew(pa_x11_wrapper, 1);
+    PA_REFCNT_INIT(w);
+    w->core = c;
+    w->property_name = pa_xstrdup(t);
+    w->display = d;
+
+    PA_LLIST_HEAD_INIT(pa_x11_client, w->clients);
+    PA_LLIST_HEAD_INIT(pa_x11_internal, w->internals);
+
+    w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w);
+    w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w);
+
+    XAddConnectionWatch(d, x11_watch, (XPointer) w);
+
+    pa_assert_se(pa_property_set(c, w->property_name, w) >= 0);
+
+    return w;
+}
+
+static void x11_wrapper_free(pa_x11_wrapper*w) {
+    pa_assert(w);
+
+    pa_assert_se(pa_property_remove(w->core, w->property_name) >= 0);
+
+    pa_assert(!w->clients);
+
+    XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
+    XCloseDisplay(w->display);
+
+    w->core->mainloop->io_free(w->io_event);
+    w->core->mainloop->defer_free(w->defer_event);
+
+    while (w->internals)
+        x11_internal_remove(w, w->internals);
+
+    pa_xfree(w->property_name);
+    pa_xfree(w);
+}
+
+pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {
+    char t[256];
+    pa_x11_wrapper *w;
+
+    pa_core_assert_ref(c);
+
+    pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : "");
+    if ((w = pa_property_get(c, t)))
+        return pa_x11_wrapper_ref(w);
+
+    return x11_wrapper_new(c, name, t);
+}
+
+pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) {
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    PA_REFCNT_INC(w);
+    return w;
+}
+
+void pa_x11_wrapper_unref(pa_x11_wrapper* w) {
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    if (PA_REFCNT_DEC(w) <= 0)
+        x11_wrapper_free(w);
+}
+
+Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    /* Somebody is using us, schedule a output buffer flush */
+    w->core->mainloop->defer_enable(w->defer_event, 1);
+
+    return w->display;
+}
+
+void pa_x11_wrapper_kill(pa_x11_wrapper *w) {
+    pa_x11_client *c, *n;
+
+    pa_assert(w);
+
+    pa_x11_wrapper_ref(w);
+
+    for (c = w->clients; c; c = n) {
+        n = c->next;
+
+        if (c->kill_cb)
+            c->kill_cb(w, c->userdata);
+    }
+
+    pa_x11_wrapper_unref(w);
+}
+
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata) {
+    pa_x11_client *c;
+
+    pa_assert(w);
+    pa_assert(PA_REFCNT_VALUE(w) >= 1);
+
+    c = pa_xnew(pa_x11_client, 1);
+    c->wrapper = w;
+    c->event_cb = event_cb;
+    c->kill_cb = kill_cb;
+    c->userdata = userdata;
+
+    PA_LLIST_PREPEND(pa_x11_client, w->clients, c);
+
+    return c;
+}
+
+void pa_x11_client_free(pa_x11_client *c) {
+    pa_assert(c);
+    pa_assert(c->wrapper);
+    pa_assert(PA_REFCNT_VALUE(c->wrapper) >= 1);
+
+    PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c);
+    pa_xfree(c);
+}
diff --git a/src/pulsecore/x11wrap.h b/src/pulsecore/x11wrap.h
new file mode 100644 (file)
index 0000000..badc3a1
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef foox11wraphfoo
+#define foox11wraphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <X11/Xlib.h>
+
+#include <pulsecore/core.h>
+
+typedef struct pa_x11_wrapper pa_x11_wrapper;
+
+typedef struct pa_x11_client pa_x11_client;
+
+typedef int (*pa_x11_event_cb_t)(pa_x11_wrapper *w, XEvent *e, void *userdata);
+typedef void (*pa_x11_kill_cb_t)(pa_x11_wrapper *w, void *userdata);
+
+/* Return the X11 wrapper for this core. In case no wrapper was
+    existant before, allocate a new one */
+pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name);
+
+/* Increase the wrapper's reference count by one */
+pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w);
+
+/* Decrease the reference counter of an X11 wrapper object */
+void pa_x11_wrapper_unref(pa_x11_wrapper* w);
+
+/* Return the X11 display object for this connection */
+Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w);
+
+/* Kill the connection to the X11 display */
+void pa_x11_wrapper_kill(pa_x11_wrapper *w);
+
+/* Register an X11 client, that is called for each X11 event */
+pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata);
+
+/* Free an X11 client object */
+void pa_x11_client_free(pa_x11_client *c);
+
+#endif
diff --git a/src/tests/Makefile b/src/tests/Makefile
new file mode 120000 (symlink)
index 0000000..c110232
--- /dev/null
@@ -0,0 +1 @@
+../pulse/Makefile
\ No newline at end of file
diff --git a/src/tests/asyncmsgq-test.c b/src/tests/asyncmsgq-test.c
new file mode 100644 (file)
index 0000000..08ad3dd
--- /dev/null
@@ -0,0 +1,108 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <unistd.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/asyncmsgq.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+enum {
+    OPERATION_A,
+    OPERATION_B,
+    OPERATION_C,
+    QUIT
+};
+
+static void the_thread(void *_q) {
+    pa_asyncmsgq *q = _q;
+    int quit = 0;
+
+    do {
+        int code = 0;
+
+        pa_assert_se(pa_asyncmsgq_get(q, NULL, &code, NULL, NULL, NULL, 1) == 0);
+
+        switch (code) {
+
+            case OPERATION_A:
+                printf("Operation A\n");
+                break;
+
+            case OPERATION_B:
+                printf("Operation B\n");
+                break;
+
+            case OPERATION_C:
+                printf("Operation C\n");
+                break;
+
+            case QUIT:
+                printf("quit\n");
+                quit = 1;
+                break;
+        }
+
+        pa_asyncmsgq_done(q, 0);
+
+    } while (!quit);
+}
+
+int main(int argc, char *argv[]) {
+    pa_asyncmsgq *q;
+    pa_thread *t;
+
+    pa_assert_se(q = pa_asyncmsgq_new(0));
+
+    pa_assert_se(t = pa_thread_new(the_thread, q));
+
+    printf("Operation A post\n");
+    pa_asyncmsgq_post(q, NULL, OPERATION_A, NULL, 0, NULL, NULL);
+
+    pa_thread_yield();
+
+    printf("Operation B post\n");
+    pa_asyncmsgq_post(q, NULL, OPERATION_B, NULL, 0, NULL, NULL);
+
+    pa_thread_yield();
+
+    printf("Operation C send\n");
+    pa_asyncmsgq_send(q, NULL, OPERATION_C, NULL, 0, NULL);
+
+    pa_thread_yield();
+
+    printf("Quit post\n");
+    pa_asyncmsgq_post(q, NULL, QUIT, NULL, 0, NULL, NULL);
+
+    pa_thread_free(t);
+
+    pa_asyncmsgq_unref(q);
+
+    return 0;
+}
diff --git a/src/tests/asyncq-test.c b/src/tests/asyncq-test.c
new file mode 100644 (file)
index 0000000..4e8a120
--- /dev/null
@@ -0,0 +1,85 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <unistd.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/asyncq.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+static void producer(void *_q) {
+    pa_asyncq *q = _q;
+    int i;
+
+    for (i = 0; i < 1000; i++) {
+        printf("pushing %i\n", i);
+        pa_asyncq_push(q, PA_UINT_TO_PTR(i+1), 1);
+    }
+
+    pa_asyncq_push(q, PA_UINT_TO_PTR(-1), TRUE);
+    printf("pushed end\n");
+}
+
+static void consumer(void *_q) {
+    pa_asyncq *q = _q;
+    void *p;
+    int i;
+
+    sleep(1);
+
+    for (i = 0;; i++) {
+        p = pa_asyncq_pop(q, TRUE);
+
+        if (p == PA_UINT_TO_PTR(-1))
+            break;
+
+        pa_assert(p == PA_UINT_TO_PTR(i+1));
+
+        printf("popped %i\n", i);
+    }
+
+    printf("popped end\n");
+}
+
+int main(int argc, char *argv[]) {
+    pa_asyncq *q;
+    pa_thread *t1, *t2;
+
+    pa_assert_se(q = pa_asyncq_new(0));
+
+    pa_assert_se(t1 = pa_thread_new(producer, q));
+    pa_assert_se(t2 = pa_thread_new(consumer, q));
+
+    pa_thread_free(t1);
+    pa_thread_free(t2);
+
+    pa_asyncq_free(q, NULL);
+
+    return 0;
+}
diff --git a/src/tests/channelmap-test.c b/src/tests/channelmap-test.c
new file mode 100644 (file)
index 0000000..9c23460
--- /dev/null
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <assert.h>
+
+#include <pulse/channelmap.h>
+#include <pulse/gccmacro.h>
+
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    pa_channel_map map, map2;
+
+    pa_channel_map_init_auto(&map, 6, PA_CHANNEL_MAP_AIFF);
+
+    fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+
+    pa_channel_map_init_auto(&map, 6, PA_CHANNEL_MAP_AUX);
+
+    fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+
+    pa_channel_map_init_auto(&map, 6, PA_CHANNEL_MAP_ALSA);
+
+    fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+
+    pa_channel_map_init_extend(&map, 14, PA_CHANNEL_MAP_ALSA);
+
+    fprintf(stderr, "map: <%s>\n", pa_channel_map_snprint(cm, sizeof(cm), &map));
+
+    pa_channel_map_parse(&map2, cm);
+
+    assert(pa_channel_map_equal(&map, &map2));
+
+    pa_channel_map_parse(&map2, "left,test");
+
+    return 0;
+}
diff --git a/src/tests/close-test.c b/src/tests/close-test.c
new file mode 100644 (file)
index 0000000..7a6fec5
--- /dev/null
@@ -0,0 +1,20 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <pulsecore/core-util.h>
+
+int main(int argc, char *argv[]) {
+
+    open("/dev/null", O_RDONLY);
+    open("/dev/null", O_RDONLY);
+    open("/dev/null", O_RDONLY);
+    open("/dev/null", O_RDONLY);
+
+    pa_close_all(5, -1);
+
+    return 0;
+}
similarity index 72%
rename from polyp/cpulimit-test.c
rename to src/tests/cpulimit-test.c
index de5e20ad952db1fa306b5166a55c5aa10e3d3b0a..b7145e8a5d5f38fabf14747cad09d2fa793c2aea 100644 (file)
@@ -1,20 +1,18 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <stdio.h>
 #include <signal.h>
 
-#include "cpulimit.h"
-#include "mainloop.h"
+#include <pulse/mainloop.h>
+#include <pulse/gccmacro.h>
 
 #ifdef TEST2
-#include "mainloop-signal.h"
+#include <pulse/mainloop-signal.h>
 #endif
 
+#include "../daemon/cpulimit.h"
+
 /* A simple example for testing the cpulimit subsystem */
 
 static time_t start;
 
 #ifdef TEST2
 
-static void func(struct pa_mainloop_api *m, struct pa_signal_event *e, int sig, void *userdata) {
+static void func(pa_mainloop_api *m, PA_GCC_UNUSED pa_signal_event *e, PA_GCC_UNUSED int sig, PA_GCC_UNUSED void *userdata) {
     time_t now;
     time(&now);
-    
+
     if ((now - start) >= 30) {
         m->quit(m, 1);
         fprintf(stderr, "Test failed\n");
@@ -55,9 +55,9 @@ static void func(struct pa_mainloop_api *m, struct pa_signal_event *e, int sig,
 
 #endif
 
-int main() {
-    struct pa_mainloop *m;
-    
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
+    pa_mainloop *m;
+
     m = pa_mainloop_new();
     assert(m);
 
@@ -75,7 +75,7 @@ int main() {
     for (;;) {
         time_t now;
         time(&now);
-        
+
         if ((now - start) >= 30) {
             fprintf(stderr, "Test failed\n");
             break;
@@ -84,7 +84,7 @@ int main() {
 #endif
 
     pa_cpu_limit_done();
-    
+
     pa_mainloop_free(m);
 
     return 0;
diff --git a/src/tests/envelope-test.c b/src/tests/envelope-test.c
new file mode 100644 (file)
index 0000000..9f91455
--- /dev/null
@@ -0,0 +1,246 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/sample.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/envelope.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+
+#include <liboil/liboil.h>
+
+const pa_envelope_def ramp_down = {
+    .n_points = 2,
+    .points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },
+    .points_y = {
+        .f = { 1.0, 0.2 },
+        .i = { 0x10000, 0x10000/5 }
+    }
+};
+
+const pa_envelope_def ramp_up = {
+    .n_points = 2,
+    .points_x = { 100*PA_USEC_PER_MSEC, 300*PA_USEC_PER_MSEC },
+    .points_y = {
+        .f = { 0.2, 1.0 },
+        .i = { 0x10000/5, 0x10000 }
+    }
+};
+
+const pa_envelope_def ramp_down2 = {
+    .n_points = 2,
+    .points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC },
+    .points_y = {
+        .f = { 0.8, 0.7 },
+        .i = { 0x10000*4/5, 0x10000*7/10 }
+    }
+};
+
+const pa_envelope_def ramp_up2 = {
+    .n_points = 2,
+    .points_x = { 50*PA_USEC_PER_MSEC, 900*PA_USEC_PER_MSEC },
+    .points_y = {
+        .f = { 0.7, 0.9 },
+        .i = { 0x10000*7/10, 0x10000*9/10 }
+    }
+};
+
+static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
+    void *d;
+    unsigned i;
+
+    static unsigned j = 0;
+
+    d = pa_memblock_acquire(chunk->memblock);
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_U8:
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW: {
+            uint8_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("0x%02x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_S16NE:
+        case PA_SAMPLE_S16RE: {
+            int16_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("%i\t%i\n", j++, *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S32RE: {
+            int32_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("%i\t%i\n", j++, *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32NE:
+        case PA_SAMPLE_FLOAT32RE: {
+            float *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                printf("%i\t%1.3g\n", j++, PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, *u));
+                u++;
+            }
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    printf("\n");
+
+    pa_memblock_release(chunk->memblock);
+}
+
+static pa_memblock * generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
+    pa_memblock *block;
+    void *d;
+    unsigned n_samples;
+
+    block = pa_memblock_new(pool, pa_bytes_per_second(ss));
+    n_samples = pa_memblock_get_length(block) / pa_sample_size(ss);
+
+    d = pa_memblock_acquire(block);
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_S16NE:
+        case PA_SAMPLE_S16RE: {
+            int16_t *i;
+
+            for (i = d; n_samples > 0; n_samples--, i++)
+                *i = 0x7FFF;
+
+            break;
+        }
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S32RE: {
+            int32_t *i;
+
+            for (i = d; n_samples > 0; n_samples--, i++)
+                *i = 0x7FFFFFFF;
+
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32RE:
+        case PA_SAMPLE_FLOAT32NE: {
+            float *f;
+
+            for (f = d; n_samples > 0; n_samples--, f++)
+                *f = PA_MAYBE_FLOAT32_SWAP(ss->format == PA_SAMPLE_FLOAT32RE, 1.0);
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_memblock_release(block);
+    return block;
+}
+
+int main(int argc, char *argv[]) {
+    pa_mempool *pool;
+    pa_memblock *block;
+    pa_memchunk chunk;
+    pa_envelope *envelope;
+    pa_envelope_item *item1, *item2;
+
+    const pa_sample_spec ss = {
+        .format = PA_SAMPLE_S16NE,
+        .channels = 1,
+        .rate = 200
+    };
+
+    const pa_cvolume v = {
+        .channels = 1,
+        .values = { PA_VOLUME_NORM, PA_VOLUME_NORM/2 }
+    };
+
+    oil_init();
+    pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+    pa_assert_se(pool = pa_mempool_new(FALSE));
+    pa_assert_se(envelope = pa_envelope_new(&ss));
+
+    block = generate_block(pool, &ss);
+
+    chunk.memblock = pa_memblock_ref(block);
+    chunk.length = pa_memblock_get_length(block);
+    chunk.index = 0;
+
+    pa_volume_memchunk(&chunk, &ss, &v);
+
+    item1 = pa_envelope_add(envelope, &ramp_down);
+    item2 = pa_envelope_add(envelope, &ramp_down2);
+    pa_envelope_apply(envelope, &chunk);
+    dump_block(&ss, &chunk);
+
+    pa_memblock_unref(chunk.memblock);
+
+    chunk.memblock = pa_memblock_ref(block);
+    chunk.length = pa_memblock_get_length(block);
+    chunk.index = 0;
+
+    item1 = pa_envelope_replace(envelope, item1, &ramp_up);
+    item2 = pa_envelope_replace(envelope, item2, &ramp_up2);
+    pa_envelope_apply(envelope, &chunk);
+    dump_block(&ss, &chunk);
+
+    pa_memblock_unref(chunk.memblock);
+
+    pa_envelope_remove(envelope, item1);
+    pa_envelope_remove(envelope, item2);
+    pa_envelope_free(envelope);
+
+    pa_memblock_unref(block);
+
+    pa_mempool_free(pool);
+
+    return 0;
+}
diff --git a/src/tests/flist-test.c b/src/tests/flist-test.c
new file mode 100644 (file)
index 0000000..b2c648d
--- /dev/null
@@ -0,0 +1,103 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <unistd.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/flist.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+
+#define THREADS_MAX 20
+
+static pa_flist *flist;
+static int quit = 0;
+
+static void spin(void) {
+    int k;
+
+    /* Spin a little */
+    k = rand() % 10000;
+    for (; k > 0; k--)
+        pa_thread_yield();
+}
+
+static void thread_func(void *data) {
+    char *s = data;
+    int n = 0;
+    int b = 1;
+
+    while (!quit) {
+        char *text;
+
+        /* Allocate some memory, if possible take it from the flist */
+        if (b && (text = pa_flist_pop(flist)))
+            pa_log("%s: popped '%s'", s, text);
+        else {
+            text = pa_sprintf_malloc("Block %i, allocated by %s", n++, s);
+            pa_log("%s: allocated '%s'", s, text);
+        }
+
+        b = !b;
+
+        spin();
+
+        /* Give it back to the flist if possible */
+        if (pa_flist_push(flist, text) < 0) {
+            pa_log("%s: failed to push back '%s'", s, text);
+            pa_xfree(text);
+        } else
+            pa_log("%s: pushed", s);
+
+        spin();
+    }
+
+    if (pa_flist_push(flist, s) < 0)
+        pa_xfree(s);
+}
+
+int main(int argc, char* argv[]) {
+    pa_thread *threads[THREADS_MAX];
+    int i;
+
+    flist = pa_flist_new(0);
+
+    for (i = 0; i < THREADS_MAX; i++) {
+        threads[i] = pa_thread_new(thread_func, pa_sprintf_malloc("Thread #%i", i+1));
+        assert(threads[i]);
+    }
+
+    pa_msleep(60000);
+    quit = 1;
+
+    for (i = 0; i < THREADS_MAX; i++)
+        pa_thread_free(threads[i]);
+
+    pa_flist_free(flist, pa_xfree);
+
+    return 0;
+}
similarity index 54%
rename from polyp/sconv-s16be.c
rename to src/tests/get-binary-name-test.c
index 54efc78cb26a415ec35d68bf6838fdc37e4d401e..7c7a8996180d4d596a7b1d2256374fad4142ed25 100644 (file)
@@ -1,20 +1,18 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <config.h>
 #endif
 
-#include "sconv-s16be.h"
+#include <limits.h>
+#include <stdio.h>
 
-#define INT16_FROM INT16_FROM_BE
-#define INT16_TO INT16_TO_BE
+#include <pulse/util.h>
 
-#define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne
-#define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne
+int main(int argc, char *argv[]) {
+    char exename[PATH_MAX];
 
-#include "sconv-s16le.c"
+    printf("%s\n", pa_get_binary_name(exename, sizeof(exename)));
+    return 0;
+}
diff --git a/src/tests/hook-list-test.c b/src/tests/hook-list-test.c
new file mode 100644 (file)
index 0000000..60b965c
--- /dev/null
@@ -0,0 +1,37 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/hook-list.h>
+#include <pulsecore/log.h>
+
+static pa_hook_result_t func1(const char *hook_data, const char *call_data, const char *slot_data) {
+    pa_log("(func1) hook=%s call=%s slot=%s", hook_data, call_data, slot_data);
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t func2(const char *hook_data, const char *call_data, const char *slot_data) {
+    pa_log("(func2) hook=%s call=%s slot=%s", hook_data, call_data, slot_data);
+    return PA_HOOK_OK;
+}
+
+int main(int argc, char *argv[]) {
+    pa_hook hook;
+    pa_hook_slot *slot;
+
+    pa_hook_init(&hook, (void*) "hook");
+
+    pa_hook_connect(&hook, PA_HOOK_LATE, (pa_hook_cb_t) func1, (void*) "slot1");
+    slot = pa_hook_connect(&hook, PA_HOOK_NORMAL, (pa_hook_cb_t) func2, (void*) "slot2");
+    pa_hook_connect(&hook, PA_HOOK_NORMAL, (pa_hook_cb_t) func1, (void*) "slot3");
+
+    pa_hook_fire(&hook, (void*) "call1");
+
+    pa_hook_slot_free(slot);
+
+    pa_hook_fire(&hook, (void*) "call2");
+
+    pa_hook_free(&hook);
+
+    return 0;
+}
diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
new file mode 100644 (file)
index 0000000..9d93077
--- /dev/null
@@ -0,0 +1,171 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <math.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/mainloop.h>
+
+#include <pulsecore/thread.h>
+
+static pa_context *context = NULL;
+static pa_stream *stream = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+
+static void stream_write_cb(pa_stream *p, size_t nbytes, void *userdata) {
+    /* Just some silence */
+    pa_stream_write(p, pa_xmalloc0(nbytes), nbytes, pa_xfree, 0, PA_SEEK_RELATIVE);
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+
+            static const pa_sample_spec ss = {
+                .format = PA_SAMPLE_S16LE,
+                .rate = 44100,
+                .channels = 2
+            };
+
+            fprintf(stderr, "Connection established.\n");
+
+            stream = pa_stream_new(c, "interpol-test", &ss, NULL);
+            assert(stream);
+
+            pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
+            pa_stream_set_write_callback(stream, stream_write_cb, NULL);
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
+            abort();
+    }
+}
+
+int main(int argc, char *argv[]) {
+    pa_threaded_mainloop* m = NULL;
+    int k, r;
+    struct timeval start, last_info = { 0, 0 };
+    pa_usec_t old_t = 0, old_rtc = 0;
+
+    /* Set up a new main loop */
+    m = pa_threaded_mainloop_new();
+    assert(m);
+
+    mainloop_api = pa_threaded_mainloop_get_api(m);
+
+    context = pa_context_new(mainloop_api, argv[0]);
+    assert(context);
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+
+    r = pa_context_connect(context, NULL, 0, NULL);
+    assert(r >= 0);
+
+    pa_gettimeofday(&start);
+
+    pa_threaded_mainloop_start(m);
+
+    for (k = 0; k < 5000; k++) {
+        pa_bool_t success = FALSE, changed = FALSE;
+        pa_usec_t t, rtc;
+        struct timeval now, tv;
+        pa_bool_t playing = FALSE;
+
+        pa_threaded_mainloop_lock(m);
+
+        if (stream) {
+            const pa_timing_info *info;
+
+            if (pa_stream_get_time(stream, &t) >= 0)
+                success = TRUE;
+
+            if ((info = pa_stream_get_timing_info(stream))) {
+                if (memcmp(&last_info, &info->timestamp, sizeof(struct timeval))) {
+                    changed = TRUE;
+                    last_info = info->timestamp;
+                }
+                if (info->playing)
+                    playing = TRUE;
+            }
+        }
+
+        pa_threaded_mainloop_unlock(m);
+
+        pa_gettimeofday(&now);
+
+        if (success) {
+            rtc = pa_timeval_diff(&now, &start);
+            printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\t%u\n", k, (unsigned long long) rtc, (unsigned long long) t, (unsigned long long) (rtc-old_rtc), (unsigned long long) (t-old_t), changed, playing);
+            fflush(stdout);
+            old_t = t;
+            old_rtc = rtc;
+        }
+
+        /* Spin loop, ugly but normal usleep() is just too badly grained */
+
+        tv = now;
+        while (pa_timeval_diff(pa_gettimeofday(&now), &tv) < 1000)
+            pa_thread_yield();
+    }
+
+    if (m)
+        pa_threaded_mainloop_stop(m);
+
+    if (stream) {
+        pa_stream_disconnect(stream);
+        pa_stream_unref(stream);
+    }
+
+    if (context) {
+        pa_context_disconnect(context);
+        pa_context_unref(context);
+    }
+
+    if (m)
+        pa_threaded_mainloop_free(m);
+
+    return 0;
+}
diff --git a/src/tests/ipacl-test.c b/src/tests/ipacl-test.c
new file mode 100644 (file)
index 0000000..bcdd469
--- /dev/null
@@ -0,0 +1,134 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "../pulsecore/winsock.h"
+
+#include <pulsecore/ipacl.h>
+
+int main(int argc, char *argv[]) {
+    struct sockaddr_in sa;
+    struct sockaddr_in6 sa6;
+    int fd;
+    int r;
+    pa_ip_acl *acl;
+
+    fd = socket(PF_INET, SOCK_STREAM, 0);
+    assert(fd >= 0);
+
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons(22);
+    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+    r = connect(fd, (struct sockaddr*) &sa, sizeof(sa));
+    assert(r >= 0);
+
+    acl = pa_ip_acl_new("127.0.0.1");
+    assert(acl);
+    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("127.0.0.2/0");
+    assert(acl);
+    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("127.0.0.1/32");
+    assert(acl);
+    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("127.0.0.1/7");
+    assert(acl);
+    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("127.0.0.2");
+    assert(acl);
+    printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("127.0.0.0/8;0.0.0.0/32");
+    assert(acl);
+    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("128.0.0.2/9");
+    assert(acl);
+    printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("::1/9");
+    assert(acl);
+    printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    close(fd);
+
+    fd = socket(PF_INET6, SOCK_STREAM, 0);
+    assert(fd >= 0);
+
+    memset(&sa6, 0, sizeof(sa6));
+    sa6.sin6_family = AF_INET6;
+    sa6.sin6_port = htons(22);
+    inet_pton(AF_INET6, "::1", &sa6.sin6_addr);
+
+    r = connect(fd, (struct sockaddr*) &sa6, sizeof(sa6));
+    assert(r >= 0);
+
+    acl = pa_ip_acl_new("::1");
+    assert(acl);
+    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("::1/9");
+    assert(acl);
+    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("::/0");
+    assert(acl);
+    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("::2/128");
+    assert(acl);
+    printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("::2/127");
+    assert(acl);
+    printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    acl = pa_ip_acl_new("::2/126");
+    assert(acl);
+    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd));
+    pa_ip_acl_free(acl);
+
+    close(fd);
+
+    return 0;
+}
similarity index 59%
rename from polyp/mainloop-test.c
rename to src/tests/mainloop-test.c
index 0d40e76ed77f70d02b70872ecaa34f604864f6cd..9fa2e4665a8ce8ba51170509e1dc79dbe3655ea0 100644 (file)
@@ -1,20 +1,18 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <sys/time.h>
 #include <assert.h>
 
+#include <pulse/timeval.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/core-util.h>
+
 #ifdef GLIB_MAIN_LOOP
 
 #include <glib.h>
-#include "glib-mainloop.h"
-static GMainLoop* glib_main_loop = NULL;
-
-#if GLIB_MAJOR_VERSION >= 2
-#define GLIB20
-#else
-#undef GLIB20
-#endif 
+#include <pulse/glib-mainloop.h>
 
+static GMainLoop* glib_main_loop = NULL;
 
 #else /* GLIB_MAIN_LOOP */
-#include "mainloop.h"
+#include <pulse/mainloop.h>
 #endif /* GLIB_MAIN_LOOP */
 
-static struct pa_defer_event *de;
+static pa_defer_event *de;
 
-static void iocb(struct pa_mainloop_api*a, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
+static void iocb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
     unsigned char c;
     read(fd, &c, sizeof(c));
     fprintf(stderr, "IO EVENT: %c\n", c < 32 ? '.' : c);
     a->defer_enable(de, 1);
 }
 
-static void dcb(struct pa_mainloop_api*a, struct pa_defer_event *e, void *userdata) {
+static void dcb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
     fprintf(stderr, "DEFER EVENT\n");
     a->defer_enable(e, 0);
 }
 
-static void tcb(struct pa_mainloop_api*a, struct pa_time_event *e, const struct timeval *tv, void *userdata) {
+static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
     fprintf(stderr, "TIME EVENT\n");
 
-#if defined(GLIB_MAIN_LOOP) && defined(GLIB20)
+#if defined(GLIB_MAIN_LOOP)
     g_main_loop_quit(glib_main_loop);
-#elif defined(GLIB_MAIN_LOOP)
-    g_main_quit(glib_main_loop);
 #else
     a->quit(a, 0);
 #endif
 }
 
-int main(int argc, char *argv[]) {
-    struct pa_mainloop_api *a;
-    struct pa_io_event *ioe;
-    struct pa_time_event *te;
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
+    pa_mainloop_api *a;
+    pa_io_event *ioe;
+    pa_time_event *te;
     struct timeval tv;
 
 #ifdef GLIB_MAIN_LOOP
-    struct pa_glib_mainloop *g;
+    pa_glib_mainloop *g;
 
-#ifdef GLIB20 
     glib_main_loop = g_main_loop_new(NULL, FALSE);
     assert(glib_main_loop);
 
     g = pa_glib_mainloop_new(NULL);
-#else /* GLIB20 */
-    glib_main_loop = g_main_new(FALSE);
-    assert(glib_main_loop);
-    
-    g = pa_glib_mainloop_new();
-#endif /* GLIB20 */
     assert(g);
 
     a = pa_glib_mainloop_get_api(g);
     assert(a);
 #else /* GLIB_MAIN_LOOP */
-    struct pa_mainloop *m;
+    pa_mainloop *m;
 
     m = pa_mainloop_new();
     assert(m);
@@ -111,14 +99,12 @@ int main(int argc, char *argv[]) {
     de = a->defer_new(a, dcb, NULL);
     assert(de);
 
-    gettimeofday(&tv, NULL);
+    pa_gettimeofday(&tv);
     tv.tv_sec += 10;
     te = a->time_new(a, &tv, tcb, NULL);
 
-#if defined(GLIB_MAIN_LOOP) && defined(GLIB20)
+#if defined(GLIB_MAIN_LOOP)
     g_main_loop_run(glib_main_loop);
-#elif defined(GLIB_MAIN_LOOP)
-    g_main_run(glib_main_loop);
 #else
     pa_mainloop_run(m, NULL);
 #endif
@@ -129,14 +115,10 @@ int main(int argc, char *argv[]) {
 
 #ifdef GLIB_MAIN_LOOP
     pa_glib_mainloop_free(g);
-#ifdef GLIB20
     g_main_loop_unref(glib_main_loop);
-#else
-    g_main_destroy(glib_main_loop);
-#endif
 #else
     pa_mainloop_free(m);
 #endif
-    
+
     return 0;
 }
similarity index 56%
rename from polyp/mcalign-test.c
rename to src/tests/mcalign-test.c
index e4a6a2393feceb18600282a9cfc62d687088684b..9e35835968b9fc7587f2dd2121770649e215defe 100644 (file)
@@ -1,20 +1,18 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Lesser General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
-  License along with polypaudio; if not, write to the Free Software
+  License along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <stdlib.h>
 #include <time.h>
 
-#include "util.h"
-#include "mcalign.h"
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/mcalign.h>
 
 /* A simple program for testing pa_mcalign */
 
-int main(int argc, char *argv[]) {
-    struct pa_mcalign *a = pa_mcalign_new(11, NULL);
-    struct pa_memchunk c;
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
+    pa_mempool *p;
+    pa_mcalign *a;
+    pa_memchunk c;
+
+    p = pa_mempool_new(0);
+
+    a = pa_mcalign_new(11);
 
     pa_memchunk_reset(&c);
 
@@ -49,39 +54,46 @@ int main(int argc, char *argv[]) {
         size_t l;
 
         if (!c.memblock) {
-            c.memblock = pa_memblock_new(2048, NULL);
+            c.memblock = pa_memblock_new(p, 2048);
             c.index = c.length = 0;
         }
 
-        assert(c.index < c.memblock->length);
+        assert(c.index < pa_memblock_get_length(c.memblock));
 
-        l = c.memblock->length - c.index;
+        l = pa_memblock_get_length(c.memblock) - c.index;
 
         l = l <= 1 ? l : rand() % (l-1) +1 ;
-        
-        if ((r = read(STDIN_FILENO, (uint8_t*) c.memblock->data + c.index, l)) <= 0) {
+
+        p = pa_memblock_acquire(c.memblock);
+
+        if ((r = read(STDIN_FILENO, (uint8_t*) p + c.index, l)) <= 0) {
+            pa_memblock_release(c.memblock);
             fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
             break;
         }
 
+        pa_memblock_release(c.memblock);
+
         c.length = r;
         pa_mcalign_push(a, &c);
-        fprintf(stderr, "Read %u bytes\n", r);
+        fprintf(stderr, "Read %ld bytes\n", (long)r);
 
         c.index += r;
 
-        if (c.index >= c.memblock->length) {
+        if (c.index >= pa_memblock_get_length(c.memblock)) {
             pa_memblock_unref(c.memblock);
             pa_memchunk_reset(&c);
         }
 
         for (;;) {
-            struct pa_memchunk t;
+            pa_memchunk t;
 
             if (pa_mcalign_pop(a, &t) < 0)
                 break;
 
-            pa_loop_write(STDOUT_FILENO, (uint8_t*) t.memblock->data + t.index, t.length);
+            p = pa_memblock_acquire(t.memblock);
+            pa_loop_write(STDOUT_FILENO, (uint8_t*) p + t.index, t.length, NULL);
+            pa_memblock_release(t.memblock);
             fprintf(stderr, "Wrote %lu bytes.\n", (unsigned long) t.length);
 
             pa_memblock_unref(t.memblock);
@@ -92,4 +104,8 @@ int main(int argc, char *argv[]) {
 
     if (c.memblock)
         pa_memblock_unref(c.memblock);
+
+    pa_mempool_free(p);
+
+    return 0;
 }
diff --git a/src/tests/memblock-test.c b/src/tests/memblock-test.c
new file mode 100644 (file)
index 0000000..6da1b1e
--- /dev/null
@@ -0,0 +1,174 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulsecore/memblock.h>
+#include <pulsecore/macro.h>
+#include <pulse/xmalloc.h>
+
+static void release_cb(pa_memimport *i, uint32_t block_id, void *userdata) {
+    printf("%s: Imported block %u is released.\n", (char*) userdata, block_id);
+}
+
+static void revoke_cb(pa_memexport *e, uint32_t block_id, void *userdata) {
+    printf("%s: Exported block %u is revoked.\n", (char*) userdata, block_id);
+}
+
+static void print_stats(pa_mempool *p, const char *text) {
+    const pa_mempool_stat*s = pa_mempool_get_stat(p);
+
+    printf("%s = {\n"
+           "n_allocated = %u\n"
+           "n_accumulated = %u\n"
+           "n_imported = %u\n"
+           "n_exported = %u\n"
+           "allocated_size = %u\n"
+           "accumulated_size = %u\n"
+           "imported_size = %u\n"
+           "exported_size = %u\n"
+           "n_too_large_for_pool = %u\n"
+           "n_pool_full = %u\n"
+           "}\n",
+           text,
+           (unsigned) pa_atomic_load(&s->n_allocated),
+           (unsigned) pa_atomic_load(&s->n_accumulated),
+           (unsigned) pa_atomic_load(&s->n_imported),
+           (unsigned) pa_atomic_load(&s->n_exported),
+           (unsigned) pa_atomic_load(&s->allocated_size),
+           (unsigned) pa_atomic_load(&s->accumulated_size),
+           (unsigned) pa_atomic_load(&s->imported_size),
+           (unsigned) pa_atomic_load(&s->exported_size),
+           (unsigned) pa_atomic_load(&s->n_too_large_for_pool),
+           (unsigned) pa_atomic_load(&s->n_pool_full));
+}
+
+int main(int argc, char *argv[]) {
+    pa_mempool *pool_a, *pool_b, *pool_c;
+    unsigned id_a, id_b, id_c;
+    pa_memexport *export_a, *export_b;
+    pa_memimport *import_b, *import_c;
+    pa_memblock *mb_a, *mb_b, *mb_c;
+    int r, i;
+    pa_memblock* blocks[5];
+    uint32_t id, shm_id;
+    size_t offset, size;
+    char *x;
+
+    const char txt[] = "This is a test!";
+
+    pool_a = pa_mempool_new(1);
+    pool_b = pa_mempool_new(1);
+    pool_c = pa_mempool_new(1);
+
+    pa_mempool_get_shm_id(pool_a, &id_a);
+    pa_mempool_get_shm_id(pool_b, &id_b);
+    pa_mempool_get_shm_id(pool_c, &id_c);
+
+    pa_assert(pool_a && pool_b && pool_c);
+
+    blocks[0] = pa_memblock_new_fixed(pool_a, (void*) txt, sizeof(txt), 1);
+
+    blocks[1] = pa_memblock_new(pool_a, sizeof(txt));
+    x = pa_memblock_acquire(blocks[1]);
+    snprintf(x, pa_memblock_get_length(blocks[1]), "%s", txt);
+    pa_memblock_release(blocks[1]);
+
+    blocks[2] = pa_memblock_new_pool(pool_a, sizeof(txt));
+    x = pa_memblock_acquire(blocks[2]);
+    snprintf(x, pa_memblock_get_length(blocks[2]), "%s", txt);
+    pa_memblock_release(blocks[2]);
+
+    blocks[3] = pa_memblock_new_malloced(pool_a, pa_xstrdup(txt), sizeof(txt));
+    blocks[4] = NULL;
+
+    for (i = 0; blocks[i]; i++) {
+        printf("Memory block %u\n", i);
+
+        mb_a = blocks[i];
+        pa_assert(mb_a);
+
+        export_a = pa_memexport_new(pool_a, revoke_cb, (void*) "A");
+        export_b = pa_memexport_new(pool_b, revoke_cb, (void*) "B");
+
+        pa_assert(export_a && export_b);
+
+        import_b = pa_memimport_new(pool_b, release_cb, (void*) "B");
+        import_c = pa_memimport_new(pool_c, release_cb, (void*) "C");
+
+        pa_assert(import_b && import_c);
+
+        r = pa_memexport_put(export_a, mb_a, &id, &shm_id, &offset, &size);
+        pa_assert(r >= 0);
+        pa_assert(shm_id == id_a);
+
+        printf("A: Memory block exported as %u\n", id);
+
+        mb_b = pa_memimport_get(import_b, id, shm_id, offset, size);
+        pa_assert(mb_b);
+        r = pa_memexport_put(export_b, mb_b, &id, &shm_id, &offset, &size);
+        pa_assert(r >= 0);
+        pa_assert(shm_id == id_a || shm_id == id_b);
+        pa_memblock_unref(mb_b);
+
+        printf("B: Memory block exported as %u\n", id);
+
+        mb_c = pa_memimport_get(import_c, id, shm_id, offset, size);
+        pa_assert(mb_c);
+        x = pa_memblock_acquire(mb_c);
+        printf("1 data=%s\n", x);
+        pa_memblock_release(mb_c);
+
+        print_stats(pool_a, "A");
+        print_stats(pool_b, "B");
+        print_stats(pool_c, "C");
+
+        pa_memexport_free(export_b);
+        x = pa_memblock_acquire(mb_c);
+        printf("2 data=%s\n", x);
+        pa_memblock_release(mb_c);
+        pa_memblock_unref(mb_c);
+
+        pa_memimport_free(import_b);
+
+        pa_memblock_unref(mb_a);
+
+        pa_memimport_free(import_c);
+        pa_memexport_free(export_a);
+    }
+
+    printf("vaccuuming...\n");
+
+    pa_mempool_vacuum(pool_a);
+    pa_mempool_vacuum(pool_b);
+    pa_mempool_vacuum(pool_c);
+
+    printf("vaccuuming done...\n");
+
+    pa_mempool_free(pool_a);
+    pa_mempool_free(pool_b);
+    pa_mempool_free(pool_c);
+
+    return 0;
+}
diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c
new file mode 100644 (file)
index 0000000..7bf992a
--- /dev/null
@@ -0,0 +1,164 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include <pulsecore/memblockq.h>
+#include <pulsecore/log.h>
+
+static void dump(pa_memblockq *bq) {
+    printf(">");
+
+    for (;;) {
+        pa_memchunk out;
+        char *e;
+        size_t n;
+        void *q;
+
+        if (pa_memblockq_peek(bq, &out) < 0)
+            break;
+
+        q = pa_memblock_acquire(out.memblock);
+        for (e = (char*) q + out.index, n = 0; n < out.length; n++)
+            printf("%c", *e);
+        pa_memblock_release(out.memblock);
+
+        pa_memblock_unref(out.memblock);
+        pa_memblockq_drop(bq, out.length);
+    }
+
+    printf("<\n");
+}
+
+int main(int argc, char *argv[]) {
+    int ret;
+
+    pa_mempool *p;
+    pa_memblockq *bq;
+    pa_memchunk chunk1, chunk2, chunk3, chunk4;
+    pa_memchunk silence;
+
+    pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+    p = pa_mempool_new(0);
+
+    silence.memblock = pa_memblock_new_fixed(p, (char*)  "__", 2, 1);
+    assert(silence.memblock);
+    silence.index = 0;
+    silence.length = pa_memblock_get_length(silence.memblock);
+
+    bq = pa_memblockq_new(0, 40, 10, 2, 4, 4, 40, &silence);
+    assert(bq);
+
+    chunk1.memblock = pa_memblock_new_fixed(p, (char*) "11", 2, 1);
+    chunk1.index = 0;
+    chunk1.length = 2;
+    assert(chunk1.memblock);
+
+    chunk2.memblock = pa_memblock_new_fixed(p, (char*) "XX22", 4, 1);
+    chunk2.index = 2;
+    chunk2.length = 2;
+    assert(chunk2.memblock);
+
+    chunk3.memblock = pa_memblock_new_fixed(p, (char*) "3333", 4, 1);
+    chunk3.index = 0;
+    chunk3.length = 4;
+    assert(chunk3.memblock);
+
+    chunk4.memblock = pa_memblock_new_fixed(p, (char*) "44444444", 8, 1);
+    chunk4.index = 0;
+    chunk4.length = 8;
+    assert(chunk4.memblock);
+
+    ret = pa_memblockq_push(bq, &chunk1);
+    assert(ret == 0);
+
+    ret = pa_memblockq_push(bq, &chunk2);
+    assert(ret == 0);
+
+    ret = pa_memblockq_push(bq, &chunk3);
+    assert(ret == 0);
+
+    ret = pa_memblockq_push(bq, &chunk4);
+    assert(ret == 0);
+
+    pa_memblockq_seek(bq, -6, 0);
+    ret = pa_memblockq_push(bq, &chunk3);
+    assert(ret == 0);
+
+    pa_memblockq_seek(bq, -2, 0);
+    ret = pa_memblockq_push(bq, &chunk1);
+    assert(ret == 0);
+
+    pa_memblockq_seek(bq, -10, 0);
+    ret = pa_memblockq_push(bq, &chunk4);
+    assert(ret == 0);
+
+    pa_memblockq_seek(bq, 10, 0);
+
+    ret = pa_memblockq_push(bq, &chunk1);
+    assert(ret == 0);
+
+    pa_memblockq_seek(bq, -6, 0);
+    ret = pa_memblockq_push(bq, &chunk2);
+    assert(ret == 0);
+
+    /* Test splitting */
+    pa_memblockq_seek(bq, -12, 0);
+    ret = pa_memblockq_push(bq, &chunk1);
+    assert(ret == 0);
+
+    pa_memblockq_seek(bq, 20, 0);
+
+    /* Test merging */
+    ret = pa_memblockq_push(bq, &chunk3);
+    assert(ret == 0);
+    pa_memblockq_seek(bq, -2, 0);
+
+    chunk3.index += 2;
+    chunk3.length -= 2;
+    ret = pa_memblockq_push(bq, &chunk3);
+    assert(ret == 0);
+
+    pa_memblockq_seek(bq, 30, PA_SEEK_RELATIVE);
+
+    dump(bq);
+
+    pa_memblockq_rewind(bq, 52);
+
+    dump(bq);
+
+    pa_memblockq_free(bq);
+    pa_memblock_unref(silence.memblock);
+    pa_memblock_unref(chunk1.memblock);
+    pa_memblock_unref(chunk2.memblock);
+    pa_memblock_unref(chunk3.memblock);
+    pa_memblock_unref(chunk4.memblock);
+
+    pa_mempool_free(p);
+
+    return 0;
+}
diff --git a/src/tests/mix-test.c b/src/tests/mix-test.c
new file mode 100644 (file)
index 0000000..f3f6f82
--- /dev/null
@@ -0,0 +1,259 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/sample.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+
+#include <liboil/liboil.h>
+
+static float swap_float(float a) {
+    uint32_t *b = (uint32_t*) &a;
+    *b = PA_UINT32_SWAP(*b);
+    return a;
+}
+
+static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
+    void *d;
+    unsigned i;
+
+    d = pa_memblock_acquire(chunk->memblock);
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_U8:
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW: {
+            uint8_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("0x%02x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_S16NE:
+        case PA_SAMPLE_S16RE: {
+            uint16_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("0x%04x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S32RE: {
+            uint32_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("0x%08x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32NE:
+        case PA_SAMPLE_FLOAT32RE: {
+            float *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                printf("%1.5f ",  ss->format == PA_SAMPLE_FLOAT32NE ? *u : swap_float(*u));
+                u++;
+            }
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    printf("\n");
+
+    pa_memblock_release(chunk->memblock);
+}
+
+static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
+    pa_memblock *r;
+    void *d;
+    unsigned i;
+
+    pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10));
+    d = pa_memblock_acquire(r);
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_U8:
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW: {
+            uint8_t *u = d;
+
+            u[0] = 0x00;
+            u[1] = 0xFF;
+            u[2] = 0x7F;
+            u[3] = 0x80;
+            u[4] = 0x9f;
+            u[5] = 0x3f;
+            u[6] = 0x1;
+            u[7] = 0xF0;
+            u[8] = 0x20;
+            u[9] = 0x21;
+            break;
+        }
+
+        case PA_SAMPLE_S16NE:
+        case PA_SAMPLE_S16RE: {
+            uint16_t *u = d;
+
+            u[0] = 0x0000;
+            u[1] = 0xFFFF;
+            u[2] = 0x7FFF;
+            u[3] = 0x8000;
+            u[4] = 0x9fff;
+            u[5] = 0x3fff;
+            u[6] = 0x1;
+            u[7] = 0xF000;
+            u[8] = 0x20;
+            u[9] = 0x21;
+            break;
+        }
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S32RE: {
+            uint32_t *u = d;
+
+            u[0] = 0x00000001;
+            u[1] = 0xFFFF0002;
+            u[2] = 0x7FFF0003;
+            u[3] = 0x80000004;
+            u[4] = 0x9fff0005;
+            u[5] = 0x3fff0006;
+            u[6] =    0x10007;
+            u[7] = 0xF0000008;
+            u[8] =   0x200009;
+            u[9] =   0x21000A;
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32NE:
+        case PA_SAMPLE_FLOAT32RE: {
+            float *u = d;
+
+            u[0] = 0.0;
+            u[1] = -1.0;
+            u[2] = 1.0;
+            u[3] = 4711;
+            u[4] = 0.222;
+            u[5] = 0.33;
+            u[6] = -.3;
+            u[7] = 99;
+            u[8] = -0.555;
+            u[9] = -.123;
+
+            if (ss->format == PA_SAMPLE_FLOAT32RE)
+                for (i = 0; i < 10; i++)
+                    u[i] = swap_float(u[i]);
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_memblock_release(r);
+
+    return r;
+}
+
+int main(int argc, char *argv[]) {
+    pa_mempool *pool;
+    pa_sample_spec a;
+    pa_cvolume v;
+
+    oil_init();
+    pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+    pa_assert_se(pool = pa_mempool_new(FALSE));
+
+    a.channels = 1;
+    a.rate = 44100;
+
+    v.channels = a.channels;
+    v.values[0] = pa_sw_volume_from_linear(0.9);
+
+    for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) {
+        pa_memchunk i, j, k;
+        pa_mix_info m[2];
+        void *ptr;
+
+        printf("=== mixing: %s\n", pa_sample_format_to_string(a.format));
+
+        /* Generate block */
+        i.memblock = generate_block(pool, &a);
+        i.length = pa_memblock_get_length(i.memblock);
+        i.index = 0;
+
+        /* Make a copy */
+        j = i;
+        pa_memblock_ref(j.memblock);
+        pa_memchunk_make_writable(&j, 0);
+
+        /* Adjust volume of the copy */
+        pa_volume_memchunk(&j, &a, &v);
+
+        m[0].chunk = i;
+        m[0].volume.values[0] = PA_VOLUME_NORM;
+        m[0].volume.channels = a.channels;
+        m[1].chunk = j;
+        m[1].volume.values[0] = PA_VOLUME_NORM;
+        m[1].volume.channels = a.channels;
+
+        k.memblock = pa_memblock_new(pool, i.length);
+        k.length = i.length;
+        k.index = 0;
+
+        ptr = (uint8_t*) pa_memblock_acquire(k.memblock) + k.index;
+        pa_mix(m, 2, ptr, k.length, &a, NULL, FALSE);
+        pa_memblock_release(k.memblock);
+
+        dump_block(&a, &i);
+        dump_block(&a, &j);
+        dump_block(&a, &k);
+
+        pa_memblock_unref(i.memblock);
+        pa_memblock_unref(j.memblock);
+        pa_memblock_unref(k.memblock);
+    }
+
+    pa_mempool_free(pool);
+
+    return 0;
+}
similarity index 64%
rename from polyp/pacat-simple.c
rename to src/tests/pacat-simple.c
index 09cf4d1a191992374b3ec05eeef841e9c20ca1ae..b26e4b68dc1040613a338882aa38a2518e98652b 100644 (file)
@@ -1,20 +1,18 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <fcntl.h>
 
-#include <polyp/polyplib-simple.h>
-#include <polyp/polyplib-error.h>
+#include <pulse/simple.h>
+#include <pulse/error.h>
+#include <pulse/gccmacro.h>
 
 #define BUFSIZE 1024
 
-int main(int argc, char*argv[]) {
+int main(PA_GCC_UNUSED int argc, char*argv[]) {
 
     /* The Sample format to use */
-    static const struct pa_sample_spec ss = {
+    static const pa_sample_spec ss = {
         .format = PA_SAMPLE_S16LE,
         .rate = 44100,
         .channels = 2
     };
-    
-    struct pa_simple *s = NULL;
+
+    pa_simple *s = NULL;
     int ret = 1;
     int error;
 
+    /* replace STDIN with the specified file if needed */
+    if (argc > 1) {
+        int fd;
+
+        if ((fd = open(argv[1], O_RDONLY)) < 0) {
+            fprintf(stderr, __FILE__": open() failed: %s\n", strerror(errno));
+            goto finish;
+        }
+
+        if (dup2(fd, STDIN_FILENO) < 0) {
+            fprintf(stderr, __FILE__": dup2() failed: %s\n", strerror(errno));
+            goto finish;
+        }
+
+        close(fd);
+    }
+
     /* Create a new playback stream */
-    if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, PA_VOLUME_NORM, &error))) {
+    if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error))) {
         fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
         goto finish;
     }
@@ -59,8 +76,8 @@ int main(int argc, char*argv[]) {
 #if 0
         pa_usec_t latency;
 
-        if ((latency = pa_simple_get_playback_latency(s, &error)) == (pa_usec_t) -1) {
-            fprintf(stderr, __FILE__": pa_simple_get_playback_latency() failed: %s\n", pa_strerror(error));
+        if ((latency = pa_simple_get_latency(s, &error)) == (pa_usec_t) -1) {
+            fprintf(stderr, __FILE__": pa_simple_get_latency() failed: %s\n", pa_strerror(error));
             goto finish;
         }
 
@@ -71,7 +88,7 @@ int main(int argc, char*argv[]) {
         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;
         }
@@ -95,6 +112,6 @@ finish:
 
     if (s)
         pa_simple_free(s);
-    
+
     return ret;
 }
similarity index 79%
rename from polyp/parec-simple.c
rename to src/tests/parec-simple.c
index 130627d741a02242f7bf6a7832b78c434498b820..6c0d529b9194dd6a639aaf996b4b7112b785c368 100644 (file)
@@ -1,20 +1,18 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
@@ -28,8 +26,9 @@
 #include <string.h>
 #include <errno.h>
 
-#include <polyp/polyplib-simple.h>
-#include <polyp/polyplib-error.h>
+#include <pulse/simple.h>
+#include <pulse/error.h>
+#include <pulse/gccmacro.h>
 
 #define BUFSIZE 1024
 
@@ -45,28 +44,28 @@ static ssize_t loop_write(int fd, const void*data, size_t size) {
 
         if (r == 0)
             break;
-        
+
         ret += r;
-        data = (uint8_t*) data + r;
+        data = (const uint8_t*) data + r;
         size -= r;
     }
 
     return ret;
 }
 
-int main(int argc, char*argv[]) {
+int main(PA_GCC_UNUSED int argc, char*argv[]) {
     /* The sample type to use */
-    static const struct pa_sample_spec ss = {
+    static const pa_sample_spec ss = {
         .format = PA_SAMPLE_S16LE,
         .rate = 44100,
         .channels = 2
     };
-    struct pa_simple *s = NULL;
+    pa_simple *s = NULL;
     int ret = 1;
     int error;
 
     /* Create the recording stream */
-    if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, 0, &error))) {
+    if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error))) {
         fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
         goto finish;
     }
@@ -94,6 +93,6 @@ finish:
 
     if (s)
         pa_simple_free(s);
-    
+
     return ret;
 }
diff --git a/src/tests/proplist-test.c b/src/tests/proplist-test.c
new file mode 100644 (file)
index 0000000..20041af
--- /dev/null
@@ -0,0 +1,60 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/proplist.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+int main(int argc, char*argv[]) {
+    pa_proplist *a, *b;
+    char *s, *t;
+
+    a = pa_proplist_new();
+    pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_TITLE, "Brandenburgische Konzerte") == 0);
+    pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_ARTIST, "Johann Sebastian Bach") == 0);
+
+    b = pa_proplist_new();
+    pa_assert_se(pa_proplist_sets(b, PA_PROP_MEDIA_TITLE, "Goldbergvariationen") == 0);
+    pa_assert_se(pa_proplist_set(b, PA_PROP_MEDIA_ICON, "\0\1\2\3\4\5\6\7", 8) == 0);
+
+    pa_proplist_update(a, PA_UPDATE_MERGE, b);
+
+    pa_assert_se(!pa_proplist_gets(a, PA_PROP_MEDIA_ICON));
+
+    printf("%s\n", pa_strnull(pa_proplist_gets(a, PA_PROP_MEDIA_TITLE)));
+    pa_assert_se(pa_proplist_unset(b, PA_PROP_MEDIA_TITLE) == 0);
+
+    s = pa_proplist_to_string(a);
+    t = pa_proplist_to_string(b);
+    printf("---\n%s---\n%s", s, t);
+    pa_xfree(s);
+    pa_xfree(t);
+
+    pa_proplist_free(a);
+    pa_proplist_free(b);
+
+    return 0;
+}
diff --git a/src/tests/queue-test.c b/src/tests/queue-test.c
new file mode 100644 (file)
index 0000000..105f094
--- /dev/null
@@ -0,0 +1,67 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <unistd.h>
+
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+#include <pulsecore/queue.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+int main(int argc, char *argv[]) {
+    pa_queue *q;
+
+    pa_assert_se(q = pa_queue_new());
+
+    pa_assert(pa_queue_is_empty(q));
+
+    pa_queue_push(q, (void*) "eins");
+    pa_log("%s\n", (char*) pa_queue_pop(q));
+
+    pa_assert(pa_queue_is_empty(q));
+
+    pa_queue_push(q, (void*) "zwei");
+    pa_queue_push(q, (void*) "drei");
+    pa_queue_push(q, (void*) "vier");
+
+    pa_log("%s\n", (char*) pa_queue_pop(q));
+    pa_log("%s\n", (char*) pa_queue_pop(q));
+
+    pa_queue_push(q, (void*) "fuenf");
+
+    pa_log("%s\n", (char*) pa_queue_pop(q));
+    pa_log("%s\n", (char*) pa_queue_pop(q));
+
+    pa_assert(pa_queue_is_empty(q));
+
+    pa_queue_push(q, (void*) "sechs");
+    pa_queue_push(q, (void*) "sieben");
+
+    pa_queue_free(q, NULL, NULL);
+
+    return 0;
+}
diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c
new file mode 100644 (file)
index 0000000..4777c15
--- /dev/null
@@ -0,0 +1,89 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/sample.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+
+#include <liboil/liboil.h>
+
+int main(int argc, char *argv[]) {
+
+    static const pa_channel_map maps[] = {
+        { 1, { PA_CHANNEL_POSITION_MONO } },
+        { 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } },
+        { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER } },
+        { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_LFE } },
+        { 3, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_CENTER } },
+        { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE } },
+        { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_REAR_CENTER } },
+        { 4, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT } },
+        { 5, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_CENTER } },
+        { 5, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE } },
+        { 6, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE, PA_CHANNEL_POSITION_CENTER } },
+        { 8, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE, PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT } },
+        { 0, { 0 } }
+    };
+
+    unsigned i, j;
+    pa_mempool *pool;
+
+    oil_init();
+    pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+    pa_assert_se(pool = pa_mempool_new(FALSE));
+
+    for (i = 0; maps[i].channels > 0; i++)
+        for (j = 0; maps[j].channels > 0; j++) {
+            char a[PA_CHANNEL_MAP_SNPRINT_MAX], b[PA_CHANNEL_MAP_SNPRINT_MAX];
+            pa_resampler *r;
+            pa_sample_spec ss1, ss2;
+
+            pa_log_info("Converting from '%s' to '%s'.\n", pa_channel_map_snprint(a, sizeof(a), &maps[i]), pa_channel_map_snprint(b, sizeof(b), &maps[j]));
+
+            ss1.channels = maps[i].channels;
+            ss2.channels = maps[j].channels;
+
+            ss1.rate = ss2.rate = 44100;
+            ss1.format = ss2.format = PA_SAMPLE_S16NE;
+
+            r = pa_resampler_new(pool, &ss1, &maps[i], &ss2, &maps[j], PA_RESAMPLER_AUTO, 0);
+
+            /* We don't really care for the resampler. We just want to
+             * see the remixing debug output. */
+
+            pa_resampler_free(r);
+        }
+
+
+    pa_mempool_free(pool);
+
+    return 0;
+}
diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c
new file mode 100644 (file)
index 0000000..1a20be2
--- /dev/null
@@ -0,0 +1,252 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulse/sample.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/endianmacros.h>
+#include <pulsecore/memblock.h>
+#include <pulsecore/sample-util.h>
+
+#include <liboil/liboil.h>
+
+static float swap_float(float a) {
+    uint32_t *b = (uint32_t*) &a;
+    *b = PA_UINT32_SWAP(*b);
+    return a;
+}
+
+static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
+    void *d;
+    unsigned i;
+
+    d = pa_memblock_acquire(chunk->memblock);
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_U8:
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW: {
+            uint8_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("0x%02x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_S16NE:
+        case PA_SAMPLE_S16RE: {
+            uint16_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("0x%04x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S32RE: {
+            uint32_t *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++)
+                printf("0x%08x ", *(u++));
+
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32NE:
+        case PA_SAMPLE_FLOAT32RE: {
+            float *u = d;
+
+            for (i = 0; i < chunk->length / pa_frame_size(ss); i++) {
+                printf("%1.3g ",  ss->format == PA_SAMPLE_FLOAT32NE ? *u : swap_float(*u));
+                u++;
+            }
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    printf("\n");
+
+    pa_memblock_release(chunk->memblock);
+}
+
+static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
+    pa_memblock *r;
+    void *d;
+    unsigned i;
+
+    pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10));
+    d = pa_memblock_acquire(r);
+
+    switch (ss->format) {
+
+        case PA_SAMPLE_U8:
+        case PA_SAMPLE_ULAW:
+        case PA_SAMPLE_ALAW: {
+            uint8_t *u = d;
+
+            u[0] = 0x00;
+            u[1] = 0xFF;
+            u[2] = 0x7F;
+            u[3] = 0x80;
+            u[4] = 0x9f;
+            u[5] = 0x3f;
+            u[6] = 0x1;
+            u[7] = 0xF0;
+            u[8] = 0x20;
+            u[9] = 0x21;
+            break;
+        }
+
+        case PA_SAMPLE_S16NE:
+        case PA_SAMPLE_S16RE: {
+            uint16_t *u = d;
+
+            u[0] = 0x0000;
+            u[1] = 0xFFFF;
+            u[2] = 0x7FFF;
+            u[3] = 0x8000;
+            u[4] = 0x9fff;
+            u[5] = 0x3fff;
+            u[6] = 0x1;
+            u[7] = 0xF000;
+            u[8] = 0x20;
+            u[9] = 0x21;
+            break;
+        }
+
+        case PA_SAMPLE_S32NE:
+        case PA_SAMPLE_S32RE: {
+            uint32_t *u = d;
+
+            u[0] = 0x00000001;
+            u[1] = 0xFFFF0002;
+            u[2] = 0x7FFF0003;
+            u[3] = 0x80000004;
+            u[4] = 0x9fff0005;
+            u[5] = 0x3fff0006;
+            u[6] =    0x10007;
+            u[7] = 0xF0000008;
+            u[8] =   0x200009;
+            u[9] =   0x21000A;
+            break;
+        }
+
+        case PA_SAMPLE_FLOAT32NE:
+        case PA_SAMPLE_FLOAT32RE: {
+            float *u = d;
+
+            u[0] = 0.0;
+            u[1] = -1.0;
+            u[2] = 1.0;
+            u[3] = 4711;
+            u[4] = 0.222;
+            u[5] = 0.33;
+            u[6] = -.3;
+            u[7] = 99;
+            u[8] = -0.555;
+            u[9] = -.123;
+
+            if (ss->format == PA_SAMPLE_FLOAT32RE)
+                for (i = 0; i < 10; i++)
+                    u[i] = swap_float(u[i]);
+
+            break;
+        }
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_memblock_release(r);
+
+    return r;
+}
+
+int main(int argc, char *argv[]) {
+    pa_mempool *pool;
+    pa_sample_spec a, b;
+    pa_cvolume v;
+
+    oil_init();
+    pa_log_set_maximal_level(PA_LOG_DEBUG);
+
+    pa_assert_se(pool = pa_mempool_new(FALSE));
+
+    a.channels = b.channels = 1;
+    a.rate = b.rate = 44100;
+
+    v.channels = a.channels;
+    v.values[0] = pa_sw_volume_from_linear(0.5);
+
+    for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) {
+        for (b.format = 0; b.format < PA_SAMPLE_MAX; b.format ++) {
+
+            pa_resampler *forth, *back;
+            pa_memchunk i, j, k;
+
+            printf("=== %s -> %s -> %s -> /2\n",
+                   pa_sample_format_to_string(a.format),
+                   pa_sample_format_to_string(b.format),
+                   pa_sample_format_to_string(a.format));
+
+            pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, PA_RESAMPLER_AUTO, 0));
+            pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, PA_RESAMPLER_AUTO, 0));
+
+            i.memblock = generate_block(pool, &a);
+            i.length = pa_memblock_get_length(i.memblock);
+            i.index = 0;
+            pa_resampler_run(forth, &i, &j);
+            pa_resampler_run(back, &j, &k);
+
+            dump_block(&a, &i);
+            dump_block(&b, &j);
+            dump_block(&a, &k);
+
+            pa_memblock_unref(j.memblock);
+            pa_memblock_unref(k.memblock);
+
+            pa_volume_memchunk(&i, &a, &v);
+            dump_block(&a, &i);
+
+            pa_memblock_unref(i.memblock);
+
+            pa_resampler_free(forth);
+            pa_resampler_free(back);
+        }
+    }
+
+    pa_mempool_free(pool);
+
+    return 0;
+}
diff --git a/src/tests/rtpoll-test.c b/src/tests/rtpoll-test.c
new file mode 100644 (file)
index 0000000..953fd61
--- /dev/null
@@ -0,0 +1,91 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <poll.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/rtsig.h>
+
+static int before(pa_rtpoll_item *i) {
+    pa_log("before");
+    return 0;
+}
+
+static void after(pa_rtpoll_item *i) {
+    pa_log("after");
+}
+
+static int worker(pa_rtpoll_item *w) {
+    pa_log("worker");
+    return 0;
+}
+
+int main(int argc, char *argv[]) {
+    pa_rtpoll *p;
+    pa_rtpoll_item *i, *w;
+    struct pollfd *pollfd;
+
+#ifdef SIGRTMIN
+    pa_rtsig_configure(SIGRTMIN+10, SIGRTMAX);
+#endif
+
+    p = pa_rtpoll_new();
+
+    i = pa_rtpoll_item_new(p, PA_RTPOLL_EARLY, 1);
+    pa_rtpoll_item_set_before_callback(i, before);
+    pa_rtpoll_item_set_after_callback(i, after);
+
+    pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+    pollfd->fd = 0;
+    pollfd->events = POLLIN;
+
+    w = pa_rtpoll_item_new(p, PA_RTPOLL_NORMAL, 0);
+    pa_rtpoll_item_set_before_callback(w, worker);
+
+    pa_rtpoll_install(p);
+    pa_rtpoll_set_timer_relative(p, 10000000); /* 10 s */
+
+    pa_rtpoll_run(p, 1);
+
+    pa_rtpoll_item_free(i);
+
+    i = pa_rtpoll_item_new(p, PA_RTPOLL_EARLY, 1);
+    pa_rtpoll_item_set_before_callback(i, before);
+    pa_rtpoll_item_set_after_callback(i, after);
+
+    pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
+    pollfd->fd = 0;
+    pollfd->events = POLLIN;
+
+    pa_rtpoll_run(p, 1);
+
+    pa_rtpoll_item_free(i);
+
+    pa_rtpoll_item_free(w);
+
+    pa_rtpoll_free(p);
+
+    return 0;
+}
diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c
new file mode 100644 (file)
index 0000000..91e85c3
--- /dev/null
@@ -0,0 +1,117 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; 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 <unistd.h>
+#include <time.h>
+#include <sched.h>
+#include <inttypes.h>
+#include <string.h>
+#include <pthread.h>
+
+#include <pulse/timeval.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+static int msec_lower, msec_upper;
+
+static void* work(void *p) PA_GCC_NORETURN;
+
+static void* work(void *p) {
+    cpu_set_t mask;
+    struct sched_param param;
+
+    pa_log_notice("CPU%i: Created thread.", PA_PTR_TO_INT(p));
+
+    memset(&param, 0, sizeof(param));
+    param.sched_priority = 12;
+    pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) == 0);
+
+    CPU_ZERO(&mask);
+    CPU_SET(PA_PTR_TO_INT(p), &mask);
+    pa_assert_se(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) == 0);
+
+    for (;;) {
+        struct timespec now, end;
+        uint64_t nsec;
+
+        pa_log_notice("CPU%i: Sleeping for 1s", PA_PTR_TO_INT(p));
+        sleep(1);
+
+        pa_assert_se(clock_gettime(CLOCK_REALTIME, &end) == 0);
+
+        nsec =
+            (uint64_t) ((((double) rand())*(msec_upper-msec_lower)*PA_NSEC_PER_MSEC)/RAND_MAX) +
+            (uint64_t) (msec_lower*PA_NSEC_PER_MSEC);
+
+        pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_INT(p), (int) (nsec/PA_NSEC_PER_MSEC));
+
+        end.tv_sec += nsec / PA_NSEC_PER_SEC;
+        end.tv_nsec += nsec % PA_NSEC_PER_SEC;
+
+        while ((pa_usec_t) end.tv_nsec > PA_NSEC_PER_SEC) {
+            end.tv_sec++;
+            end.tv_nsec -= PA_NSEC_PER_SEC;
+        }
+
+        do {
+            pa_assert_se(clock_gettime(CLOCK_REALTIME, &now) == 0);
+        } while (now.tv_sec < end.tv_sec ||
+                 (now.tv_sec == end.tv_sec && now.tv_nsec < end.tv_nsec));
+    }
+}
+
+int main(int argc, char*argv[]) {
+    int n;
+
+    srand(time(NULL));
+
+    if (argc >= 3) {
+        msec_lower = atoi(argv[1]);
+        msec_upper = atoi(argv[2]);
+    } else if (argc >= 2) {
+        msec_lower = 0;
+        msec_upper = atoi(argv[1]);
+    } else {
+        msec_lower = 0;
+        msec_upper = 1000;
+    }
+
+    pa_assert(msec_upper > 0);
+    pa_assert(msec_upper >= msec_lower);
+
+    pa_log_notice("Creating random latencies in the range of %ims to  %ims.", msec_lower, msec_upper);
+
+    for (n = 1; n < sysconf(_SC_NPROCESSORS_CONF); n++) {
+        pthread_t t;
+        pa_assert_se(pthread_create(&t, NULL, work, PA_INT_TO_PTR(n)) == 0);
+    }
+
+    work(PA_INT_TO_PTR(0));
+
+    return 0;
+}
diff --git a/src/tests/sig2str-test.c b/src/tests/sig2str-test.c
new file mode 100644 (file)
index 0000000..d64a890
--- /dev/null
@@ -0,0 +1,37 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <stdio.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+int main(int argc, char *argv[]) {
+    int sig;
+
+    for (sig = -1; sig <= NSIG; sig++)
+        printf("%i = %s\n", sig, pa_sig2str(sig));
+
+    return 0;
+}
diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c
new file mode 100644 (file)
index 0000000..b78f3c9
--- /dev/null
@@ -0,0 +1,78 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulsecore/time-smoother.h>
+#include <pulse/timeval.h>
+
+int main(int argc, char*argv[]) {
+    pa_usec_t x;
+    unsigned u = 0;
+    pa_smoother *s;
+    int m;
+
+/*     unsigned msec[] = { */
+/*         200, 200, */
+/*         300, 320, */
+/*         400, 400, */
+/*         500, 480, */
+/*         0, 0 */
+/*     }; */
+
+    int msec[200];
+
+    srand(0);
+
+    for (m = 0, u = 0; u < PA_ELEMENTSOF(msec); u+= 2) {
+
+        msec[u] = m+1 + (rand() % 100) - 50;
+        msec[u+1] = m + (rand() % 2000) - 1000;
+
+        m += rand() % 100;
+
+        if (msec[u] < 0)
+            msec[u] = 0;
+
+        if (msec[u+1] < 0)
+            msec[u+1] = 0;
+    }
+
+    s = pa_smoother_new(700*PA_USEC_PER_MSEC, 2000*PA_USEC_PER_MSEC, TRUE, 6);
+
+    for (x = 0, u = 0; x < PA_USEC_PER_SEC * 10; x += PA_USEC_PER_MSEC) {
+
+        while (u < PA_ELEMENTSOF(msec) && (pa_usec_t) msec[u]*PA_USEC_PER_MSEC < x) {
+            pa_smoother_put(s, msec[u]*PA_USEC_PER_MSEC, msec[u+1]*PA_USEC_PER_MSEC);
+            printf("%i\t\t%i\n", msec[u],  msec[u+1]);
+            u += 2;
+        }
+
+        printf("%llu\t%llu\n", (unsigned long long) (x/PA_USEC_PER_MSEC), (unsigned long long) (pa_smoother_get(s, x)/PA_USEC_PER_MSEC));
+    }
+
+    pa_smoother_free(s);
+
+    return 0;
+}
diff --git a/src/tests/stripnul.c b/src/tests/stripnul.c
new file mode 100644 (file)
index 0000000..0ab0677
--- /dev/null
@@ -0,0 +1,70 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <inttypes.h>
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/macro.h>
+
+int main(int argc, char *argv[]) {
+    FILE *i, *o;
+    size_t granularity;
+    pa_bool_t found;
+    uint8_t *zero;
+
+    pa_assert_se(argc >= 2);
+    pa_assert_se((granularity = atoi(argv[1])) >= 1);
+    pa_assert_se((i = (argc >= 3) ? fopen(argv[2], "r") : stdin));
+    pa_assert_se((o = (argc >= 4) ? fopen(argv[3], "w") : stdout));
+
+    zero = pa_xmalloc0(granularity);
+
+    for (;;) {
+        uint8_t buffer[16*1024], *p;
+        size_t k;
+
+        k = fread(buffer, granularity, sizeof(buffer)/granularity, i);
+
+        if (k <= 0)
+            break;
+
+        if (found)
+            pa_assert_se(fwrite(buffer, granularity, k, o) == k);
+        else {
+            for (p = buffer; (p-buffer)/granularity < k; p += granularity)
+                if (memcmp(p, zero, granularity)) {
+                    size_t left;
+                    found = TRUE;
+                    left = k - (p-buffer)/granularity;
+                    pa_assert_se(fwrite(p, granularity, left, o) == left);
+                    break;
+                }
+        }
+    }
+
+    fflush(o);
+
+    return 0;
+}
similarity index 79%
rename from polyp/strlist-test.c
rename to src/tests/strlist-test.c
index b68a0415418e94ff89c9df3fdac2d2b6ef314ebe..2bd1645c0436bca7c30358794b06ca5817ea744f 100644 (file)
@@ -1,11 +1,13 @@
 #include <stdio.h>
 
-#include "strlist.h"
-#include "xmalloc.h"
+#include <pulse/xmalloc.h>
+#include <pulse/gccmacro.h>
 
-int main(int argc, char* argv[]) {
+#include <pulsecore/strlist.h>
+
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char* argv[]) {
     char *t, *u;
-    struct pa_strlist *l = NULL;
+    pa_strlist *l = NULL;
 
     l = pa_strlist_prepend(l, "e");
     l = pa_strlist_prepend(l, "d");
@@ -15,7 +17,7 @@ int main(int argc, char* argv[]) {
 
     t = pa_strlist_tostring(l);
     pa_strlist_free(l);
-    
+
     fprintf(stderr, "1: %s\n", t);
 
     l = pa_strlist_parse(t);
@@ -28,9 +30,9 @@ int main(int argc, char* argv[]) {
     l = pa_strlist_pop(l, &u);
     fprintf(stderr, "3: %s\n", u);
     pa_xfree(u);
-    
+
     l = pa_strlist_remove(l, "c");
-    
+
     t = pa_strlist_tostring(l);
     fprintf(stderr, "4: %s\n", t);
     pa_xfree(t);
diff --git a/src/tests/sync-playback.c b/src/tests/sync-playback.c
new file mode 100644 (file)
index 0000000..7ab3a25
--- /dev/null
@@ -0,0 +1,190 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <math.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/mainloop.h>
+
+#define NSTREAMS 4
+#define SINE_HZ 440
+#define SAMPLE_HZ 8000
+
+static pa_context *context = NULL;
+static pa_stream *streams[NSTREAMS];
+static pa_mainloop_api *mainloop_api = NULL;
+
+static float data[SAMPLE_HZ]; /* one second space */
+
+static int n_streams_ready = 0;
+
+static const pa_sample_spec sample_spec = {
+    .format = PA_SAMPLE_FLOAT32,
+    .rate = SAMPLE_HZ,
+    .channels = 1
+};
+
+static const pa_buffer_attr buffer_attr = {
+    .maxlength = SAMPLE_HZ*sizeof(float)*NSTREAMS, /* exactly space for the entire play time */
+    .tlength = 0,
+    .prebuf = 0, /* Setting prebuf to 0 guarantees us the the streams will run synchronously, no matter what */
+    .minreq = 0
+};
+
+static void nop_free_cb(void *p) {}
+
+static void underflow_cb(struct pa_stream *s, void *userdata) {
+    int i = (int) (long) userdata;
+
+    fprintf(stderr, "Stream %i finished\n", i);
+
+    if (++n_streams_ready >= 2*NSTREAMS) {
+        fprintf(stderr, "We're done\n");
+        mainloop_api->quit(mainloop_api, 0);
+    }
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    assert(s);
+
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+        case PA_STREAM_TERMINATED:
+            break;
+
+        case PA_STREAM_READY: {
+
+            int r, i = (int) (long) userdata;
+
+            fprintf(stderr, "Writing data to stream %i.\n", i);
+
+            r = pa_stream_write(s, data, sizeof(data), nop_free_cb, sizeof(data) * i, PA_SEEK_ABSOLUTE);
+            assert(r == 0);
+
+            /* Be notified when this stream is drained */
+            pa_stream_set_underflow_callback(s, underflow_cb, userdata);
+
+            /* All streams have been set up, let's go! */
+            if (++n_streams_ready >= NSTREAMS) {
+                fprintf(stderr, "Uncorking\n");
+                pa_operation_unref(pa_stream_cork(s, 0, NULL, NULL));
+            }
+
+            break;
+        }
+
+        default:
+        case PA_STREAM_FAILED:
+            fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+            abort();
+    }
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+
+            int i;
+            fprintf(stderr, "Connection established.\n");
+
+            for (i = 0; i < NSTREAMS; i++) {
+                char name[64];
+
+                fprintf(stderr, "Creating stream %i\n", i);
+
+                snprintf(name, sizeof(name), "stream #%i", i);
+
+                streams[i] = pa_stream_new(c, name, &sample_spec, NULL);
+                assert(streams[i]);
+                pa_stream_set_state_callback(streams[i], stream_state_callback, (void*) (long) i);
+                pa_stream_connect_playback(streams[i], NULL, &buffer_attr, PA_STREAM_START_CORKED, NULL, i == 0 ? NULL : streams[0]);
+            }
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            mainloop_api->quit(mainloop_api, 0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
+            abort();
+    }
+}
+
+int main(int argc, char *argv[]) {
+    pa_mainloop* m = NULL;
+    int i, ret = 0;
+
+    for (i = 0; i < SAMPLE_HZ; i++)
+        data[i] = (float) sin(((double) i/SAMPLE_HZ)*2*M_PI*SINE_HZ)/2;
+
+    for (i = 0; i < NSTREAMS; i++)
+        streams[i] = NULL;
+
+    /* Set up a new main loop */
+    m = pa_mainloop_new();
+    assert(m);
+
+    mainloop_api = pa_mainloop_get_api(m);
+
+    context = pa_context_new(mainloop_api, argv[0]);
+    assert(context);
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+
+    pa_context_connect(context, NULL, 0, NULL);
+
+    if (pa_mainloop_run(m, &ret) < 0)
+        fprintf(stderr, "pa_mainloop_run() failed.\n");
+
+    pa_context_unref(context);
+
+    for (i = 0; i < NSTREAMS; i++)
+        if (streams[i])
+            pa_stream_unref(streams[i]);
+
+    pa_mainloop_free(m);
+
+    return ret;
+}
diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c
new file mode 100644 (file)
index 0000000..7a62f85
--- /dev/null
@@ -0,0 +1,77 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <unistd.h>
+#include <stdio.h>
+
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/thread-mainloop.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
+
+static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    pa_assert_se(pa_threaded_mainloop_in_thread(userdata));
+    fprintf(stderr, "TIME EVENT START\n");
+    pa_threaded_mainloop_signal(userdata, 1);
+    fprintf(stderr, "TIME EVENT END\n");
+}
+
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
+    pa_mainloop_api *a;
+    pa_threaded_mainloop *m;
+    struct timeval tv;
+
+    pa_assert_se(m = pa_threaded_mainloop_new());
+    pa_assert_se(a = pa_threaded_mainloop_get_api(m));
+
+    pa_threaded_mainloop_start(m);
+
+    pa_threaded_mainloop_lock(m);
+
+    pa_assert_se(!pa_threaded_mainloop_in_thread(m));
+
+    pa_gettimeofday(&tv);
+    tv.tv_sec += 5;
+    a->time_new(a, &tv, tcb, m);
+
+    fprintf(stderr, "waiting 5s (signal)\n");
+    pa_threaded_mainloop_wait(m);
+    fprintf(stderr, "wait completed\n");
+    pa_threaded_mainloop_accept(m);
+    fprintf(stderr, "signal accepted\n");
+
+    pa_threaded_mainloop_unlock(m);
+
+    fprintf(stderr, "waiting 5s (sleep)\n");
+    pa_msleep(5000);
+
+    fprintf(stderr, "shutting down\n");
+
+    pa_threaded_mainloop_stop(m);
+
+    pa_threaded_mainloop_free(m);
+    return 0;
+}
diff --git a/src/tests/thread-test.c b/src/tests/thread-test.c
new file mode 100644 (file)
index 0000000..f29b5e7
--- /dev/null
@@ -0,0 +1,142 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <pulsecore/thread.h>
+#include <pulsecore/mutex.h>
+#include <pulsecore/once.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulse/xmalloc.h>
+
+static pa_mutex *mutex = NULL;
+static pa_cond *cond1 = NULL, *cond2 = NULL;
+static pa_tls *tls = NULL;
+
+static int magic_number = 0;
+
+#define THREADS_MAX 20
+
+static void once_func(void) {
+    pa_log("once!");
+}
+
+static pa_once once = PA_ONCE_INIT;
+
+static void thread_func(void *data) {
+    pa_tls_set(tls, data);
+
+    pa_log("thread_func() for %s starting...", (char*) pa_tls_get(tls));
+
+    pa_mutex_lock(mutex);
+
+    for (;;) {
+        int k, n;
+
+        pa_log("%s waiting ...", (char*) pa_tls_get(tls));
+
+        for (;;) {
+
+            if (magic_number < 0)
+                goto quit;
+
+            if (magic_number != 0)
+                break;
+
+            pa_cond_wait(cond1, mutex);
+        }
+
+        k = magic_number;
+        magic_number = 0;
+
+        pa_mutex_unlock(mutex);
+
+        pa_run_once(&once, once_func);
+
+        pa_cond_signal(cond2, 0);
+
+        pa_log("%s got number %i", (char*) pa_tls_get(tls), k);
+
+        /* Spin! */
+        for (n = 0; n < k; n++)
+            pa_thread_yield();
+
+        pa_mutex_lock(mutex);
+    }
+
+quit:
+
+    pa_mutex_unlock(mutex);
+
+    pa_log("thread_func() for %s done...", (char*) pa_tls_get(tls));
+}
+
+int main(int argc, char *argv[]) {
+    int i, k;
+    pa_thread* t[THREADS_MAX];
+
+    assert(pa_thread_is_running(pa_thread_self()));
+
+    mutex = pa_mutex_new(FALSE, FALSE);
+    cond1 = pa_cond_new();
+    cond2 = pa_cond_new();
+    tls = pa_tls_new(pa_xfree);
+
+    for (i = 0; i < THREADS_MAX; i++) {
+        t[i] = pa_thread_new(thread_func, pa_sprintf_malloc("Thread #%i", i+1));
+        assert(t[i]);
+    }
+
+    pa_mutex_lock(mutex);
+
+    pa_log("loop-init");
+
+    for (k = 0; k < 100; k++) {
+        assert(magic_number == 0);
+
+
+        magic_number = (int) rand() % 0x10000;
+
+        pa_log("iteration %i (%i)", k, magic_number);
+
+        pa_cond_signal(cond1, 0);
+
+        pa_cond_wait(cond2, mutex);
+    }
+
+    pa_log("loop-exit");
+
+    magic_number = -1;
+    pa_cond_signal(cond1, 1);
+
+    pa_mutex_unlock(mutex);
+
+    for (i = 0; i < THREADS_MAX; i++)
+        pa_thread_free(t[i]);
+
+    pa_mutex_free(mutex);
+    pa_cond_free(cond1);
+    pa_cond_free(cond2);
+    pa_tls_free(tls);
+
+    return 0;
+}
diff --git a/src/tests/utf8-test.c b/src/tests/utf8-test.c
new file mode 100644 (file)
index 0000000..f1708ad
--- /dev/null
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <assert.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+int main(int argc, char *argv[]) {
+    char *c;
+
+    assert(pa_utf8_valid("hallo"));
+    assert(pa_utf8_valid("hallo\n"));
+    assert(!pa_utf8_valid("hüpfburg\n"));
+    assert(pa_utf8_valid("hallo\n"));
+    assert(pa_utf8_valid("hüpfburg\n"));
+
+    printf("LATIN1: %s\n", c = pa_utf8_filter("hüpfburg"));
+    pa_xfree(c);
+    printf("UTF8: %sx\n", c = pa_utf8_filter("hüpfburg"));
+    pa_xfree(c);
+    printf("LATIN1: %sx\n", c = pa_utf8_filter("üxknärzmörzeltörszß³§dsjkfh"));
+    pa_xfree(c);
+
+    return 0;
+}
similarity index 76%
rename from polyp/voltest.c
rename to src/tests/voltest.c
index 0c4e232662d5329cf67e1ada1e8deb5f5a8d55ec..d2c0ff69b917684f80c887b44490d8622088a1c8 100644 (file)
@@ -1,19 +1,20 @@
-/* $Id$ */
-
 #include <stdio.h>
 
-#include <polyp/volume.h>
+#include <pulse/volume.h>
+#include <pulse/gccmacro.h>
 
-int main() {
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) {
     pa_volume_t v;
 
     for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) {
 
         double dB = pa_sw_volume_to_dB(v);
         double f = pa_sw_volume_to_linear(v);
-        
+
         printf("Volume: %3i; percent: %i%%; decibel %0.2f; linear = %0.2f; volume(decibel): %3i; volume(linear): %3i\n",
                v, (v*100)/PA_VOLUME_NORM, dB, f, pa_sw_volume_from_dB(dB), pa_sw_volume_from_linear(f));
 
     }
+
+    return 0;
 }
diff --git a/src/utils/Makefile b/src/utils/Makefile
new file mode 120000 (symlink)
index 0000000..c110232
--- /dev/null
@@ -0,0 +1 @@
+../pulse/Makefile
\ No newline at end of file
similarity index 61%
rename from polyp/pabrowse.c
rename to src/utils/pabrowse.c
index ccea0cd419cd7a4da82026b343b3e5b122b8432b..f2ed9553f99cfdc2cf614984511c78eb25a77db9 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <assert.h>
 #include <signal.h>
 
-#include <polyp/mainloop.h>
-#include <polyp/mainloop-signal.h>
-#include <polyp/polyplib-browser.h>
-#include <polyp/typeid.h>
+#include <pulse/pulseaudio.h>
+#include <pulse/browser.h>
 
-static void exit_signal_callback(struct pa_mainloop_api*m, struct pa_signal_event *e, int sig, void *userdata) {
+static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
     fprintf(stderr, "Got signal, exiting\n");
     m->quit(m, 0);
 }
 
-static void dump_server(const struct pa_browse_info *i) {
+static void dump_server(const pa_browse_info *i) {
     char t[16];
 
     if (i->cookie)
         snprintf(t, sizeof(t), "0x%08x", *i->cookie);
-    
+
     printf("server: %s\n"
            "server-version: %s\n"
            "user-name: %s\n"
@@ -55,27 +53,22 @@ static void dump_server(const struct pa_browse_info *i) {
            i->cookie ? t : "n/a");
 }
 
-static void dump_device(const struct pa_browse_info *i) {
-    char t[16], ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
+static void dump_device(const pa_browse_info *i) {
+    char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
 
     if (i->sample_spec)
         pa_sample_spec_snprint(ss, sizeof(ss), i->sample_spec);
 
-    if (i->typeid)
-        pa_typeid_to_string(*i->typeid, t, sizeof(t));
-    
     printf("device: %s\n"
            "description: %s\n"
-           "type: %s\n"
            "sample spec: %s\n",
            i->device,
            i->description ? i->description : "n/a",
-           i->typeid ? t : "n/a",
            i->sample_spec ? ss : "n/a");
-           
+
 }
 
-static void browser_callback(struct pa_browser *b, enum pa_browse_opcode c, const struct pa_browse_info *i, void *userdata) {
+static void browser_callback(pa_browser *b, pa_browse_opcode_t c, const pa_browse_info *i, void *userdata) {
     assert(b && i);
 
     switch (c) {
@@ -96,21 +89,36 @@ static void browser_callback(struct pa_browser *b, enum pa_browse_opcode c, cons
             dump_server(i);
             dump_device(i);
             break;
-            
-        case PA_BROWSE_REMOVE:
-            printf("\n=> removed service <%s>\n", i->name);
+
+        case PA_BROWSE_REMOVE_SERVER:
+            printf("\n=> removed server <%s>\n", i->name);
+            break;
+
+        case PA_BROWSE_REMOVE_SINK:
+            printf("\n=> removed sink <%s>\n", i->name);
+            break;
+
+        case PA_BROWSE_REMOVE_SOURCE:
+            printf("\n=> removed source <%s>\n", i->name);
             break;
-            
+
         default:
             ;
     }
 }
 
+static void error_callback(pa_browser *b, const char *s, void *userdata) {
+    pa_mainloop_api*m = userdata;
+
+    fprintf(stderr, "Failure: %s\n", s);
+    m->quit(m, 1);
+}
 
 int main(int argc, char *argv[]) {
-    struct pa_mainloop *mainloop = NULL;
-    struct pa_browser *browser = NULL;
+    pa_mainloop *mainloop = NULL;
+    pa_browser *browser = NULL;
     int ret = 1, r;
+    const char *s;
 
     if (!(mainloop = pa_mainloop_new()))
         goto finish;
@@ -120,16 +128,23 @@ int main(int argc, char *argv[]) {
     pa_signal_new(SIGINT, exit_signal_callback, NULL);
     pa_signal_new(SIGTERM, exit_signal_callback, NULL);
     signal(SIGPIPE, SIG_IGN);
-    
-    if (!(browser = pa_browser_new(pa_mainloop_get_api(mainloop))))
+
+    if (!(browser = pa_browser_new_full(pa_mainloop_get_api(mainloop), PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, &s))) {
+        fprintf(stderr, "pa_browse_new_full(): %s\n", s);
         goto finish;
+    }
 
     pa_browser_set_callback(browser, browser_callback, NULL);
-    
+    pa_browser_set_error_callback(browser, error_callback, pa_mainloop_get_api(mainloop));
+
     ret = 0;
     pa_mainloop_run(mainloop, &ret);
 
 finish:
+
+    if (browser)
+        pa_browser_unref(browser);
+
     if (mainloop) {
         pa_signal_done();
         pa_mainloop_free(mainloop);
diff --git a/src/utils/pacat.c b/src/utils/pacat.c
new file mode 100644 (file)
index 0000000..ee784a9
--- /dev/null
@@ -0,0 +1,823 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <fcntl.h>
+
+#include <pulse/pulseaudio.h>
+
+#define TIME_EVENT_USEC 50000
+
+#if PA_API_VERSION < 10
+#error Invalid PulseAudio API version
+#endif
+
+#define CLEAR_LINE "\x1B[K"
+
+static enum { RECORD, PLAYBACK } mode = PLAYBACK;
+
+static pa_context *context = NULL;
+static pa_stream *stream = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+
+static void *buffer = NULL;
+static size_t buffer_length = 0, buffer_index = 0;
+
+static pa_io_event* stdio_event = NULL;
+
+static char *stream_name = NULL, *client_name = NULL, *device = NULL;
+
+static int verbose = 0;
+static pa_volume_t volume = PA_VOLUME_NORM;
+
+static pa_sample_spec sample_spec = {
+    .format = PA_SAMPLE_S16LE,
+    .rate = 44100,
+    .channels = 2
+};
+
+static pa_channel_map channel_map;
+static int channel_map_set = 0;
+
+static pa_stream_flags_t flags = 0;
+
+static size_t latency = 0, process_time=0;
+
+/* A shortcut for terminating the application */
+static void quit(int ret) {
+    assert(mainloop_api);
+    mainloop_api->quit(mainloop_api, ret);
+}
+
+/* Write some data to the stream */
+static void do_stream_write(size_t length) {
+    size_t l;
+    assert(length);
+
+    if (!buffer || !buffer_length)
+        return;
+
+    l = length;
+    if (l > buffer_length)
+        l = buffer_length;
+
+    if (pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0, PA_SEEK_RELATIVE) < 0) {
+        fprintf(stderr, "pa_stream_write() failed: %s\n", pa_strerror(pa_context_errno(context)));
+        quit(1);
+        return;
+    }
+
+    buffer_length -= l;
+    buffer_index += l;
+
+    if (!buffer_length) {
+        pa_xfree(buffer);
+        buffer = NULL;
+        buffer_index = buffer_length = 0;
+    }
+}
+
+/* This is called whenever new data may be written to the stream */
+static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
+    assert(s);
+    assert(length > 0);
+
+    if (stdio_event)
+        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
+
+    if (!buffer)
+        return;
+
+    do_stream_write(length);
+}
+
+/* This is called whenever new data may is available */
+static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
+    const void *data;
+    assert(s);
+    assert(length > 0);
+
+    if (stdio_event)
+        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
+
+    if (pa_stream_peek(s, &data, &length) < 0) {
+        fprintf(stderr, "pa_stream_peek() failed: %s\n", pa_strerror(pa_context_errno(context)));
+        quit(1);
+        return;
+    }
+
+    assert(data);
+    assert(length > 0);
+
+    if (buffer) {
+        fprintf(stderr, "Buffer overrun, dropping incoming data\n");
+        if (pa_stream_drop(s) < 0) {
+            fprintf(stderr, "pa_stream_drop() failed: %s\n", pa_strerror(pa_context_errno(context)));
+            quit(1);
+        }
+        return;
+    }
+
+    buffer = pa_xmalloc(buffer_length = length);
+    memcpy(buffer, data, length);
+    buffer_index = 0;
+    pa_stream_drop(s);
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    assert(s);
+
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_CREATING:
+        case PA_STREAM_TERMINATED:
+            break;
+
+        case PA_STREAM_READY:
+            if (verbose) {
+                const pa_buffer_attr *a;
+                char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
+                fprintf(stderr, "Stream successfully created.\n");
+
+                if (!(a = pa_stream_get_buffer_attr(s)))
+                    fprintf(stderr, "pa_stream_get_buffer_attr() failed: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+                else {
+
+                    if (mode == PLAYBACK)
+                        fprintf(stderr, "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u\n", a->maxlength, a->tlength, a->prebuf, a->minreq);
+                    else {
+                        assert(mode == RECORD);
+                        fprintf(stderr, "Buffer metrics: maxlength=%u, fragsize=%u\n", a->maxlength, a->fragsize);
+                    }
+                }
+
+                fprintf(stderr, "Using sample spec '%s', channel map '%s'.\n",
+                        pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
+                        pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
+
+                fprintf(stderr, "Connected to device %s (%u, %ssuspended).\n",
+                        pa_stream_get_device_name(s),
+                        pa_stream_get_device_index(s),
+                        pa_stream_is_suspended(s) ? "" : "not ");
+            }
+
+            break;
+
+        case PA_STREAM_FAILED:
+        default:
+            fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+            quit(1);
+    }
+}
+
+static void stream_suspended_callback(pa_stream *s, void *userdata) {
+    assert(s);
+
+    if (verbose) {
+        if (pa_stream_is_suspended(s))
+            fprintf(stderr, "Stream device suspended." CLEAR_LINE " \n");
+        else
+            fprintf(stderr, "Stream device resumed." CLEAR_LINE " \n");
+    }
+}
+
+static void stream_underflow_callback(pa_stream *s, void *userdata) {
+    assert(s);
+
+    if (verbose)
+        fprintf(stderr, "Stream underrun." CLEAR_LINE " \n");
+}
+
+static void stream_overflow_callback(pa_stream *s, void *userdata) {
+    assert(s);
+
+    if (verbose)
+        fprintf(stderr, "Stream overrun." CLEAR_LINE " \n");
+}
+
+static void stream_started_callback(pa_stream *s, void *userdata) {
+    assert(s);
+
+    if (verbose)
+        fprintf(stderr, "Stream started." CLEAR_LINE " \n");
+}
+
+static void stream_moved_callback(pa_stream *s, void *userdata) {
+    assert(s);
+
+    if (verbose)
+        fprintf(stderr, "Stream moved to device %s (%u, %ssuspended)." CLEAR_LINE " \n", pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : "not ");
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+            int r;
+            pa_buffer_attr buffer_attr;
+
+            assert(c);
+            assert(!stream);
+
+            if (verbose)
+                fprintf(stderr, "Connection established." CLEAR_LINE " \n");
+
+            if (!(stream = pa_stream_new(c, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL))) {
+                fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c)));
+                goto fail;
+            }
+
+            pa_stream_set_state_callback(stream, stream_state_callback, NULL);
+            pa_stream_set_write_callback(stream, stream_write_callback, NULL);
+            pa_stream_set_read_callback(stream, stream_read_callback, NULL);
+            pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
+            pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
+            pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
+            pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
+            pa_stream_set_started_callback(stream, stream_started_callback, NULL);
+
+            if (latency > 0) {
+                memset(&buffer_attr, 0, sizeof(buffer_attr));
+                buffer_attr.tlength = latency;
+                buffer_attr.minreq = process_time;
+                flags |= PA_STREAM_ADJUST_LATENCY;
+            }
+
+            if (mode == PLAYBACK) {
+                pa_cvolume cv;
+                if ((r = pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, flags, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) {
+                    fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(c)));
+                    goto fail;
+                }
+
+            } else {
+                if ((r = pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags)) < 0) {
+                    fprintf(stderr, "pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(c)));
+                    goto fail;
+                }
+            }
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            quit(0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
+            goto fail;
+    }
+
+    return;
+
+fail:
+    quit(1);
+
+}
+
+/* Connection draining complete */
+static void context_drain_complete(pa_context*c, void *userdata) {
+    pa_context_disconnect(c);
+}
+
+/* Stream draining complete */
+static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
+    pa_operation *o;
+
+    if (!success) {
+        fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
+        quit(1);
+    }
+
+    if (verbose)
+        fprintf(stderr, "Playback stream drained.\n");
+
+    pa_stream_disconnect(stream);
+    pa_stream_unref(stream);
+    stream = NULL;
+
+    if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+        pa_context_disconnect(context);
+    else {
+        if (verbose)
+            fprintf(stderr, "Draining connection to server.\n");
+    }
+}
+
+/* New data on STDIN **/
+static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    size_t l, w = 0;
+    ssize_t r;
+
+    assert(a == mainloop_api);
+    assert(e);
+    assert(stdio_event == e);
+
+    if (buffer) {
+        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
+        return;
+    }
+
+    if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
+        l = 4096;
+
+    buffer = pa_xmalloc(l);
+
+    if ((r = read(fd, buffer, l)) <= 0) {
+        if (r == 0) {
+            if (verbose)
+                fprintf(stderr, "Got EOF.\n");
+
+            if (stream) {
+                pa_operation *o;
+
+                if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
+                    fprintf(stderr, "pa_stream_drain(): %s\n", pa_strerror(pa_context_errno(context)));
+                    quit(1);
+                    return;
+                }
+
+                pa_operation_unref(o);
+            } else
+                quit(0);
+
+        } else {
+            fprintf(stderr, "read() failed: %s\n", strerror(errno));
+            quit(1);
+        }
+
+        mainloop_api->io_free(stdio_event);
+        stdio_event = NULL;
+        return;
+    }
+
+    buffer_length = r;
+    buffer_index = 0;
+
+    if (w)
+        do_stream_write(w);
+}
+
+/* Some data may be written to STDOUT */
+static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
+    ssize_t r;
+
+    assert(a == mainloop_api);
+    assert(e);
+    assert(stdio_event == e);
+
+    if (!buffer) {
+        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
+        return;
+    }
+
+    assert(buffer_length);
+
+    if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
+        fprintf(stderr, "write() failed: %s\n", strerror(errno));
+        quit(1);
+
+        mainloop_api->io_free(stdio_event);
+        stdio_event = NULL;
+        return;
+    }
+
+    buffer_length -= r;
+    buffer_index += r;
+
+    if (!buffer_length) {
+        pa_xfree(buffer);
+        buffer = NULL;
+        buffer_length = buffer_index = 0;
+    }
+}
+
+/* UNIX signal to quit recieved */
+static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
+    if (verbose)
+        fprintf(stderr, "Got signal, exiting.\n");
+    quit(0);
+}
+
+/* Show the current latency */
+static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
+    pa_usec_t l, usec;
+    int negative = 0;
+
+    assert(s);
+
+    if (!success ||
+        pa_stream_get_time(s, &usec) < 0 ||
+        pa_stream_get_latency(s, &l, &negative) < 0) {
+        fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
+        quit(1);
+        return;
+    }
+
+    fprintf(stderr, "Time: %0.3f sec; Latency: %0.0f usec.  \r",
+            (float) usec / 1000000,
+            (float) l * (negative?-1:1));
+}
+
+/* Someone requested that the latency is shown */
+static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
+
+    if (!stream)
+        return;
+
+    pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
+}
+
+static void time_event_callback(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    struct timeval next;
+
+    if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
+        pa_operation *o;
+        if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)))
+            fprintf(stderr, "pa_stream_update_timing_info() failed: %s\n", pa_strerror(pa_context_errno(context)));
+        else
+            pa_operation_unref(o);
+    }
+
+    pa_gettimeofday(&next);
+    pa_timeval_add(&next, TIME_EVENT_USEC);
+
+    m->time_restart(e, &next);
+}
+
+static void help(const char *argv0) {
+
+    printf("%s [options]\n\n"
+           "  -h, --help                            Show this help\n"
+           "      --version                         Show version\n\n"
+           "  -r, --record                          Create a connection for recording\n"
+           "  -p, --playback                        Create a connection for playback\n\n"
+           "  -v, --verbose                         Enable verbose operations\n\n"
+           "  -s, --server=SERVER                   The name of the server to connect to\n"
+           "  -d, --device=DEVICE                   The name of the sink/source to connect to\n"
+           "  -n, --client-name=NAME                How to call this client on the server\n"
+           "      --stream-name=NAME                How to call this stream on the server\n"
+           "      --volume=VOLUME                   Specify the initial (linear) volume in range 0...65536\n"
+           "      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"
+           "      --format=SAMPLEFORMAT             The sample type, one of s16le, s16be, u8, float32le,\n"
+           "                                        float32be, ulaw, alaw (defaults to s16ne)\n"
+           "      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
+           "                                        (defaults to 2)\n"
+           "      --channel-map=CHANNELMAP          Channel map to use instead of the default\n"
+           "      --fix-format                      Take the sample format from the sink the stream is\n"
+           "                                        being connected to.\n"
+           "      --fix-rate                        Take the sampling rate from the sink the stream is\n"
+           "                                        being connected to.\n"
+           "      --fix-channels                    Take the number of channels and the channel map\n"
+           "                                        from the sink the stream is being connected to.\n"
+           "      --no-remix                        Don't upmix or downmix channels.\n"
+           "      --no-remap                        Map channels by index instead of name.\n"
+           "      --latency=BYTES                   Request the specified latency in bytes.\n"
+           "      --process-time=BYTES              Request the specified process time per request in bytes.\n"
+           ,
+           argv0);
+}
+
+enum {
+    ARG_VERSION = 256,
+    ARG_STREAM_NAME,
+    ARG_VOLUME,
+    ARG_SAMPLERATE,
+    ARG_SAMPLEFORMAT,
+    ARG_CHANNELS,
+    ARG_CHANNELMAP,
+    ARG_FIX_FORMAT,
+    ARG_FIX_RATE,
+    ARG_FIX_CHANNELS,
+    ARG_NO_REMAP,
+    ARG_NO_REMIX,
+    ARG_LATENCY,
+    ARG_PROCESS_TIME
+};
+
+int main(int argc, char *argv[]) {
+    pa_mainloop* m = NULL;
+    int ret = 1, r, c;
+    char *bn, *server = NULL;
+    pa_time_event *time_event = NULL;
+
+    static const struct option long_options[] = {
+        {"record",       0, NULL, 'r'},
+        {"playback",     0, NULL, 'p'},
+        {"device",       1, NULL, 'd'},
+        {"server",       1, NULL, 's'},
+        {"client-name",  1, NULL, 'n'},
+        {"stream-name",  1, NULL, ARG_STREAM_NAME},
+        {"version",      0, NULL, ARG_VERSION},
+        {"help",         0, NULL, 'h'},
+        {"verbose",      0, NULL, 'v'},
+        {"volume",       1, NULL, ARG_VOLUME},
+        {"rate",         1, NULL, ARG_SAMPLERATE},
+        {"format",       1, NULL, ARG_SAMPLEFORMAT},
+        {"channels",     1, NULL, ARG_CHANNELS},
+        {"channel-map",  1, NULL, ARG_CHANNELMAP},
+        {"fix-format",   0, NULL, ARG_FIX_FORMAT},
+        {"fix-rate",     0, NULL, ARG_FIX_RATE},
+        {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
+        {"no-remap",     0, NULL, ARG_NO_REMAP},
+        {"no-remix",     0, NULL, ARG_NO_REMIX},
+        {"latency",      1, NULL, ARG_LATENCY},
+        {"process-time", 1, NULL, ARG_PROCESS_TIME},
+        {NULL,           0, NULL, 0}
+    };
+
+    if (!(bn = strrchr(argv[0], '/')))
+        bn = argv[0];
+    else
+        bn++;
+
+    if (strstr(bn, "rec") || strstr(bn, "mon"))
+        mode = RECORD;
+    else if (strstr(bn, "cat") || strstr(bn, "play"))
+        mode = PLAYBACK;
+
+    while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
+
+        switch (c) {
+            case 'h' :
+                help(bn);
+                ret = 0;
+                goto quit;
+
+            case ARG_VERSION:
+                printf("pacat "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version());
+                ret = 0;
+                goto quit;
+
+            case 'r':
+                mode = RECORD;
+                break;
+
+            case 'p':
+                mode = PLAYBACK;
+                break;
+
+            case 'd':
+                pa_xfree(device);
+                device = pa_xstrdup(optarg);
+                break;
+
+            case 's':
+                pa_xfree(server);
+                server = pa_xstrdup(optarg);
+                break;
+
+            case 'n':
+                pa_xfree(client_name);
+                client_name = pa_xstrdup(optarg);
+                break;
+
+            case ARG_STREAM_NAME:
+                pa_xfree(stream_name);
+                stream_name = pa_xstrdup(optarg);
+                break;
+
+            case 'v':
+                verbose = 1;
+                break;
+
+            case ARG_VOLUME: {
+                int v = atoi(optarg);
+                volume = v < 0 ? 0 : v;
+                break;
+            }
+
+            case ARG_CHANNELS:
+                sample_spec.channels = atoi(optarg);
+                break;
+
+            case ARG_SAMPLEFORMAT:
+                sample_spec.format = pa_parse_sample_format(optarg);
+                break;
+
+            case ARG_SAMPLERATE:
+                sample_spec.rate = atoi(optarg);
+                break;
+
+            case ARG_CHANNELMAP:
+                if (!pa_channel_map_parse(&channel_map, optarg)) {
+                    fprintf(stderr, "Invalid channel map '%s'\n", optarg);
+                    goto quit;
+                }
+
+                channel_map_set = 1;
+                break;
+
+            case ARG_FIX_CHANNELS:
+                flags |= PA_STREAM_FIX_CHANNELS;
+                break;
+
+            case ARG_FIX_RATE:
+                flags |= PA_STREAM_FIX_RATE;
+                break;
+
+            case ARG_FIX_FORMAT:
+                flags |= PA_STREAM_FIX_FORMAT;
+                break;
+
+            case ARG_NO_REMIX:
+                flags |= PA_STREAM_NO_REMIX_CHANNELS;
+                break;
+
+            case ARG_NO_REMAP:
+                flags |= PA_STREAM_NO_REMAP_CHANNELS;
+                break;
+
+            case ARG_LATENCY:
+                if (((latency = atoi(optarg))) <= 0) {
+                    fprintf(stderr, "Invalid latency specification '%s'\n", optarg);
+                    goto quit;
+                }
+                break;
+
+            case ARG_PROCESS_TIME:
+                if (((process_time = atoi(optarg))) <= 0) {
+                    fprintf(stderr, "Invalid process time specification '%s'\n", optarg);
+                    goto quit;
+                }
+                break;
+
+            default:
+                goto quit;
+        }
+    }
+
+    if (!pa_sample_spec_valid(&sample_spec)) {
+        fprintf(stderr, "Invalid sample specification\n");
+        goto quit;
+    }
+
+    if (channel_map_set && channel_map.channels != sample_spec.channels) {
+        fprintf(stderr, "Channel map doesn't match sample specification\n");
+        goto quit;
+    }
+
+    if (verbose) {
+        char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
+        pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
+        fprintf(stderr, "Opening a %s stream with sample specification '%s'.\n", mode == RECORD ? "recording" : "playback", t);
+    }
+
+    if (!(optind >= argc)) {
+        if (optind+1 == argc) {
+            int fd;
+
+            if ((fd = open(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
+                fprintf(stderr, "open(): %s\n", strerror(errno));
+                goto quit;
+            }
+
+            if (dup2(fd, mode == PLAYBACK ? 0 : 1) < 0) {
+                fprintf(stderr, "dup2(): %s\n", strerror(errno));
+                goto quit;
+            }
+
+            close(fd);
+
+            if (!stream_name)
+                stream_name = pa_xstrdup(argv[optind]);
+
+        } else {
+            fprintf(stderr, "Too many arguments.\n");
+            goto quit;
+        }
+    }
+
+    if (!client_name)
+        client_name = pa_xstrdup(bn);
+
+    if (!stream_name)
+        stream_name = pa_xstrdup(client_name);
+
+    /* Set up a new main loop */
+    if (!(m = pa_mainloop_new())) {
+        fprintf(stderr, "pa_mainloop_new() failed.\n");
+        goto quit;
+    }
+
+    mainloop_api = pa_mainloop_get_api(m);
+
+    r = pa_signal_init(mainloop_api);
+    assert(r == 0);
+    pa_signal_new(SIGINT, exit_signal_callback, NULL);
+    pa_signal_new(SIGTERM, exit_signal_callback, NULL);
+#ifdef SIGUSR1
+    pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
+#endif
+#ifdef SIGPIPE
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    if (!(stdio_event = mainloop_api->io_new(mainloop_api,
+                                             mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
+                                             mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
+                                             mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
+        fprintf(stderr, "io_new() failed.\n");
+        goto quit;
+    }
+
+    /* Create a new connection context */
+    if (!(context = pa_context_new(mainloop_api, client_name))) {
+        fprintf(stderr, "pa_context_new() failed.\n");
+        goto quit;
+    }
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+
+    /* Connect the context */
+    pa_context_connect(context, server, 0, NULL);
+
+    if (verbose) {
+        struct timeval tv;
+
+        pa_gettimeofday(&tv);
+        pa_timeval_add(&tv, TIME_EVENT_USEC);
+
+        if (!(time_event = mainloop_api->time_new(mainloop_api, &tv, time_event_callback, NULL))) {
+            fprintf(stderr, "time_new() failed.\n");
+            goto quit;
+        }
+    }
+
+    /* Run the main loop */
+    if (pa_mainloop_run(m, &ret) < 0) {
+        fprintf(stderr, "pa_mainloop_run() failed.\n");
+        goto quit;
+    }
+
+quit:
+    if (stream)
+        pa_stream_unref(stream);
+
+    if (context)
+        pa_context_unref(context);
+
+    if (stdio_event) {
+        assert(mainloop_api);
+        mainloop_api->io_free(stdio_event);
+    }
+
+    if (time_event) {
+        assert(mainloop_api);
+        mainloop_api->time_free(time_event);
+    }
+
+    if (m) {
+        pa_signal_done();
+        pa_mainloop_free(m);
+    }
+
+    pa_xfree(buffer);
+
+    pa_xfree(server);
+    pa_xfree(device);
+    pa_xfree(client_name);
+    pa_xfree(stream_name);
+
+    return ret;
+}
similarity index 70%
rename from polyp/pacmd.c
rename to src/utils/pacmd.c
index d69c14d7fd1faa3e275c49fccbba502d4a2be2dd..67d9525295d972804de903248dc805a17dae16d2 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <string.h>
 #include <sys/un.h>
 
-#include "util.h"
-#include "log.h"
-#include "pid.h"
+#include <pulse/error.h>
+#include <pulse/util.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/pid.h>
 
-int main() {
+int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
     pid_t pid ;
     int fd = -1;
     int ret = 1, i;
@@ -44,42 +48,48 @@ int main() {
     char ibuf[256], obuf[256];
     size_t ibuf_index, ibuf_length, obuf_index, obuf_length;
     fd_set ifds, ofds;
+    char *cli;
 
-    if (pa_pid_file_check_running(&pid) < 0) {
-        pa_log(__FILE__": no Polypaudio daemon running\n");
+    if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) {
+        pa_log("no PulseAudio daemon running");
         goto fail;
     }
 
     if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
-        pa_log(__FILE__": socket(PF_UNIX, SOCK_STREAM, 0): %s\n", strerror(errno));
+        pa_log("socket(PF_UNIX, SOCK_STREAM, 0): %s", strerror(errno));
         goto fail;
     }
 
     memset(&sa, 0, sizeof(sa));
     sa.sun_family = AF_UNIX;
-    pa_runtime_path("cli", sa.sun_path, sizeof(sa.sun_path));
+
+    if (!(cli = pa_runtime_path("cli")))
+        goto fail;
+
+    pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path));
+    pa_xfree(cli);
 
     for (i = 0; i < 5; i++) {
         int r;
-        
+
         if ((r = connect(fd, (struct sockaddr*) &sa, sizeof(sa))) < 0 && (errno != ECONNREFUSED && errno != ENOENT)) {
-            pa_log(__FILE__": connect() failed: %s\n", strerror(errno));
+            pa_log("connect(): %s", strerror(errno));
             goto fail;
         }
-            
+
         if (r >= 0)
             break;
 
-        if (pa_pid_file_kill(SIGUSR2, NULL) < 0) {
-            pa_log(__FILE__": failed to kill Polypaudio daemon.\n");
+        if (pa_pid_file_kill(SIGUSR2, NULL, "pulseaudio") < 0) {
+            pa_log("failed to kill PulseAudio daemon.");
             goto fail;
         }
 
-        pa_msleep(50);
+        pa_msleep(300);
     }
 
     if (i >= 5) {
-        pa_log(__FILE__": daemon not responding.\n");
+        pa_log("daemon not responding.");
         goto fail;
     }
 
@@ -91,29 +101,29 @@ int main() {
     FD_SET(fd, &ifds);
 
     FD_ZERO(&ofds);
-    
+
     for (;;) {
         if (select(FD_SETSIZE, &ifds, &ofds, NULL, NULL) < 0) {
-            pa_log(__FILE__": select() failed: %s\n", strerror(errno));
+            pa_log("select(): %s", strerror(errno));
             goto fail;
         }
 
         if (FD_ISSET(0, &ifds)) {
             ssize_t r;
             assert(!ibuf_length);
-            
+
             if ((r = read(0, ibuf, sizeof(ibuf))) <= 0) {
                 if (r == 0)
                     break;
-                
-                pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+
+                pa_log("read(): %s", strerror(errno));
                 goto fail;
             }
-            
+
             ibuf_length = (size_t) r;
             ibuf_index = 0;
         }
-        
+
         if (FD_ISSET(fd, &ifds)) {
             ssize_t r;
             assert(!obuf_length);
@@ -121,8 +131,8 @@ int main() {
             if ((r = read(fd, obuf, sizeof(obuf))) <= 0) {
                 if (r == 0)
                     break;
-                
-                pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+
+                pa_log("read(): %s", strerror(errno));
                 goto fail;
             }
 
@@ -133,12 +143,12 @@ int main() {
         if (FD_ISSET(1, &ofds)) {
             ssize_t r;
             assert(obuf_length);
-            
+
             if ((r = write(1, obuf + obuf_index, obuf_length)) < 0) {
-                pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+                pa_log("write(): %s", strerror(errno));
                 goto fail;
             }
-            
+
             obuf_length -= (size_t) r;
             obuf_index += obuf_index;
 
@@ -147,12 +157,12 @@ int main() {
         if (FD_ISSET(fd, &ofds)) {
             ssize_t r;
             assert(ibuf_length);
-            
+
             if ((r = write(fd, ibuf + ibuf_index, ibuf_length)) < 0) {
-                pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+                pa_log("write(): %s", strerror(errno));
                 goto fail;
             }
-            
+
             ibuf_length -= (size_t) r;
             ibuf_index += obuf_index;
 
@@ -160,24 +170,24 @@ int main() {
 
         FD_ZERO(&ifds);
         FD_ZERO(&ofds);
-        
+
         if (obuf_length <= 0)
             FD_SET(fd, &ifds);
         else
             FD_SET(1, &ofds);
-        
+
         if (ibuf_length <= 0)
             FD_SET(0, &ifds);
         else
             FD_SET(fd, &ofds);
     }
-    
+
 
     ret = 0;
-    
+
 fail:
     if (fd >= 0)
         close(fd);
-    
+
     return ret;
 }
similarity index 54%
rename from polyp/pactl.c
rename to src/utils/pactl.c
index 423cce9593b983de62fedc9b36ab25142e84869b..4cca2f865b972daf83e5818493726e7667f68fc7 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include <sndfile.h>
 
-#include <polyp/polyplib.h>
-#include <polyp/polyplib-error.h>
-#include <polyp/mainloop.h>
-#include <polyp/mainloop-signal.h>
-#include <polyp/sample.h>
+#include <pulse/pulseaudio.h>
+#include <pulsecore/core-util.h>
 
-#if PA_API_VERSION != 8
-#error Invalid Polypaudio API version
+#if PA_API_VERSION < 10
+#error Invalid PulseAudio API version
 #endif
 
 #define BUFSIZE 1024
 
-static struct pa_context *context = NULL;
-static struct pa_mainloop_api *mainloop_api = NULL;
+static pa_context *context = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
 
-static char *device = NULL, *sample_name = NULL;
+static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL, *module_name = NULL, *module_args = NULL;
+static uint32_t sink_input_idx = PA_INVALID_INDEX, source_output_idx = PA_INVALID_INDEX;
+static uint32_t module_index;
+static int suspend;
 
 static SNDFILE *sndfile = NULL;
-static struct pa_stream *sample_stream = NULL;
-static struct pa_sample_spec sample_spec;
+static pa_stream *sample_stream = NULL;
+static pa_sample_spec sample_spec;
 static size_t sample_length = 0;
 
 static int actions = 1;
@@ -68,7 +68,13 @@ static enum {
     UPLOAD_SAMPLE,
     PLAY_SAMPLE,
     REMOVE_SAMPLE,
-    LIST
+    LIST,
+    MOVE_SINK_INPUT,
+    MOVE_SOURCE_OUTPUT,
+    LOAD_MODULE,
+    UNLOAD_MODULE,
+    SUSPEND_SINK,
+    SUSPEND_SOURCE,
 } action = NONE;
 
 static void quit(int ret) {
@@ -77,12 +83,12 @@ static void quit(int ret) {
 }
 
 
-static void context_drain_complete(struct pa_context *c, void *userdata) {
+static void context_drain_complete(pa_context *c, void *userdata) {
     pa_context_disconnect(c);
 }
 
 static void drain(void) {
-    struct pa_operation *o;
+    pa_operation *o;
     if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
         pa_context_disconnect(context);
     else
@@ -97,7 +103,7 @@ static void complete_action(void) {
         drain();
 }
 
-static void stat_callback(struct pa_context *c, const struct pa_stat_info *i, void *userdata) {
+static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
     char s[128];
     if (!i) {
         fprintf(stderr, "Failed to get statistics: %s\n", pa_strerror(pa_context_errno(c)));
@@ -113,13 +119,13 @@ static void stat_callback(struct pa_context *c, const struct pa_stat_info *i, vo
 
     pa_bytes_snprint(s, sizeof(s), i->scache_size);
     printf("Sample cache size: %s\n", s);
-    
+
     complete_action();
 }
 
-static void get_server_info_callback(struct pa_context *c, const struct pa_server_info *i, void *useerdata) {
+static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
     char s[PA_SAMPLE_SPEC_SNPRINT_MAX];
-    
+
     if (!i) {
         fprintf(stderr, "Failed to get server information: %s\n", pa_strerror(pa_context_errno(c)));
         quit(1);
@@ -148,8 +154,9 @@ static void get_server_info_callback(struct pa_context *c, const struct pa_serve
     complete_action();
 }
 
-static void get_sink_info_callback(struct pa_context *c, const struct pa_sink_info *i, int is_last, void *userdata) {
-    char s[PA_SAMPLE_SPEC_SNPRINT_MAX], tid[5];
+static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
+    char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char *pl;
 
     if (is_last < 0) {
         fprintf(stderr, "Failed to get sink information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -161,38 +168,47 @@ static void get_sink_info_callback(struct pa_context *c, const struct pa_sink_in
         complete_action();
         return;
     }
-    
+
     assert(i);
 
     if (nl)
         printf("\n");
     nl = 1;
 
-    pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
-    
     printf("*** Sink #%u ***\n"
            "Name: %s\n"
-           "Type: %s\n"
-           "Description: %s\n"
+           "Driver: %s\n"
            "Sample Specification: %s\n"
+           "Channel Map: %s\n"
            "Owner Module: %u\n"
-           "Volume: 0x%03x (%0.2f dB)\n"
-           "Monitor Source: %u\n"
-           "Latency: %0.0f usec\n",
+           "Volume: %s\n"
+           "Monitor Source: %s\n"
+           "Latency: %0.0f usec, configured %0.0f usec\n"
+           "Flags: %s%s%s%s%s%s\n"
+           "Properties:\n%s",
            i->index,
            i->name,
-           pa_typeid_to_string(i->_typeid, tid, sizeof(tid)),
-           i->description,
-           s,
+           pa_strnull(i->driver),
+           pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+           pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
            i->owner_module,
-           i->volume, pa_volume_to_dB(i->volume),
-           i->monitor_source,
-           (double) i->latency);
-
+           i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           pa_strnull(i->monitor_source_name),
+           (double) i->latency, (double) i->configured_latency,
+           i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
+           i->flags & PA_SINK_NETWORK ? "NETWORK " : "",
+           i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
+           i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+           i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
+           i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
+           pl = pa_proplist_to_string(i->proplist));
+
+    pa_xfree(pl);
 }
 
-static void get_source_info_callback(struct pa_context *c, const struct pa_source_info *i, int is_last, void *userdata) {
-    char s[PA_SAMPLE_SPEC_SNPRINT_MAX], t[32], tid[5];
+static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
+    char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char *pl;
 
     if (is_last < 0) {
         fprintf(stderr, "Failed to get source information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -204,37 +220,45 @@ static void get_source_info_callback(struct pa_context *c, const struct pa_sourc
         complete_action();
         return;
     }
-    
+
     assert(i);
 
     if (nl)
         printf("\n");
     nl = 1;
 
-    snprintf(t, sizeof(t), "%u", i->monitor_of_sink);
-    
-    pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
-    
     printf("*** Source #%u ***\n"
            "Name: %s\n"
-           "Type: %s\n"
-           "Description: %s\n"
+           "Driver: %s\n"
            "Sample Specification: %s\n"
+           "Channel Map: %s\n"
            "Owner Module: %u\n"
+           "Volume: %s\n"
            "Monitor of Sink: %s\n"
-           "Latency: %0.0f usec\n",
+           "Latency: %0.0f usec, configured %0.0f usec\n"
+           "Flags: %s%s%s%s%s%s\n"
+           "Properties:\n%s",
            i->index,
-           pa_typeid_to_string(i->_typeid, tid, sizeof(tid)),
            i->name,
-           i->description,
-           s,
+           pa_strnull(i->driver),
+           pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+           pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
            i->owner_module,
-           i->monitor_of_sink != PA_INVALID_INDEX ? t : "no",
-           (double) i->latency);
-    
+           i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           i->monitor_of_sink_name ? i->monitor_of_sink_name : "n/a",
+           (double) i->latency, (double) i->configured_latency,
+           i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
+           i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
+           i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
+           i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
+           i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
+           i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
+           pl = pa_proplist_to_string(i->proplist));
+
+    pa_xfree(pl);
 }
 
-static void get_module_info_callback(struct pa_context *c, const struct pa_module_info *i, int is_last, void *userdata) {
+static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
     char t[32];
 
     if (is_last < 0) {
@@ -247,7 +271,7 @@ static void get_module_info_callback(struct pa_context *c, const struct pa_modul
         complete_action();
         return;
     }
-    
+
     assert(i);
 
     if (nl)
@@ -255,7 +279,7 @@ static void get_module_info_callback(struct pa_context *c, const struct pa_modul
     nl = 1;
 
     snprintf(t, sizeof(t), "%u", i->n_used);
-    
+
     printf("*** Module #%u ***\n"
            "Name: %s\n"
            "Argument: %s\n"
@@ -263,13 +287,14 @@ static void get_module_info_callback(struct pa_context *c, const struct pa_modul
            "Auto unload: %s\n",
            i->index,
            i->name,
-           i->argument,
+           i->argument ? i->argument : "",
            i->n_used != PA_INVALID_INDEX ? t : "n/a",
-           i->auto_unload ? "yes" : "no");
+           pa_yes_no(i->auto_unload));
 }
 
-static void get_client_info_callback(struct pa_context *c, const struct pa_client_info *i, int is_last, void *userdata) {
-    char t[32], tid[5];
+static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
+    char t[32];
+    char *pl;
 
     if (is_last < 0) {
         fprintf(stderr, "Failed to get client information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -281,7 +306,7 @@ static void get_client_info_callback(struct pa_context *c, const struct pa_clien
         complete_action();
         return;
     }
-    
+
     assert(i);
 
     if (nl)
@@ -289,19 +314,22 @@ static void get_client_info_callback(struct pa_context *c, const struct pa_clien
     nl = 1;
 
     snprintf(t, sizeof(t), "%u", i->owner_module);
-    
+
     printf("*** Client #%u ***\n"
-           "Name: %s\n"
-           "Type: %s\n"
-           "Owner Module: %s\n",
+           "Driver: %s\n"
+           "Owner Module: %s\n"
+           "Properties:\n%s",
            i->index,
-           i->name,
-           pa_typeid_to_string(i->_typeid, tid, sizeof(tid)),
-           i->owner_module != PA_INVALID_INDEX ? t : "n/a");
+           pa_strnull(i->driver),
+           i->owner_module != PA_INVALID_INDEX ? t : "n/a",
+           pl = pa_proplist_to_string(i->proplist));
+
+    pa_xfree(pl);
 }
 
-static void get_sink_input_info_callback(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
-    char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], tid[5];
+static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
+    char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char *pl;
 
     if (is_last < 0) {
         fprintf(stderr, "Failed to get sink input information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -313,44 +341,47 @@ static void get_sink_input_info_callback(struct pa_context *c, const struct pa_s
         complete_action();
         return;
     }
-    
+
     assert(i);
 
     if (nl)
         printf("\n");
     nl = 1;
 
-    pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
     snprintf(t, sizeof(t), "%u", i->owner_module);
     snprintf(k, sizeof(k), "%u", i->client);
-    
+
     printf("*** Sink Input #%u ***\n"
-           "Name: %s\n"
-           "Type: %s\n"
+           "Driver: %s\n"
            "Owner Module: %s\n"
            "Client: %s\n"
            "Sink: %u\n"
            "Sample Specification: %s\n"
-           "Volume: 0x%03x (%0.2f dB)\n"
+           "Channel Map: %s\n"
+           "Volume: %s\n"
            "Buffer Latency: %0.0f usec\n"
            "Sink Latency: %0.0f usec\n"
-           "Resample method: %s\n",
+           "Resample method: %s\n"
+           "Properties:\n%s",
            i->index,
-           i->name,
-           pa_typeid_to_string(i->_typeid, tid, sizeof(tid)),
+           pa_strnull(i->driver),
            i->owner_module != PA_INVALID_INDEX ? t : "n/a",
            i->client != PA_INVALID_INDEX ? k : "n/a",
            i->sink,
-           s,
-           i->volume, pa_volume_to_dB(i->volume),
+           pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+           pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+           i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
            (double) i->buffer_usec,
            (double) i->sink_usec,
-           i->resample_method ? i->resample_method : "n/a");
-}
+           i->resample_method ? i->resample_method : "n/a",
+           pl = pa_proplist_to_string(i->proplist));
 
+    pa_xfree(pl);
+}
 
-static void get_source_output_info_callback(struct pa_context *c, const struct pa_source_output_info *i, int is_last, void *userdata) {
-    char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], tid[5];
+static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
+    char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char *pl;
 
     if (is_last < 0) {
         fprintf(stderr, "Failed to get source output information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -362,41 +393,46 @@ static void get_source_output_info_callback(struct pa_context *c, const struct p
         complete_action();
         return;
     }
-    
+
     assert(i);
 
     if (nl)
         printf("\n");
     nl = 1;
 
-    pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
+
     snprintf(t, sizeof(t), "%u", i->owner_module);
     snprintf(k, sizeof(k), "%u", i->client);
-    
+
     printf("*** Source Output #%u ***\n"
-           "Name: %s\n"
-           "Type: %s\n"
+           "Driver: %s\n"
            "Owner Module: %s\n"
            "Client: %s\n"
            "Source: %u\n"
            "Sample Specification: %s\n"
+           "Channel Map: %s\n"
            "Buffer Latency: %0.0f usec\n"
            "Source Latency: %0.0f usec\n"
-           "Resample method: %s\n",
+           "Resample method: %s\n"
+           "Properties:\n%s",
            i->index,
-           i->name,
-           pa_typeid_to_string(i->_typeid, tid, sizeof(tid)),
+           pa_strnull(i->driver),
            i->owner_module != PA_INVALID_INDEX ? t : "n/a",
            i->client != PA_INVALID_INDEX ? k : "n/a",
            i->source,
-           s,
+           pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
+           pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
            (double) i->buffer_usec,
            (double) i->source_usec,
-           i->resample_method ? i->resample_method : "n/a");
+           i->resample_method ? i->resample_method : "n/a",
+           pl = pa_proplist_to_string(i->proplist));
+
+    pa_xfree(pl);
 }
 
-static void get_sample_info_callback(struct pa_context *c, const struct pa_sample_info *i, int is_last, void *userdata) {
-    char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX];
+static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
+    char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char *pl;
 
     if (is_last < 0) {
         fprintf(stderr, "Failed to get sample information: %s\n", pa_strerror(pa_context_errno(c)));
@@ -408,35 +444,41 @@ static void get_sample_info_callback(struct pa_context *c, const struct pa_sampl
         complete_action();
         return;
     }
-    
+
     assert(i);
 
     if (nl)
         printf("\n");
     nl = 1;
 
-    pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
+
     pa_bytes_snprint(t, sizeof(t), i->bytes);
-    
+
     printf("*** Sample #%u ***\n"
            "Name: %s\n"
-           "Volume: 0x%03x (%0.2f dB)\n"
+           "Volume: %s\n"
            "Sample Specification: %s\n"
+           "Channel Map: %s\n"
            "Duration: %0.1fs\n"
            "Size: %s\n"
            "Lazy: %s\n"
-           "Filename: %s\n",
+           "Filename: %s\n"
+           "Properties:\n%s",
            i->index,
            i->name,
-           i->volume, pa_volume_to_dB(i->volume),
-           pa_sample_spec_valid(&i->sample_spec) ? s : "n/a",
+           pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : "n/a",
+           pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : "n/a",
            (double) i->duration/1000000,
            t,
-           i->lazy ? "yes" : "no",
-           i->filename ? i->filename : "n/a");
+           pa_yes_no(i->lazy),
+           i->filename ? i->filename : "n/a",
+           pl = pa_proplist_to_string(i->proplist));
+
+    pa_xfree(pl);
 }
 
-static void get_autoload_info_callback(struct pa_context *c, const struct pa_autoload_info *i, int is_last, void *userdata) {
+static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata) {
     if (is_last < 0) {
         fprintf(stderr, "Failed to get autoload information: %s\n", pa_strerror(pa_context_errno(c)));
         quit(1);
@@ -447,7 +489,7 @@ static void get_autoload_info_callback(struct pa_context *c, const struct pa_aut
         complete_action();
         return;
     }
-    
+
     assert(i);
 
     if (nl)
@@ -463,10 +505,10 @@ static void get_autoload_info_callback(struct pa_context *c, const struct pa_aut
            i->name,
            i->type == PA_AUTOLOAD_SINK ? "sink" : "source",
            i->module,
-           i->argument);
+           i->argument ? i->argument : "");
 }
 
-static void simple_callback(struct pa_context *c, int success, void *userdata) {
+static void simple_callback(pa_context *c, int success, void *userdata) {
     if (!success) {
         fprintf(stderr, "Failure: %s\n", pa_strerror(pa_context_errno(c)));
         quit(1);
@@ -476,18 +518,30 @@ static void simple_callback(struct pa_context *c, int success, void *userdata) {
     complete_action();
 }
 
-static void stream_state_callback(struct pa_stream *s, void *userdata) {
+static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
+    if (idx == PA_INVALID_INDEX) {
+        fprintf(stderr, "Failure: %s\n", pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    printf("%u\n", idx);
+
+    complete_action();
+}
+
+static void stream_state_callback(pa_stream *s, void *userdata) {
     assert(s);
 
     switch (pa_stream_get_state(s)) {
         case PA_STREAM_CREATING:
         case PA_STREAM_READY:
             break;
-            
+
         case PA_STREAM_TERMINATED:
             drain();
             break;
-            
+
         case PA_STREAM_FAILED:
         default:
             fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
@@ -495,24 +549,23 @@ static void stream_state_callback(struct pa_stream *s, void *userdata) {
     }
 }
 
-static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) {
+static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
     sf_count_t l;
     float *d;
     assert(s && length && sndfile);
 
-    d = malloc(length);
-    assert(d);
+    d = pa_xmalloc(length);
 
     assert(sample_length >= length);
     l = length/pa_frame_size(&sample_spec);
 
     if ((sf_readf_float(sndfile, d, l)) != l) {
-        free(d);
+        pa_xfree(d);
         fprintf(stderr, "Premature end of file\n");
         quit(1);
     }
-    
-    pa_stream_write(s, d, length, free, 0);
+
+    pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
 
     sample_length -= length;
 
@@ -522,7 +575,7 @@ static void stream_write_callback(struct pa_stream *s, size_t length, void *user
     }
 }
 
-static void context_state_callback(struct pa_context *c, void *userdata) {
+static void context_state_callback(pa_context *c, void *userdata) {
     assert(c);
     switch (pa_context_get_state(c)) {
         case PA_CONTEXT_CONNECTING:
@@ -538,7 +591,7 @@ static void context_state_callback(struct pa_context *c, void *userdata) {
                     pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
                     break;
 
-                case PLAY_SAMPLE: 
+                case PLAY_SAMPLE:
                     pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL));
                     break;
 
@@ -547,16 +600,16 @@ static void context_state_callback(struct pa_context *c, void *userdata) {
                     break;
 
                 case UPLOAD_SAMPLE:
-                    sample_stream = pa_stream_new(c, sample_name, &sample_spec);
+                    sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
                     assert(sample_stream);
-                    
+
                     pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
                     pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
                     pa_stream_connect_upload(sample_stream, sample_length);
                     break;
-                    
+
                 case EXIT:
-                    pa_context_exit_daemon(c);
+                    pa_operation_unref(pa_context_exit_daemon(c, NULL, NULL));
                     drain();
 
                 case LIST:
@@ -565,12 +618,42 @@ static void context_state_callback(struct pa_context *c, void *userdata) {
                     pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
                     pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
                     pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
-                    pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL)); 
+                    pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
                     pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
                     pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
                     pa_operation_unref(pa_context_get_autoload_info_list(c, get_autoload_info_callback, NULL));
                     break;
 
+                case MOVE_SINK_INPUT:
+                    pa_operation_unref(pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL));
+                    break;
+
+                case MOVE_SOURCE_OUTPUT:
+                    pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL));
+                    break;
+
+                case LOAD_MODULE:
+                    pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL));
+                    break;
+
+                case UNLOAD_MODULE:
+                    pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL));
+                    break;
+
+                case SUSPEND_SINK:
+                    if (sink_name)
+                        pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL));
+                    else
+                        pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
+                    break;
+
+                case SUSPEND_SOURCE:
+                    if (source_name)
+                        pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL));
+                    else
+                        pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
+                    break;
+
                 default:
                     assert(0);
             }
@@ -587,7 +670,7 @@ static void context_state_callback(struct pa_context *c, void *userdata) {
     }
 }
 
-static void exit_signal_callback(struct pa_mainloop_api *m, struct pa_signal_event *e, int sig, void *userdata) {
+static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
     fprintf(stderr, "Got SIGINT, exiting.\n");
     quit(0);
 }
@@ -599,18 +682,24 @@ static void help(const char *argv0) {
            "%s [options] exit\n"
            "%s [options] upload-sample FILENAME [NAME]\n"
            "%s [options] play-sample NAME [SINK]\n"
-           "%s [options] remove-sample NAME\n\n"
+           "%s [options] remove-sample NAME\n"
+           "%s [options] move-sink-input ID SINK\n"
+           "%s [options] move-source-output ID SOURCE\n"
+           "%s [options] load-module NAME [ARGS ...]\n"
+           "%s [options] unload-module ID\n"
+           "%s [options] suspend-sink [SINK] 1|0\n"
+           "%s [options] suspend-source [SOURCE] 1|0\n\n"
            "  -h, --help                            Show this help\n"
            "      --version                         Show version\n\n"
            "  -s, --server=SERVER                   The name of the server to connect to\n"
            "  -n, --client-name=NAME                How to call this client on the server\n",
-           argv0, argv0, argv0, argv0, argv0, argv0);
+           argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
 }
 
 enum { ARG_VERSION = 256 };
 
 int main(int argc, char *argv[]) {
-    struct pa_mainloop* m = NULL;
+    pa_mainloop* m = NULL;
     char tmp[PATH_MAX];
     int ret = 1, r, c;
     char *server = NULL, *client_name = NULL, *bn;
@@ -627,27 +716,27 @@ int main(int argc, char *argv[]) {
         bn = argv[0];
     else
         bn++;
-    
+
     while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
         switch (c) {
             case 'h' :
                 help(bn);
                 ret = 0;
                 goto quit;
-                
+
             case ARG_VERSION:
-                printf("pactl "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
+                printf("pactl "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version());
                 ret = 0;
                 goto quit;
 
             case 's':
-                free(server);
-                server = strdup(optarg);
+                pa_xfree(server);
+                server = pa_xstrdup(optarg);
                 break;
 
             case 'n':
-                free(client_name);
-                client_name = strdup(optarg);
+                pa_xfree(client_name);
+                client_name = pa_xstrdup(optarg);
                 break;
 
             default:
@@ -656,8 +745,8 @@ int main(int argc, char *argv[]) {
     }
 
     if (!client_name)
-        client_name = strdup(bn);
-    
+        client_name = pa_xstrdup(bn);
+
     if (optind < argc) {
         if (!strcmp(argv[optind], "stat"))
             action = STAT;
@@ -675,7 +764,7 @@ int main(int argc, char *argv[]) {
             }
 
             if (optind+2 < argc)
-                sample_name = strdup(argv[optind+2]);
+                sample_name = pa_xstrdup(argv[optind+2]);
             else {
                 char *f = strrchr(argv[optind+1], '/');
                 size_t n;
@@ -687,15 +776,15 @@ int main(int argc, char *argv[]) {
                 n = strcspn(f, ".");
                 strncpy(tmp, f, n);
                 tmp[n] = 0;
-                sample_name = strdup(tmp);
+                sample_name = pa_xstrdup(tmp);
             }
-            
+
             memset(&sfinfo, 0, sizeof(sfinfo));
             if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfinfo))) {
                 fprintf(stderr, "Failed to open sound file.\n");
                 goto quit;
             }
-            
+
             sample_spec.format =  PA_SAMPLE_FLOAT32;
             sample_spec.rate = sfinfo.samplerate;
             sample_spec.channels = sfinfo.channels;
@@ -703,24 +792,105 @@ int main(int argc, char *argv[]) {
             sample_length = sfinfo.frames*pa_frame_size(&sample_spec);
         } else if (!strcmp(argv[optind], "play-sample")) {
             action = PLAY_SAMPLE;
-            if (optind+1 >= argc) {
+            if (argc != optind+2 && argc != optind+3) {
                 fprintf(stderr, "You have to specify a sample name to play\n");
                 goto quit;
             }
 
-            sample_name = strdup(argv[optind+1]);
+            sample_name = pa_xstrdup(argv[optind+1]);
 
             if (optind+2 < argc)
-                device = strdup(argv[optind+2]);
-            
+                device = pa_xstrdup(argv[optind+2]);
+
         } else if (!strcmp(argv[optind], "remove-sample")) {
             action = REMOVE_SAMPLE;
-            if (optind+1 >= argc) {
+            if (argc != optind+2) {
                 fprintf(stderr, "You have to specify a sample name to remove\n");
                 goto quit;
             }
 
-            sample_name = strdup(argv[optind+1]);
+            sample_name = pa_xstrdup(argv[optind+1]);
+        } else if (!strcmp(argv[optind], "move-sink-input")) {
+            action = MOVE_SINK_INPUT;
+            if (argc != optind+3) {
+                fprintf(stderr, "You have to specify a sink input index and a sink\n");
+                goto quit;
+            }
+
+            sink_input_idx = atoi(argv[optind+1]);
+            sink_name = pa_xstrdup(argv[optind+2]);
+        } else if (!strcmp(argv[optind], "move-source-output")) {
+            action = MOVE_SOURCE_OUTPUT;
+            if (argc != optind+3) {
+                fprintf(stderr, "You have to specify a source output index and a source\n");
+                goto quit;
+            }
+
+            source_output_idx = atoi(argv[optind+1]);
+            source_name = pa_xstrdup(argv[optind+2]);
+        } else if (!strcmp(argv[optind], "load-module")) {
+            int i;
+            size_t n = 0;
+            char *p;
+
+            action = LOAD_MODULE;
+
+            if (argc <= optind+1) {
+                fprintf(stderr, "You have to specify a module name and arguments.\n");
+                goto quit;
+            }
+
+            module_name = argv[optind+1];
+
+            for (i = optind+2; i < argc; i++)
+                n += strlen(argv[i])+1;
+
+            if (n > 0) {
+                p = module_args = pa_xnew0(char, n);
+
+                for (i = optind+2; i < argc; i++)
+                    p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
+            }
+
+        } else if (!strcmp(argv[optind], "unload-module")) {
+            action = UNLOAD_MODULE;
+
+            if (argc != optind+2) {
+                fprintf(stderr, "You have to specify a module index\n");
+                goto quit;
+            }
+
+            module_index = atoi(argv[optind+1]);
+
+        } else if (!strcmp(argv[optind], "suspend-sink")) {
+            action = SUSPEND_SINK;
+
+            if (argc > optind+3 || optind+1 >= argc) {
+                fprintf(stderr, "You may not specify more than one sink. You have to specify at least one boolean value.\n");
+                goto quit;
+            }
+
+            suspend = pa_parse_boolean(argv[argc-1]);
+
+            if (argc > optind+2)
+                sink_name = pa_xstrdup(argv[optind+1]);
+
+        } else if (!strcmp(argv[optind], "suspend-source")) {
+            action = SUSPEND_SOURCE;
+
+            if (argc > optind+3 || optind+1 >= argc) {
+                fprintf(stderr, "You may not specify more than one source. You have to specify at least one boolean value.\n");
+                goto quit;
+            }
+
+            suspend = pa_parse_boolean(argv[argc-1]);
+
+            if (argc > optind+2)
+                source_name = pa_xstrdup(argv[optind+1]);
+        } else if (!strcmp(argv[optind], "help")) {
+            help(bn);
+            ret = 0;
+            goto quit;
         }
     }
 
@@ -739,15 +909,17 @@ int main(int argc, char *argv[]) {
     r = pa_signal_init(mainloop_api);
     assert(r == 0);
     pa_signal_new(SIGINT, exit_signal_callback, NULL);
+#ifdef SIGPIPE
     signal(SIGPIPE, SIG_IGN);
-    
+#endif
+
     if (!(context = pa_context_new(mainloop_api, client_name))) {
         fprintf(stderr, "pa_context_new() failed.\n");
         goto quit;
     }
 
     pa_context_set_state_callback(context, context_state_callback, NULL);
-    pa_context_connect(context, server, 1, NULL);
+    pa_context_connect(context, server, 0, NULL);
 
     if (pa_mainloop_run(m, &ret) < 0) {
         fprintf(stderr, "pa_mainloop_run() failed.\n");
@@ -765,13 +937,17 @@ quit:
         pa_signal_done();
         pa_mainloop_free(m);
     }
-    
+
     if (sndfile)
         sf_close(sndfile);
 
-    free(server);
-    free(device);
-    free(sample_name);
+    pa_xfree(server);
+    pa_xfree(device);
+    pa_xfree(sample_name);
+    pa_xfree(sink_name);
+    pa_xfree(source_name);
+    pa_xfree(module_args);
+    pa_xfree(client_name);
 
     return ret;
 }
diff --git a/src/utils/padsp b/src/utils/padsp
new file mode 100755 (executable)
index 0000000..4fe175c
--- /dev/null
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+# This file is part of PulseAudio.
+#
+# Copyright 2006 Lennart Poettering
+# Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+while getopts 'hs:n:m:MSDd' param ; do
+       case $param in
+               s)
+                       PULSE_SERVER="$OPTARG"
+                       export PULSE_SERVER
+                       ;;
+               n)
+                       PADSP_CLIENT_NAME="$OPTARG"
+                       export PADSP_CLIENT_NAME
+                       ;;
+               m)
+                       PADSP_STREAM_NAME="$OPTARG"
+                       export PADSP_STREAM_NAME
+                       ;;
+               M)
+                       PADSP_NO_MIXER=1
+                       export PADSP_NO_MIXER
+                       ;;
+               S)
+                       PADSP_NO_SNDSTAT=1
+                       export PADSP_NO_SNDSTAT
+                       ;;
+               D)
+                       PADSP_NO_DSP=1
+                       export PADSP_NO_DSP
+                       ;;
+               d)
+                       if [ x"$PADSP_DEBUG" = x ]; then
+                               PADSP_DEBUG=1
+                       else
+                               PADSP_DEBUG=$(( $PADSP_DEBUG + 1 ))
+                       fi
+                       export PADSP_DEBUG
+                       ;;
+               *)
+                       echo "$0 - redirect OSS audio devices to PulseAudio"
+                       echo " "
+                       echo "$0 [options] application [arguments]"
+                       echo " "
+                       echo "options:"
+                       echo "  -h                  show brief help"
+                       echo "  -s <host>[:<port>]  contact a specific PulseAudio server"
+                       echo "  -n <name>           client name to report to the server"
+                       echo "  -m <name>           stream name to report to the server"
+                       echo "  -M                  disable /dev/mixer emulation"
+                       echo "  -S                  disable /dev/sndstat emulation"
+                       echo "  -D                  disable /dev/dsp emulation"
+                       echo "  -d                  enable debug output"
+                       exit 0
+                       ;;
+       esac
+done
+
+shift $(( $OPTIND - 1 ))
+
+if [ x"$LD_PRELOAD" = x ] ; then
+   LD_PRELOAD="libpulsedsp.so"
+else
+   LD_PRELOAD="$LD_PRELOAD libpulsedsp.so"
+fi
+
+export LD_PRELOAD
+
+exec "$@"
diff --git a/src/utils/padsp.c b/src/utils/padsp.c
new file mode 100644 (file)
index 0000000..d650707
--- /dev/null
@@ -0,0 +1,2665 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef _FILE_OFFSET_BITS
+#undef _FILE_OFFSET_BITS
+#endif
+
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE 1
+#endif
+
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <signal.h>
+
+#ifdef __linux__
+#include <linux/sockios.h>
+#endif
+
+#include <pulse/pulseaudio.h>
+#include <pulse/gccmacro.h>
+#include <pulsecore/llist.h>
+
+/* On some systems SIOCINQ isn't defined, but FIONREAD is just an alias */
+#if !defined(SIOCINQ) && defined(FIONREAD)
+# define SIOCINQ FIONREAD
+#endif
+
+/* make sure gcc doesn't redefine open and friends as macros */
+#undef open
+#undef open64
+
+typedef enum {
+    FD_INFO_MIXER,
+    FD_INFO_STREAM,
+} fd_info_type_t;
+
+typedef struct fd_info fd_info;
+
+struct fd_info {
+    pthread_mutex_t mutex;
+    int ref;
+    int unusable;
+
+    fd_info_type_t type;
+    int app_fd, thread_fd;
+
+    pa_sample_spec sample_spec;
+    size_t fragment_size;
+    unsigned n_fragments;
+
+    pa_threaded_mainloop *mainloop;
+    pa_context *context;
+    pa_stream *play_stream;
+    pa_stream *rec_stream;
+    int play_precork;
+    int rec_precork;
+
+    pa_io_event *io_event;
+    pa_io_event_flags_t io_flags;
+
+    void *buf;
+    size_t rec_offset;
+
+    int operation_success;
+
+    pa_cvolume sink_volume, source_volume;
+    uint32_t sink_index, source_index;
+    int volume_modify_count;
+
+    int optr_n_blocks;
+
+    PA_LLIST_FIELDS(fd_info);
+};
+
+static int dsp_drain(fd_info *i);
+static void fd_info_remove_from_list(fd_info *i);
+
+static pthread_mutex_t fd_infos_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t func_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static PA_LLIST_HEAD(fd_info, fd_infos) = NULL;
+
+static int (*_ioctl)(int, int, void*) = NULL;
+static int (*_close)(int) = NULL;
+static int (*_open)(const char *, int, mode_t) = NULL;
+static FILE* (*_fopen)(const char *path, const char *mode) = NULL;
+static int (*_stat)(const char *, struct stat *) = NULL;
+#ifdef _STAT_VER
+static int (*___xstat)(int, const char *, struct stat *) = NULL;
+#endif
+#ifdef HAVE_OPEN64
+static int (*_open64)(const char *, int, mode_t) = NULL;
+static FILE* (*_fopen64)(const char *path, const char *mode) = NULL;
+static int (*_stat64)(const char *, struct stat64 *) = NULL;
+#ifdef _STAT_VER
+static int (*___xstat64)(int, const char *, struct stat64 *) = NULL;
+#endif
+#endif
+static int (*_fclose)(FILE *f) = NULL;
+static int (*_access)(const char *, int) = NULL;
+
+/* dlsym() violates ISO C, so confide the breakage into this function to
+ * avoid warnings. */
+typedef void (*fnptr)(void);
+static inline fnptr dlsym_fn(void *handle, const char *symbol) {
+    return (fnptr) (long) dlsym(handle, symbol);
+}
+
+#define LOAD_IOCTL_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_ioctl)  \
+        _ioctl = (int (*)(int, int, void*)) dlsym_fn(RTLD_NEXT, "ioctl"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_OPEN_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_open) \
+        _open = (int (*)(const char *, int, mode_t)) dlsym_fn(RTLD_NEXT, "open"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_OPEN64_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_open64) \
+        _open64 = (int (*)(const char *, int, mode_t)) dlsym_fn(RTLD_NEXT, "open64"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_CLOSE_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_close) \
+        _close = (int (*)(int)) dlsym_fn(RTLD_NEXT, "close"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_ACCESS_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_access) \
+        _access = (int (*)(const char*, int)) dlsym_fn(RTLD_NEXT, "access"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_STAT_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_stat) \
+        _stat = (int (*)(const char *, struct stat *)) dlsym_fn(RTLD_NEXT, "stat"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_STAT64_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_stat64) \
+        _stat64 = (int (*)(const char *, struct stat64 *)) dlsym_fn(RTLD_NEXT, "stat64"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_XSTAT_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!___xstat) \
+        ___xstat = (int (*)(int, const char *, struct stat *)) dlsym_fn(RTLD_NEXT, "__xstat"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_XSTAT64_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!___xstat64) \
+        ___xstat64 = (int (*)(int, const char *, struct stat64 *)) dlsym_fn(RTLD_NEXT, "__xstat64"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_FOPEN_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_fopen) \
+        _fopen = (FILE* (*)(const char *, const char*)) dlsym_fn(RTLD_NEXT, "fopen"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_FOPEN64_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_fopen64) \
+        _fopen64 = (FILE* (*)(const char *, const char*)) dlsym_fn(RTLD_NEXT, "fopen64"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define LOAD_FCLOSE_FUNC() \
+do { \
+    pthread_mutex_lock(&func_mutex); \
+    if (!_fclose) \
+        _fclose = (int (*)(FILE *)) dlsym_fn(RTLD_NEXT, "fclose"); \
+    pthread_mutex_unlock(&func_mutex); \
+} while(0)
+
+#define CONTEXT_CHECK_DEAD_GOTO(i, label) do { \
+if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY) { \
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
+    goto label; \
+} \
+} while(0)
+
+#define PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, label) do { \
+if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY || \
+    !(i)->play_stream || pa_stream_get_state((i)->play_stream) != PA_STREAM_READY) { \
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
+    goto label; \
+} \
+} while(0)
+
+#define RECORD_STREAM_CHECK_DEAD_GOTO(i, label) do { \
+if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY || \
+    !(i)->rec_stream || pa_stream_get_state((i)->rec_stream) != PA_STREAM_READY) { \
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
+    goto label; \
+} \
+} while(0)
+
+static void debug(int level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
+
+#define DEBUG_LEVEL_ALWAYS                0
+#define DEBUG_LEVEL_NORMAL                1
+#define DEBUG_LEVEL_VERBOSE                2
+
+static void debug(int level, const char *format, ...) {
+    va_list ap;
+    const char *dlevel_s;
+    int dlevel;
+
+    dlevel_s = getenv("PADSP_DEBUG");
+    if (!dlevel_s)
+        return;
+
+    dlevel = atoi(dlevel_s);
+
+    if (dlevel < level)
+        return;
+
+    va_start(ap, format);
+    vfprintf(stderr, format, ap);
+    va_end(ap);
+}
+
+static int padsp_disabled(void) {
+    static int *sym;
+    static int sym_resolved = 0;
+
+    /* If the current process has a symbol __padsp_disabled__ we use
+     * it to detect whether we should enable our stuff or not. A
+     * program needs to be compiled with -rdynamic for this to work!
+     * The symbol must be an int containing a three bit bitmask: bit 1
+     * -> disable /dev/dsp emulation, bit 2 -> disable /dev/sndstat
+     * emulation, bit 3 -> disable /dev/mixer emulation. Hence a value
+     * of 7 disables padsp entirely. */
+
+    pthread_mutex_lock(&func_mutex);
+    if (!sym_resolved) {
+        sym = (int*) dlsym(RTLD_DEFAULT, "__padsp_disabled__");
+        sym_resolved = 1;
+    }
+    pthread_mutex_unlock(&func_mutex);
+
+    if (!sym)
+        return 0;
+
+    return *sym;
+}
+
+static int dsp_cloak_enable(void) {
+    if (padsp_disabled() & 1)
+        return 0;
+
+    if (getenv("PADSP_NO_DSP") || getenv("PULSE_INTERNAL"))
+        return 0;
+
+    return 1;
+}
+
+static int sndstat_cloak_enable(void) {
+    if (padsp_disabled() & 2)
+        return 0;
+
+    if (getenv("PADSP_NO_SNDSTAT") || getenv("PULSE_INTERNAL"))
+        return 0;
+
+    return 1;
+}
+
+static int mixer_cloak_enable(void) {
+    if (padsp_disabled() & 4)
+        return 0;
+
+    if (getenv("PADSP_NO_MIXER") || getenv("PULSE_INTERNAL"))
+        return 0;
+
+    return 1;
+}
+static pthread_key_t recursion_key;
+
+static void recursion_key_alloc(void) {
+    pthread_key_create(&recursion_key, NULL);
+}
+
+static int function_enter(void) {
+    /* Avoid recursive calls */
+    static pthread_once_t recursion_key_once = PTHREAD_ONCE_INIT;
+    pthread_once(&recursion_key_once, recursion_key_alloc);
+
+    if (pthread_getspecific(recursion_key))
+        return 0;
+
+    pthread_setspecific(recursion_key, (void*) 1);
+    return 1;
+}
+
+static void function_exit(void) {
+    pthread_setspecific(recursion_key, NULL);
+}
+
+static void fd_info_free(fd_info *i) {
+    assert(i);
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": freeing fd info (fd=%i)\n", i->app_fd);
+
+    dsp_drain(i);
+
+    if (i->mainloop)
+        pa_threaded_mainloop_stop(i->mainloop);
+
+    if (i->play_stream) {
+        pa_stream_disconnect(i->play_stream);
+        pa_stream_unref(i->play_stream);
+    }
+
+    if (i->rec_stream) {
+        pa_stream_disconnect(i->rec_stream);
+        pa_stream_unref(i->rec_stream);
+    }
+
+    if (i->context) {
+        pa_context_disconnect(i->context);
+        pa_context_unref(i->context);
+    }
+
+    if (i->mainloop)
+        pa_threaded_mainloop_free(i->mainloop);
+
+    if (i->app_fd >= 0) {
+        LOAD_CLOSE_FUNC();
+        _close(i->app_fd);
+    }
+
+    if (i->thread_fd >= 0) {
+        LOAD_CLOSE_FUNC();
+        _close(i->thread_fd);
+    }
+
+    free(i->buf);
+
+    pthread_mutex_destroy(&i->mutex);
+    free(i);
+}
+
+static fd_info *fd_info_ref(fd_info *i) {
+    assert(i);
+
+    pthread_mutex_lock(&i->mutex);
+    assert(i->ref >= 1);
+    i->ref++;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": ref++, now %i\n", i->ref);
+    pthread_mutex_unlock(&i->mutex);
+
+    return i;
+}
+
+static void fd_info_unref(fd_info *i) {
+    int r;
+    pthread_mutex_lock(&i->mutex);
+    assert(i->ref >= 1);
+    r = --i->ref;
+        debug(DEBUG_LEVEL_VERBOSE, __FILE__": ref--, now %i\n", i->ref);
+    pthread_mutex_unlock(&i->mutex);
+
+    if (r <= 0)
+        fd_info_free(i);
+}
+
+static void context_state_cb(pa_context *c, void *userdata) {
+    fd_info *i = userdata;
+    assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_READY:
+        case PA_CONTEXT_TERMINATED:
+        case PA_CONTEXT_FAILED:
+            pa_threaded_mainloop_signal(i->mainloop, 0);
+            break;
+
+        case PA_CONTEXT_UNCONNECTED:
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+    }
+}
+
+static void reset_params(fd_info *i) {
+    assert(i);
+
+    i->sample_spec.format = PA_SAMPLE_U8;
+    i->sample_spec.channels = 1;
+    i->sample_spec.rate = 8000;
+    i->fragment_size = 0;
+    i->n_fragments = 0;
+}
+
+static const char *client_name(char *buf, size_t n) {
+    char p[PATH_MAX];
+    const char *e;
+
+    if ((e = getenv("PADSP_CLIENT_NAME")))
+        return e;
+
+    if (pa_get_binary_name(p, sizeof(p)))
+        snprintf(buf, n, "OSS Emulation[%s]", p);
+    else
+        snprintf(buf, n, "OSS");
+
+    return buf;
+}
+
+static const char *stream_name(void) {
+    const char *e;
+
+    if ((e = getenv("PADSP_STREAM_NAME")))
+        return e;
+
+    return "Audio Stream";
+}
+
+static void atfork_prepare(void) {
+    fd_info *i;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_prepare() enter\n");
+
+    function_enter();
+
+    pthread_mutex_lock(&fd_infos_mutex);
+
+    for (i = fd_infos; i; i = i->next) {
+        pthread_mutex_lock(&i->mutex);
+        pa_threaded_mainloop_lock(i->mainloop);
+    }
+
+    pthread_mutex_lock(&func_mutex);
+
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_prepare() exit\n");
+}
+
+static void atfork_parent(void) {
+    fd_info *i;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_parent() enter\n");
+
+    pthread_mutex_unlock(&func_mutex);
+
+    for (i = fd_infos; i; i = i->next) {
+        pa_threaded_mainloop_unlock(i->mainloop);
+        pthread_mutex_unlock(&i->mutex);
+    }
+
+    pthread_mutex_unlock(&fd_infos_mutex);
+
+    function_exit();
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_parent() exit\n");
+}
+
+static void atfork_child(void) {
+    fd_info *i;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_child() enter\n");
+
+    /* We do only the bare minimum to get all fds closed */
+    pthread_mutex_init(&func_mutex, NULL);
+    pthread_mutex_init(&fd_infos_mutex, NULL);
+
+    for (i = fd_infos; i; i = i->next) {
+        pthread_mutex_init(&i->mutex, NULL);
+
+        if (i->context) {
+            pa_context_disconnect(i->context);
+            pa_context_unref(i->context);
+            i->context = NULL;
+        }
+
+        if (i->play_stream) {
+            pa_stream_unref(i->play_stream);
+            i->play_stream = NULL;
+        }
+
+        if (i->rec_stream) {
+            pa_stream_unref(i->rec_stream);
+            i->rec_stream = NULL;
+        }
+
+        if (i->app_fd >= 0) {
+            close(i->app_fd);
+            i->app_fd = -1;
+        }
+
+        if (i->thread_fd >= 0) {
+            close(i->thread_fd);
+            i->thread_fd = -1;
+        }
+
+        i->unusable = 1;
+    }
+
+    function_exit();
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_child() exit\n");
+}
+
+static void install_atfork(void) {
+    pthread_atfork(atfork_prepare, atfork_parent, atfork_child);
+}
+
+static void stream_success_cb(pa_stream *s, int success, void *userdata) {
+    fd_info *i = userdata;
+
+    assert(s);
+    assert(i);
+
+    i->operation_success = success;
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+}
+
+static void context_success_cb(pa_context *c, int success, void *userdata) {
+    fd_info *i = userdata;
+
+    assert(c);
+    assert(i);
+
+    i->operation_success = success;
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+}
+
+static fd_info* fd_info_new(fd_info_type_t type, int *_errno) {
+    fd_info *i;
+    int sfds[2] = { -1, -1 };
+    char name[64];
+    static pthread_once_t install_atfork_once = PTHREAD_ONCE_INIT;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": fd_info_new()\n");
+
+    signal(SIGPIPE, SIG_IGN); /* Yes, ugly as hell */
+
+    pthread_once(&install_atfork_once, install_atfork);
+
+    if (!(i = malloc(sizeof(fd_info)))) {
+        *_errno = ENOMEM;
+        goto fail;
+    }
+
+    i->app_fd = i->thread_fd = -1;
+    i->type = type;
+
+    i->mainloop = NULL;
+    i->context = NULL;
+    i->play_stream = NULL;
+    i->rec_stream = NULL;
+    i->play_precork = 0;
+    i->rec_precork = 0;
+    i->io_event = NULL;
+    i->io_flags = 0;
+    pthread_mutex_init(&i->mutex, NULL);
+    i->ref = 1;
+    i->buf = NULL;
+    i->rec_offset = 0;
+    i->unusable = 0;
+    pa_cvolume_reset(&i->sink_volume, 2);
+    pa_cvolume_reset(&i->source_volume, 2);
+    i->volume_modify_count = 0;
+    i->sink_index = (uint32_t) -1;
+    i->source_index = (uint32_t) -1;
+    i->optr_n_blocks = 0;
+    PA_LLIST_INIT(fd_info, i);
+
+    reset_params(i);
+
+    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfds) < 0) {
+        *_errno = errno;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": socket() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    i->app_fd = sfds[0];
+    i->thread_fd = sfds[1];
+
+    if (!(i->mainloop = pa_threaded_mainloop_new())) {
+        *_errno = EIO;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_threaded_mainloop_new() failed\n");
+        goto fail;
+    }
+
+    if (!(i->context = pa_context_new(pa_threaded_mainloop_get_api(i->mainloop), client_name(name, sizeof(name))))) {
+        *_errno = EIO;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_context_new() failed\n");
+        goto fail;
+    }
+
+    pa_context_set_state_callback(i->context, context_state_cb, i);
+
+    if (pa_context_connect(i->context, NULL, 0, NULL) < 0) {
+        *_errno = ECONNREFUSED;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    pa_threaded_mainloop_lock(i->mainloop);
+
+    if (pa_threaded_mainloop_start(i->mainloop) < 0) {
+        *_errno = EIO;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_threaded_mainloop_start() failed\n");
+        goto unlock_and_fail;
+    }
+
+    /* Wait until the context is ready */
+    pa_threaded_mainloop_wait(i->mainloop);
+
+    if (pa_context_get_state(i->context) != PA_CONTEXT_READY) {
+        *_errno = ECONNREFUSED;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto unlock_and_fail;
+    }
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+    return i;
+
+unlock_and_fail:
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+fail:
+
+    if (i)
+        fd_info_unref(i);
+
+    return NULL;
+}
+
+static void fd_info_add_to_list(fd_info *i) {
+    assert(i);
+
+    pthread_mutex_lock(&fd_infos_mutex);
+    PA_LLIST_PREPEND(fd_info, fd_infos, i);
+    pthread_mutex_unlock(&fd_infos_mutex);
+
+    fd_info_ref(i);
+}
+
+static void fd_info_remove_from_list(fd_info *i) {
+    assert(i);
+
+    pthread_mutex_lock(&fd_infos_mutex);
+    PA_LLIST_REMOVE(fd_info, fd_infos, i);
+    pthread_mutex_unlock(&fd_infos_mutex);
+
+    fd_info_unref(i);
+}
+
+static fd_info* fd_info_find(int fd) {
+    fd_info *i;
+
+    pthread_mutex_lock(&fd_infos_mutex);
+
+    for (i = fd_infos; i; i = i->next)
+        if (i->app_fd == fd && !i->unusable) {
+            fd_info_ref(i);
+            break;
+        }
+
+    pthread_mutex_unlock(&fd_infos_mutex);
+
+    return i;
+}
+
+static void fix_metrics(fd_info *i) {
+    size_t fs;
+    char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
+    fs = pa_frame_size(&i->sample_spec);
+
+    /* Don't fix things more than necessary */
+    if ((i->fragment_size % fs) == 0 &&
+        i->n_fragments >= 2 &&
+        i->fragment_size > 0)
+        return;
+
+    i->fragment_size = (i->fragment_size/fs)*fs;
+
+    /* Number of fragments set? */
+    if (i->n_fragments < 2) {
+        if (i->fragment_size > 0) {
+            i->n_fragments = pa_bytes_per_second(&i->sample_spec) / 2 / i->fragment_size;
+            if (i->n_fragments < 2)
+                i->n_fragments = 2;
+        } else
+            i->n_fragments = 12;
+    }
+
+    /* Fragment size set? */
+    if (i->fragment_size <= 0) {
+        i->fragment_size = pa_bytes_per_second(&i->sample_spec) / 2 / i->n_fragments;
+        if (i->fragment_size < 1024)
+            i->fragment_size = 1024;
+    }
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": sample spec: %s\n", pa_sample_spec_snprint(t, sizeof(t), &i->sample_spec));
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": fixated metrics to %i fragments, %li bytes each.\n", i->n_fragments, (long)i->fragment_size);
+}
+
+static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
+    fd_info *i = userdata;
+    assert(s);
+
+    if (i->io_event) {
+        pa_mainloop_api *api;
+        size_t n;
+
+        api = pa_threaded_mainloop_get_api(i->mainloop);
+
+        if (s == i->play_stream) {
+            n = pa_stream_writable_size(i->play_stream);
+            if (n == (size_t)-1) {
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_writable_size(): %s\n",
+                    pa_strerror(pa_context_errno(i->context)));
+            }
+
+            if (n >= i->fragment_size)
+                i->io_flags |= PA_IO_EVENT_INPUT;
+            else
+                i->io_flags &= ~PA_IO_EVENT_INPUT;
+        }
+
+        if (s == i->rec_stream) {
+            n = pa_stream_readable_size(i->rec_stream);
+            if (n == (size_t)-1) {
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_readable_size(): %s\n",
+                    pa_strerror(pa_context_errno(i->context)));
+            }
+
+            if (n >= i->fragment_size)
+                i->io_flags |= PA_IO_EVENT_OUTPUT;
+            else
+                i->io_flags &= ~PA_IO_EVENT_OUTPUT;
+        }
+
+        api->io_enable(i->io_event, i->io_flags);
+    }
+}
+
+static void stream_latency_update_cb(pa_stream *s, void *userdata) {
+    fd_info *i = userdata;
+    assert(s);
+
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+}
+
+static void fd_info_shutdown(fd_info *i) {
+    assert(i);
+
+    if (i->io_event) {
+        pa_mainloop_api *api;
+        api = pa_threaded_mainloop_get_api(i->mainloop);
+        api->io_free(i->io_event);
+        i->io_event = NULL;
+        i->io_flags = 0;
+    }
+
+    if (i->thread_fd >= 0) {
+        close(i->thread_fd);
+        i->thread_fd = -1;
+    }
+}
+
+static int fd_info_copy_data(fd_info *i, int force) {
+    size_t n;
+
+    if (!i->play_stream && !i->rec_stream)
+        return -1;
+
+    if ((i->play_stream) && (pa_stream_get_state(i->play_stream) == PA_STREAM_READY)) {
+        n = pa_stream_writable_size(i->play_stream);
+
+        if (n == (size_t)-1) {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_writable_size(): %s\n",
+                pa_strerror(pa_context_errno(i->context)));
+            return -1;
+        }
+
+        while (n >= i->fragment_size || force) {
+            ssize_t r;
+
+            if (!i->buf) {
+                if (!(i->buf = malloc(i->fragment_size))) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": malloc() failed.\n");
+                    return -1;
+                }
+            }
+
+            if ((r = read(i->thread_fd, i->buf, i->fragment_size)) <= 0) {
+
+                if (errno == EAGAIN)
+                    break;
+
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": read(): %s\n", r == 0 ? "EOF" : strerror(errno));
+                return -1;
+            }
+
+            if (pa_stream_write(i->play_stream, i->buf, r, free, 0, PA_SEEK_RELATIVE) < 0) {
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_write(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                return -1;
+            }
+
+            i->buf = NULL;
+
+            assert(n >= (size_t) r);
+            n -= r;
+        }
+
+        if (n >= i->fragment_size)
+            i->io_flags |= PA_IO_EVENT_INPUT;
+        else
+            i->io_flags &= ~PA_IO_EVENT_INPUT;
+    }
+
+    if ((i->rec_stream) && (pa_stream_get_state(i->rec_stream) == PA_STREAM_READY)) {
+        n = pa_stream_readable_size(i->rec_stream);
+
+        if (n == (size_t)-1) {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_readable_size(): %s\n",
+                pa_strerror(pa_context_errno(i->context)));
+            return -1;
+        }
+
+        while (n >= i->fragment_size || force) {
+            ssize_t r;
+            const void *data;
+            const char *buf;
+            size_t len;
+
+            if (pa_stream_peek(i->rec_stream, &data, &len) < 0) {
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_peek(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                return -1;
+            }
+
+            if (!data)
+                break;
+
+            buf = (const char*)data + i->rec_offset;
+
+            if ((r = write(i->thread_fd, buf, len - i->rec_offset)) <= 0) {
+
+                if (errno == EAGAIN)
+                    break;
+
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": write(): %s\n", strerror(errno));
+                return -1;
+            }
+
+            assert((size_t)r <= len - i->rec_offset);
+            i->rec_offset += r;
+
+            if (i->rec_offset == len) {
+                if (pa_stream_drop(i->rec_stream) < 0) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drop(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                    return -1;
+                }
+                i->rec_offset = 0;
+            }
+
+            assert(n >= (size_t) r);
+            n -= r;
+        }
+
+        if (n >= i->fragment_size)
+            i->io_flags |= PA_IO_EVENT_OUTPUT;
+        else
+            i->io_flags &= ~PA_IO_EVENT_OUTPUT;
+    }
+
+    if (i->io_event) {
+        pa_mainloop_api *api;
+
+        api = pa_threaded_mainloop_get_api(i->mainloop);
+        api->io_enable(i->io_event, i->io_flags);
+    }
+
+    return 0;
+}
+
+static void stream_state_cb(pa_stream *s, void * userdata) {
+    fd_info *i = userdata;
+    assert(s);
+
+    switch (pa_stream_get_state(s)) {
+
+        case PA_STREAM_READY:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": stream established.\n");
+            break;
+
+        case PA_STREAM_FAILED:
+            if (s == i->play_stream) {
+                debug(DEBUG_LEVEL_NORMAL,
+                    __FILE__": pa_stream_connect_playback() failed: %s\n",
+                    pa_strerror(pa_context_errno(i->context)));
+                pa_stream_unref(i->play_stream);
+                i->play_stream = NULL;
+            } else if (s == i->rec_stream) {
+                debug(DEBUG_LEVEL_NORMAL,
+                    __FILE__": pa_stream_connect_record() failed: %s\n",
+                    pa_strerror(pa_context_errno(i->context)));
+                pa_stream_unref(i->rec_stream);
+                i->rec_stream = NULL;
+            }
+            fd_info_shutdown(i);
+            break;
+
+        case PA_STREAM_TERMINATED:
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+            break;
+    }
+}
+
+static int create_playback_stream(fd_info *i) {
+    pa_buffer_attr attr;
+    int n, flags;
+
+    assert(i);
+
+    fix_metrics(i);
+
+    if (!(i->play_stream = pa_stream_new(i->context, stream_name(), &i->sample_spec, NULL))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    pa_stream_set_state_callback(i->play_stream, stream_state_cb, i);
+    pa_stream_set_write_callback(i->play_stream, stream_request_cb, i);
+    pa_stream_set_latency_update_callback(i->play_stream, stream_latency_update_cb, i);
+
+    memset(&attr, 0, sizeof(attr));
+    attr.maxlength = i->fragment_size * (i->n_fragments+1);
+    attr.tlength = i->fragment_size * i->n_fragments;
+    attr.prebuf = i->fragment_size;
+    attr.minreq = i->fragment_size;
+
+    flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE;
+    if (i->play_precork) {
+        flags |= PA_STREAM_START_CORKED;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": creating stream corked\n");
+    }
+    if (pa_stream_connect_playback(i->play_stream, NULL, &attr, flags, NULL, NULL) < 0) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    n = i->fragment_size;
+    setsockopt(i->app_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n));
+    n = i->fragment_size;
+    setsockopt(i->thread_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+static int create_record_stream(fd_info *i) {
+    pa_buffer_attr attr;
+    int n, flags;
+
+    assert(i);
+
+    fix_metrics(i);
+
+    if (!(i->rec_stream = pa_stream_new(i->context, stream_name(), &i->sample_spec, NULL))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    pa_stream_set_state_callback(i->rec_stream, stream_state_cb, i);
+    pa_stream_set_read_callback(i->rec_stream, stream_request_cb, i);
+    pa_stream_set_latency_update_callback(i->rec_stream, stream_latency_update_cb, i);
+
+    memset(&attr, 0, sizeof(attr));
+    attr.maxlength = i->fragment_size * (i->n_fragments+1);
+    attr.fragsize = i->fragment_size;
+
+    flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE;
+    if (i->rec_precork) {
+        flags |= PA_STREAM_START_CORKED;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": creating stream corked\n");
+    }
+    if (pa_stream_connect_record(i->rec_stream, NULL, &attr, flags) < 0) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    n = i->fragment_size;
+    setsockopt(i->app_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
+    n = i->fragment_size;
+    setsockopt(i->thread_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n));
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+static void free_streams(fd_info *i) {
+    assert(i);
+
+    if (i->play_stream) {
+        pa_stream_disconnect(i->play_stream);
+        pa_stream_unref(i->play_stream);
+        i->play_stream = NULL;
+        i->io_flags |= PA_IO_EVENT_INPUT;
+    }
+
+    if (i->rec_stream) {
+        pa_stream_disconnect(i->rec_stream);
+        pa_stream_unref(i->rec_stream);
+        i->rec_stream = NULL;
+        i->io_flags |= PA_IO_EVENT_OUTPUT;
+    }
+
+    if (i->io_event) {
+        pa_mainloop_api *api;
+
+        api = pa_threaded_mainloop_get_api(i->mainloop);
+        api->io_enable(i->io_event, i->io_flags);
+    }
+}
+
+static void io_event_cb(pa_mainloop_api *api, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
+    fd_info *i = userdata;
+
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+
+    if (flags & PA_IO_EVENT_INPUT) {
+
+        if (!i->play_stream) {
+            if (create_playback_stream(i) < 0)
+                goto fail;
+        } else {
+            if (fd_info_copy_data(i, 0) < 0)
+                goto fail;
+        }
+
+    } else if (flags & PA_IO_EVENT_OUTPUT) {
+
+        if (!i->rec_stream) {
+            if (create_record_stream(i) < 0)
+                goto fail;
+        } else {
+            if (fd_info_copy_data(i, 0) < 0)
+                goto fail;
+        }
+
+    } else if (flags & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR))
+        goto fail;
+
+    return;
+
+fail:
+    /* We can't do anything better than removing the event source */
+    fd_info_shutdown(i);
+}
+
+static int dsp_open(int flags, int *_errno) {
+    fd_info *i;
+    pa_mainloop_api *api;
+    int ret;
+    int f;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open()\n");
+
+    if (!(i = fd_info_new(FD_INFO_STREAM, _errno)))
+        return -1;
+
+    if ((flags & O_NONBLOCK) == O_NONBLOCK) {
+        if ((f = fcntl(i->app_fd, F_GETFL)) >= 0)
+            fcntl(i->app_fd, F_SETFL, f|O_NONBLOCK);
+    }
+    if ((f = fcntl(i->thread_fd, F_GETFL)) >= 0)
+        fcntl(i->thread_fd, F_SETFL, f|O_NONBLOCK);
+
+    fcntl(i->app_fd, F_SETFD, FD_CLOEXEC);
+    fcntl(i->thread_fd, F_SETFD, FD_CLOEXEC);
+
+    pa_threaded_mainloop_lock(i->mainloop);
+    api = pa_threaded_mainloop_get_api(i->mainloop);
+
+    switch (flags & O_ACCMODE) {
+    case O_RDONLY:
+        i->io_flags = PA_IO_EVENT_OUTPUT;
+        shutdown(i->thread_fd, SHUT_RD);
+        shutdown(i->app_fd, SHUT_WR);
+        break;
+    case O_WRONLY:
+        i->io_flags = PA_IO_EVENT_INPUT;
+        shutdown(i->thread_fd, SHUT_WR);
+        shutdown(i->app_fd, SHUT_RD);
+        break;
+    case O_RDWR:
+        i->io_flags = PA_IO_EVENT_INPUT | PA_IO_EVENT_OUTPUT;
+        break;
+    default:
+        return -1;
+    }
+
+    if (!(i->io_event = api->io_new(api, i->thread_fd, i->io_flags, io_event_cb, i)))
+        goto fail;
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open() succeeded, fd=%i\n", i->app_fd);
+
+    fd_info_add_to_list(i);
+    ret = i->app_fd;
+    fd_info_unref(i);
+
+    return ret;
+
+fail:
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    if (i)
+        fd_info_unref(i);
+
+    *_errno = EIO;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open() failed\n");
+
+    return -1;
+}
+
+static void sink_info_cb(pa_context *context, const pa_sink_info *si, int eol, void *userdata) {
+    fd_info *i = userdata;
+
+    if (!si && eol < 0) {
+        i->operation_success = 0;
+        pa_threaded_mainloop_signal(i->mainloop, 0);
+        return;
+    }
+
+    if (eol)
+        return;
+
+    if (!pa_cvolume_equal(&i->sink_volume, &si->volume))
+        i->volume_modify_count++;
+
+    i->sink_volume = si->volume;
+    i->sink_index = si->index;
+
+    i->operation_success = 1;
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+}
+
+static void source_info_cb(pa_context *context, const pa_source_info *si, int eol, void *userdata) {
+    fd_info *i = userdata;
+
+    if (!si && eol < 0) {
+        i->operation_success = 0;
+        pa_threaded_mainloop_signal(i->mainloop, 0);
+        return;
+    }
+
+    if (eol)
+        return;
+
+    if (!pa_cvolume_equal(&i->source_volume, &si->volume))
+        i->volume_modify_count++;
+
+    i->source_volume = si->volume;
+    i->source_index = si->index;
+
+    i->operation_success = 1;
+    pa_threaded_mainloop_signal(i->mainloop, 0);
+}
+
+static void subscribe_cb(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    fd_info *i = userdata;
+    pa_operation *o = NULL;
+
+    if (i->sink_index != idx)
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
+        return;
+
+    if (!(o = pa_context_get_sink_info_by_index(i->context, i->sink_index, sink_info_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
+        return;
+    }
+
+    pa_operation_unref(o);
+}
+
+static int mixer_open(int flags, int *_errno) {
+    fd_info *i;
+    pa_operation *o = NULL;
+    int ret;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open()\n");
+
+    if (!(i = fd_info_new(FD_INFO_MIXER, _errno)))
+        return -1;
+
+    pa_threaded_mainloop_lock(i->mainloop);
+
+    pa_context_set_subscribe_callback(i->context, subscribe_cb, i);
+
+    if (!(o = pa_context_subscribe(i->context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, context_success_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to subscribe to events: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        pa_threaded_mainloop_wait(i->mainloop);
+        CONTEXT_CHECK_DEAD_GOTO(i, fail);
+    }
+
+    pa_operation_unref(o);
+    o = NULL;
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__":Failed to subscribe to events: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    /* Get sink info */
+
+    if (!(o = pa_context_get_sink_info_by_name(i->context, NULL, sink_info_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        pa_threaded_mainloop_wait(i->mainloop);
+        CONTEXT_CHECK_DEAD_GOTO(i, fail);
+    }
+
+    pa_operation_unref(o);
+    o = NULL;
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    /* Get source info */
+
+    if (!(o = pa_context_get_source_info_by_name(i->context, NULL, source_info_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get source info: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        pa_threaded_mainloop_wait(i->mainloop);
+        CONTEXT_CHECK_DEAD_GOTO(i, fail);
+    }
+
+    pa_operation_unref(o);
+    o = NULL;
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get source info: %s", pa_strerror(pa_context_errno(i->context)));
+        *_errno = EIO;
+        goto fail;
+    }
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open() succeeded, fd=%i\n", i->app_fd);
+
+    fd_info_add_to_list(i);
+    ret = i->app_fd;
+    fd_info_unref(i);
+
+    return ret;
+
+fail:
+    if (o)
+        pa_operation_unref(o);
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    if (i)
+        fd_info_unref(i);
+
+    *_errno = EIO;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open() failed\n");
+
+    return -1;
+}
+
+static int sndstat_open(int flags, int *_errno) {
+    static const char sndstat[] =
+        "Sound Driver:3.8.1a-980706 (PulseAudio Virtual OSS)\n"
+        "Kernel: POSIX\n"
+        "Config options: 0\n"
+        "\n"
+        "Installed drivers:\n"
+        "Type 255: PulseAudio Virtual OSS\n"
+        "\n"
+        "Card config:\n"
+        "PulseAudio Virtual OSS\n"
+        "\n"
+        "Audio devices:\n"
+        "0: PulseAudio Virtual OSS\n"
+        "\n"
+        "Synth devices: NOT ENABLED IN CONFIG\n"
+        "\n"
+        "Midi devices:\n"
+        "\n"
+        "Timers:\n"
+        "\n"
+        "Mixers:\n"
+        "0: PulseAudio Virtual OSS\n";
+
+    char fn[] = "/tmp/padsp-sndstat-XXXXXX";
+    mode_t u;
+    int fd = -1;
+    int e;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": sndstat_open()\n");
+
+    if (flags != O_RDONLY
+#ifdef O_LARGEFILE
+        && flags != (O_RDONLY|O_LARGEFILE)
+#endif
+       ) {
+        *_errno = EACCES;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": bad access!\n");
+        goto fail;
+    }
+
+    u = umask(0077);
+    fd = mkstemp(fn);
+    e = errno;
+    umask(u);
+
+    if (fd < 0) {
+        *_errno = e;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": mkstemp() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    unlink(fn);
+
+    if (write(fd, sndstat, sizeof(sndstat) -1) != sizeof(sndstat)-1) {
+        *_errno = errno;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": write() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    if (lseek(fd, SEEK_SET, 0) < 0) {
+        *_errno = errno;
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": lseek() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    return fd;
+
+fail:
+    if (fd >= 0)
+        close(fd);
+    return -1;
+}
+
+static int real_open(const char *filename, int flags, mode_t mode) {
+    int r, _errno = 0;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": open(%s)\n", filename?filename:"NULL");
+
+    if (!function_enter()) {
+        LOAD_OPEN_FUNC();
+        return _open(filename, flags, mode);
+    }
+
+    if (filename && dsp_cloak_enable() && (strcmp(filename, "/dev/dsp") == 0 || strcmp(filename, "/dev/adsp") == 0))
+        r = dsp_open(flags, &_errno);
+    else if (filename && mixer_cloak_enable() && strcmp(filename, "/dev/mixer") == 0)
+        r = mixer_open(flags, &_errno);
+    else if (filename && sndstat_cloak_enable() && strcmp(filename, "/dev/sndstat") == 0)
+        r = sndstat_open(flags, &_errno);
+    else {
+        function_exit();
+        LOAD_OPEN_FUNC();
+        return _open(filename, flags, mode);
+    }
+
+    function_exit();
+
+    if (_errno)
+        errno = _errno;
+
+    return r;
+}
+
+int open(const char *filename, int flags, ...) {
+    va_list args;
+    mode_t mode = 0;
+
+    if (flags & O_CREAT) {
+        va_start(args, flags);
+        if (sizeof(mode_t) < sizeof(int))
+            mode = va_arg(args, int);
+        else
+            mode = va_arg(args, mode_t);
+        va_end(args);
+    }
+
+    return real_open(filename, flags, mode);
+}
+
+static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {
+    int ret = -1;
+
+    switch (request) {
+        case SOUND_MIXER_READ_DEVMASK :
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_DEVMASK\n");
+
+            *(int*) argp = SOUND_MASK_PCM | SOUND_MASK_IGAIN;
+            break;
+
+        case SOUND_MIXER_READ_RECMASK :
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_RECMASK\n");
+
+            *(int*) argp = SOUND_MASK_IGAIN;
+            break;
+
+        case SOUND_MIXER_READ_STEREODEVS:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_STEREODEVS\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+            *(int*) argp = 0;
+            if (i->sink_volume.channels > 1)
+                *(int*) argp |= SOUND_MASK_PCM;
+            if (i->source_volume.channels > 1)
+                *(int*) argp |= SOUND_MASK_IGAIN;
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            break;
+
+        case SOUND_MIXER_READ_RECSRC:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_RECSRC\n");
+
+            *(int*) argp = SOUND_MASK_IGAIN;
+            break;
+
+        case SOUND_MIXER_WRITE_RECSRC:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_RECSRC\n");
+            break;
+
+        case SOUND_MIXER_READ_CAPS:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_CAPS\n");
+
+            *(int*) argp = 0;
+            break;
+
+        case SOUND_MIXER_READ_PCM:
+        case SOUND_MIXER_READ_IGAIN: {
+            pa_cvolume *v;
+
+            if (request == SOUND_MIXER_READ_PCM)
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_PCM\n");
+            else
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_IGAIN\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            if (request == SOUND_MIXER_READ_PCM)
+                v = &i->sink_volume;
+            else
+                v = &i->source_volume;
+
+            *(int*) argp =
+                ((v->values[0]*100/PA_VOLUME_NORM)) |
+                ((v->values[v->channels > 1 ? 1 : 0]*100/PA_VOLUME_NORM)  << 8);
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            break;
+        }
+
+        case SOUND_MIXER_WRITE_PCM:
+        case SOUND_MIXER_WRITE_IGAIN: {
+            pa_cvolume v, *pv;
+
+            if (request == SOUND_MIXER_WRITE_PCM)
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_PCM\n");
+            else
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_IGAIN\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            if (request == SOUND_MIXER_WRITE_PCM) {
+                v = i->sink_volume;
+                pv = &i->sink_volume;
+            } else {
+                v = i->source_volume;
+                pv = &i->source_volume;
+            }
+
+            pv->values[0] = ((*(int*) argp & 0xFF)*PA_VOLUME_NORM)/100;
+            pv->values[1] = ((*(int*) argp >> 8)*PA_VOLUME_NORM)/100;
+
+            if (!pa_cvolume_equal(pv, &v)) {
+                pa_operation *o;
+
+                if (request == SOUND_MIXER_WRITE_PCM)
+                    o = pa_context_set_sink_volume_by_index(i->context, i->sink_index, pv, context_success_cb, i);
+                else
+                    o = pa_context_set_source_volume_by_index(i->context, i->source_index, pv, context_success_cb, i);
+
+                if (!o)
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__":Failed set volume: %s", pa_strerror(pa_context_errno(i->context)));
+                else {
+
+                    i->operation_success = 0;
+                    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+                        CONTEXT_CHECK_DEAD_GOTO(i, exit_loop);
+
+                        pa_threaded_mainloop_wait(i->mainloop);
+                    }
+                exit_loop:
+
+                    if (!i->operation_success)
+                        debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to set volume: %s\n", pa_strerror(pa_context_errno(i->context)));
+
+                    pa_operation_unref(o);
+                }
+
+                /* We don't wait for completion here */
+                i->volume_modify_count++;
+            }
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            break;
+        }
+
+        case SOUND_MIXER_INFO: {
+            mixer_info *mi = argp;
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_INFO\n");
+
+            memset(mi, 0, sizeof(mixer_info));
+            strncpy(mi->id, "PULSEAUDIO", sizeof(mi->id));
+            strncpy(mi->name, "PulseAudio Virtual OSS", sizeof(mi->name));
+            pa_threaded_mainloop_lock(i->mainloop);
+            mi->modify_counter = i->volume_modify_count;
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+        }
+
+        default:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": unknown ioctl 0x%08lx\n", request);
+
+            *_errno = EINVAL;
+            goto fail;
+    }
+
+    ret = 0;
+
+fail:
+
+    return ret;
+}
+
+static int map_format(int *fmt, pa_sample_spec *ss) {
+
+    switch (*fmt) {
+        case AFMT_MU_LAW:
+            ss->format = PA_SAMPLE_ULAW;
+            break;
+
+        case AFMT_A_LAW:
+            ss->format = PA_SAMPLE_ALAW;
+            break;
+
+        case AFMT_S8:
+            *fmt = AFMT_U8;
+            /* fall through */
+        case AFMT_U8:
+            ss->format = PA_SAMPLE_U8;
+            break;
+
+        case AFMT_U16_BE:
+            *fmt = AFMT_S16_BE;
+            /* fall through */
+        case AFMT_S16_BE:
+            ss->format = PA_SAMPLE_S16BE;
+            break;
+
+        case AFMT_U16_LE:
+            *fmt = AFMT_S16_LE;
+            /* fall through */
+        case AFMT_S16_LE:
+            ss->format = PA_SAMPLE_S16LE;
+            break;
+
+        default:
+            ss->format = PA_SAMPLE_S16NE;
+            *fmt = AFMT_S16_NE;
+            break;
+    }
+
+    return 0;
+}
+
+static int map_format_back(pa_sample_format_t format) {
+    switch (format) {
+        case PA_SAMPLE_S16LE: return AFMT_S16_LE;
+        case PA_SAMPLE_S16BE: return AFMT_S16_BE;
+        case PA_SAMPLE_ULAW: return AFMT_MU_LAW;
+        case PA_SAMPLE_ALAW: return AFMT_A_LAW;
+        case PA_SAMPLE_U8: return AFMT_U8;
+        default:
+            abort();
+    }
+}
+
+static int dsp_flush_fd(int fd) {
+#ifdef SIOCINQ
+    int l;
+
+    if (ioctl(fd, SIOCINQ, &l) < 0) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ: %s\n", strerror(errno));
+        return -1;
+    }
+
+    while (l > 0) {
+        char buf[1024];
+        size_t k;
+
+        k = (size_t) l > sizeof(buf) ? sizeof(buf) : (size_t) l;
+        if (read(fd, buf, k) < 0)
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": read(): %s\n", strerror(errno));
+        l -= k;
+    }
+
+    return 0;
+#else
+# warning "Your platform does not support SIOCINQ, something might not work as intended."
+    return 0;
+#endif
+}
+
+static int dsp_flush_socket(fd_info *i) {
+    int res = 0;
+
+    if ((i->thread_fd < 0) && (i->app_fd < 0))
+        return -1;
+
+    if (i->thread_fd >= 0)
+        res = dsp_flush_fd(i->thread_fd);
+
+    if (res < 0)
+        return res;
+
+    if (i->app_fd >= 0)
+        res = dsp_flush_fd(i->app_fd);
+
+    if (res < 0)
+        return res;
+
+    return 0;
+}
+
+static int dsp_empty_socket(fd_info *i) {
+#ifdef SIOCINQ
+    int ret = -1;
+
+    /* Empty the socket */
+    for (;;) {
+        int l;
+
+        if (i->thread_fd < 0)
+            break;
+
+        if (ioctl(i->thread_fd, SIOCINQ, &l) < 0) {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ: %s\n", strerror(errno));
+            break;
+        }
+
+        if (!l) {
+            ret = 0;
+            break;
+        }
+
+        pa_threaded_mainloop_wait(i->mainloop);
+    }
+
+    return ret;
+#else
+# warning "Your platform does not support SIOCINQ, something might not work as intended."
+    return 0;
+#endif
+}
+
+static int dsp_drain(fd_info *i) {
+    pa_operation *o = NULL;
+    int r = -1;
+
+    if (!i->mainloop)
+        return 0;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Draining.\n");
+
+    pa_threaded_mainloop_lock(i->mainloop);
+
+    if (dsp_empty_socket(i) < 0)
+        goto fail;
+
+    if (!i->play_stream)
+        goto fail;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Really draining.\n");
+
+    if (!(o = pa_stream_drain(i->play_stream, stream_success_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drain(): %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
+
+        pa_threaded_mainloop_wait(i->mainloop);
+    }
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drain() 2: %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    r = 0;
+
+fail:
+
+    if (o)
+        pa_operation_unref(o);
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    return 0;
+}
+
+static int dsp_trigger(fd_info *i) {
+    pa_operation *o = NULL;
+    int r = -1;
+
+    if (!i->play_stream)
+        return 0;
+
+    pa_threaded_mainloop_lock(i->mainloop);
+
+    if (dsp_empty_socket(i) < 0)
+        goto fail;
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": Triggering.\n");
+
+    if (!(o = pa_stream_trigger(i->play_stream, stream_success_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_trigger(): %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (!pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
+
+        pa_threaded_mainloop_wait(i->mainloop);
+    }
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_trigger(): %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    r = 0;
+
+fail:
+
+    if (o)
+        pa_operation_unref(o);
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    return 0;
+}
+
+static int dsp_cork(fd_info *i, pa_stream *s, int b) {
+    pa_operation *o = NULL;
+    int r = -1;
+
+    pa_threaded_mainloop_lock(i->mainloop);
+
+    if (!(o = pa_stream_cork(s, b, stream_success_cb, i))) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_cork(): %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    i->operation_success = 0;
+    while (!pa_operation_get_state(o) != PA_OPERATION_DONE) {
+        if (s == i->play_stream)
+            PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
+        else if (s == i->rec_stream)
+            RECORD_STREAM_CHECK_DEAD_GOTO(i, fail);
+
+        pa_threaded_mainloop_wait(i->mainloop);
+    }
+
+    if (!i->operation_success) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_cork(): %s\n", pa_strerror(pa_context_errno(i->context)));
+        goto fail;
+    }
+
+    r = 0;
+
+fail:
+
+    if (o)
+        pa_operation_unref(o);
+
+    pa_threaded_mainloop_unlock(i->mainloop);
+
+    return 0;
+}
+
+static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {
+    int ret = -1;
+
+    if (i->thread_fd == -1) {
+        /*
+         * We've encountered some fatal error and are just waiting
+         * for a close.
+         */
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": got ioctl 0x%08lx in fatal error state\n", request);
+        *_errno = EIO;
+        return -1;
+    }
+
+    switch (request) {
+        case SNDCTL_DSP_SETFMT: {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETFMT: %i\n", *(int*) argp);
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            if (*(int*) argp == AFMT_QUERY)
+                *(int*) argp = map_format_back(i->sample_spec.format);
+            else {
+                map_format((int*) argp, &i->sample_spec);
+                free_streams(i);
+            }
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+        }
+
+        case SNDCTL_DSP_SPEED: {
+            pa_sample_spec ss;
+            int valid;
+            char t[256];
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SPEED: %i\n", *(int*) argp);
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            ss = i->sample_spec;
+            ss.rate = *(int*) argp;
+
+            if ((valid = pa_sample_spec_valid(&ss))) {
+                i->sample_spec = ss;
+                free_streams(i);
+            }
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": ss: %s\n", pa_sample_spec_snprint(t, sizeof(t), &i->sample_spec));
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            if (!valid) {
+                *_errno = EINVAL;
+                goto fail;
+            }
+
+            break;
+        }
+
+        case SNDCTL_DSP_STEREO:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_STEREO: %i\n", *(int*) argp);
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            i->sample_spec.channels = *(int*) argp ? 2 : 1;
+            free_streams(i);
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+            return 0;
+
+        case SNDCTL_DSP_CHANNELS: {
+            pa_sample_spec ss;
+            int valid;
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_CHANNELS: %i\n", *(int*) argp);
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            ss = i->sample_spec;
+            ss.channels = *(int*) argp;
+
+            if ((valid = pa_sample_spec_valid(&ss))) {
+                i->sample_spec = ss;
+                free_streams(i);
+            }
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            if (!valid) {
+                *_errno = EINVAL;
+                goto fail;
+            }
+
+            break;
+        }
+
+        case SNDCTL_DSP_GETBLKSIZE:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETBLKSIZE\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            fix_metrics(i);
+            *(int*) argp = i->fragment_size;
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            break;
+
+        case SNDCTL_DSP_SETFRAGMENT:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETFRAGMENT: 0x%08x\n", *(int*) argp);
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            i->fragment_size = 1 << ((*(int*) argp) & 31);
+            i->n_fragments = (*(int*) argp) >> 16;
+
+            /* 0x7FFF means that we can set whatever we like */
+            if (i->n_fragments == 0x7FFF)
+                i->n_fragments = 12;
+
+            free_streams(i);
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            break;
+
+        case SNDCTL_DSP_GETCAPS:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_CAPS\n");
+
+            *(int*)  argp = DSP_CAP_DUPLEX | DSP_CAP_TRIGGER
+#ifdef DSP_CAP_MULTI
+              | DSP_CAP_MULTI
+#endif
+              ;
+            break;
+
+        case SNDCTL_DSP_GETODELAY: {
+            int l;
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETODELAY\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            *(int*) argp = 0;
+
+            for (;;) {
+                pa_usec_t usec;
+
+                PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, exit_loop);
+
+                if (pa_stream_get_latency(i->play_stream, &usec, NULL) >= 0) {
+                    *(int*) argp = pa_usec_to_bytes(usec, &i->sample_spec);
+                    break;
+                }
+
+                if (pa_context_errno(i->context) != PA_ERR_NODATA) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_get_latency(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                    break;
+                }
+
+                pa_threaded_mainloop_wait(i->mainloop);
+            }
+
+        exit_loop:
+
+#ifdef SIOCINQ
+            if (ioctl(i->thread_fd, SIOCINQ, &l) < 0)
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ failed: %s\n", strerror(errno));
+            else
+                *(int*) argp += l;
+#else
+# warning "Your platform does not support SIOCINQ, something might not work as intended."
+#endif
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": ODELAY: %i\n", *(int*) argp);
+
+            break;
+        }
+
+        case SNDCTL_DSP_RESET: {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_RESET\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            free_streams(i);
+            dsp_flush_socket(i);
+
+            i->optr_n_blocks = 0;
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+        }
+
+        case SNDCTL_DSP_GETFMTS: {
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETFMTS\n");
+
+            *(int*) argp = AFMT_MU_LAW|AFMT_A_LAW|AFMT_U8|AFMT_S16_LE|AFMT_S16_BE;
+            break;
+        }
+
+        case SNDCTL_DSP_POST:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_POST\n");
+
+            if (dsp_trigger(i) < 0)
+                *_errno = EIO;
+            break;
+
+        case SNDCTL_DSP_GETTRIGGER:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETTRIGGER\n");
+
+            *(int*) argp = 0;
+            if (!i->play_precork)
+                *(int*) argp |= PCM_ENABLE_OUTPUT;
+            if (!i->rec_precork)
+                *(int*) argp |= PCM_ENABLE_INPUT;
+
+            break;
+
+        case SNDCTL_DSP_SETTRIGGER:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETTRIGGER: 0x%08x\n", *(int*) argp);
+
+            if (!i->io_event) {
+                *_errno = EIO;
+                break;
+            }
+
+            i->play_precork = !((*(int*) argp) & PCM_ENABLE_OUTPUT);
+
+            if (i->play_stream) {
+                if (dsp_cork(i, i->play_stream, !((*(int*) argp) & PCM_ENABLE_OUTPUT)) < 0)
+                    *_errno = EIO;
+                if (dsp_trigger(i) < 0)
+                    *_errno = EIO;
+            }
+
+            i->rec_precork = !((*(int*) argp) & PCM_ENABLE_INPUT);
+
+            if (i->rec_stream) {
+                if (dsp_cork(i, i->rec_stream, !((*(int*) argp) & PCM_ENABLE_INPUT)) < 0)
+                    *_errno = EIO;
+            }
+
+            break;
+
+        case SNDCTL_DSP_SYNC:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SYNC\n");
+
+            if (dsp_drain(i) < 0)
+                *_errno = EIO;
+
+            break;
+
+        case SNDCTL_DSP_GETOSPACE:
+        case SNDCTL_DSP_GETISPACE: {
+            audio_buf_info *bi = (audio_buf_info*) argp;
+            int l = 0;
+            size_t k = 0;
+
+            if (request == SNDCTL_DSP_GETOSPACE)
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETOSPACE\n");
+            else
+                debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETISPACE\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            fix_metrics(i);
+
+            if (request == SNDCTL_DSP_GETOSPACE) {
+                if (i->play_stream) {
+                    if ((k = pa_stream_writable_size(i->play_stream)) == (size_t) -1)
+                        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_writable_size(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                } else
+                    k = i->fragment_size * i->n_fragments;
+
+#ifdef SIOCINQ
+                if (ioctl(i->thread_fd, SIOCINQ, &l) < 0) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ failed: %s\n", strerror(errno));
+                    l = 0;
+                }
+#else
+# warning "Your platform does not dsp_flush_fd, something might not work as intended."
+#endif
+
+                bi->bytes = k > (size_t) l ? k - l : 0;
+            } else {
+                if (i->rec_stream) {
+                    if ((k = pa_stream_readable_size(i->rec_stream)) == (size_t) -1)
+                        debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_readable_size(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                } else
+                    k = 0;
+
+#ifdef SIOCINQ
+                if (ioctl(i->app_fd, SIOCINQ, &l) < 0) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ failed: %s\n", strerror(errno));
+                    l = 0;
+                }
+#else
+# warning "Your platform does not dsp_flush_fd, something might not work as intended."
+#endif
+                bi->bytes = k + l;
+            }
+
+            bi->fragsize = i->fragment_size;
+            bi->fragstotal = i->n_fragments;
+            bi->fragments = bi->bytes / bi->fragsize;
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": fragsize=%i, fragstotal=%i, bytes=%i, fragments=%i\n", bi->fragsize, bi->fragstotal, bi->bytes, bi->fragments);
+
+            break;
+        }
+
+        case SOUND_PCM_READ_RATE:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_RATE\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+            *(int*) argp = i->sample_spec.rate;
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+
+        case SOUND_PCM_READ_CHANNELS:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_CHANNELS\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+            *(int*) argp = i->sample_spec.channels;
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+
+        case SOUND_PCM_READ_BITS:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_BITS\n");
+
+            pa_threaded_mainloop_lock(i->mainloop);
+            *(int*) argp = pa_sample_size(&i->sample_spec)*8;
+            pa_threaded_mainloop_unlock(i->mainloop);
+            break;
+
+        case SNDCTL_DSP_GETOPTR: {
+            count_info *info;
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETOPTR\n");
+
+            info = (count_info*) argp;
+            memset(info, 0, sizeof(*info));
+
+            pa_threaded_mainloop_lock(i->mainloop);
+
+            for (;;) {
+                pa_usec_t usec;
+
+                PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, exit_loop);
+
+                if (pa_stream_get_time(i->play_stream, &usec) >= 0) {
+                    size_t k = pa_usec_to_bytes(usec, &i->sample_spec);
+                    int m;
+
+                    info->bytes = (int) k;
+                    m = k / i->fragment_size;
+                    info->blocks = m - i->optr_n_blocks;
+                    i->optr_n_blocks = m;
+
+                    break;
+                }
+
+                if (pa_context_errno(i->context) != PA_ERR_NODATA) {
+                    debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_get_latency(): %s\n", pa_strerror(pa_context_errno(i->context)));
+                    break;
+                }
+
+                pa_threaded_mainloop_wait(i->mainloop);
+            }
+
+            pa_threaded_mainloop_unlock(i->mainloop);
+
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": GETOPTR bytes=%i, blocks=%i, ptr=%i\n", info->bytes, info->blocks, info->ptr);
+
+            break;
+        }
+
+        case SNDCTL_DSP_GETIPTR:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": invalid ioctl SNDCTL_DSP_GETIPTR\n");
+            goto inval;
+
+        case SNDCTL_DSP_SETDUPLEX:
+            debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETDUPLEX\n");
+            /* this is a no-op */
+            break;
+
+        default:
+            /* Mixer ioctls are valid on /dev/dsp aswell */
+            return mixer_ioctl(i, request, argp, _errno);
+
+inval:
+            *_errno = EINVAL;
+            goto fail;
+    }
+
+    ret = 0;
+
+fail:
+
+    return ret;
+}
+
+#ifdef sun
+int ioctl(int fd, int request, ...) {
+#else
+int ioctl(int fd, unsigned long request, ...) {
+#endif
+    fd_info *i;
+    va_list args;
+    void *argp;
+    int r, _errno = 0;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": ioctl()\n");
+
+    va_start(args, request);
+    argp = va_arg(args, void *);
+    va_end(args);
+
+    if (!function_enter()) {
+        LOAD_IOCTL_FUNC();
+        return _ioctl(fd, request, argp);
+    }
+
+    if (!(i = fd_info_find(fd))) {
+        function_exit();
+        LOAD_IOCTL_FUNC();
+        return _ioctl(fd, request, argp);
+    }
+
+    if (i->type == FD_INFO_MIXER)
+        r = mixer_ioctl(i, request, argp, &_errno);
+    else
+        r = dsp_ioctl(i, request, argp, &_errno);
+
+    fd_info_unref(i);
+
+    if (_errno)
+        errno = _errno;
+
+    function_exit();
+
+    return r;
+}
+
+int close(int fd) {
+    fd_info *i;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": close()\n");
+
+    if (!function_enter()) {
+        LOAD_CLOSE_FUNC();
+        return _close(fd);
+    }
+
+    if (!(i = fd_info_find(fd))) {
+        function_exit();
+        LOAD_CLOSE_FUNC();
+        return _close(fd);
+    }
+
+    fd_info_remove_from_list(i);
+    fd_info_unref(i);
+
+    function_exit();
+
+    return 0;
+}
+
+int access(const char *pathname, int mode) {
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": access(%s)\n", pathname?pathname:"NULL");
+
+    if (!pathname ||
+        ( strcmp(pathname, "/dev/dsp") != 0 &&
+          strcmp(pathname, "/dev/adsp") != 0 &&
+          strcmp(pathname, "/dev/sndstat") != 0 &&
+          strcmp(pathname, "/dev/mixer") != 0 )) {
+        LOAD_ACCESS_FUNC();
+        return _access(pathname, mode);
+    }
+
+    if (mode & (W_OK | X_OK)) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": access(%s, %x) = EACCESS\n", pathname, mode);
+        errno = EACCES;
+        return -1;
+    }
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": access(%s, %x) = OK\n", pathname, mode);
+
+    return 0;
+}
+
+int stat(const char *pathname, struct stat *buf) {
+#ifdef HAVE_OPEN64
+    struct stat64 parent;
+#else
+    struct stat parent;
+#endif
+    int ret;
+
+    if (!pathname ||
+        !buf ||
+        ( strcmp(pathname, "/dev/dsp") != 0 &&
+          strcmp(pathname, "/dev/adsp") != 0 &&
+          strcmp(pathname, "/dev/sndstat") != 0 &&
+          strcmp(pathname, "/dev/mixer") != 0 )) {
+        debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat(%s)\n", pathname?pathname:"NULL");
+        LOAD_STAT_FUNC();
+        return _stat(pathname, buf);
+    }
+
+    debug(DEBUG_LEVEL_NORMAL, __FILE__": stat(%s)\n", pathname);
+
+#ifdef _STAT_VER
+#ifdef HAVE_OPEN64
+    ret = __xstat64(_STAT_VER, "/dev", &parent);
+#else
+    ret = __xstat(_STAT_VER, "/dev", &parent);
+#endif
+#else
+#ifdef HAVE_OPEN64
+    ret = stat64("/dev", &parent);
+#else
+    ret = stat("/dev", &parent);
+#endif
+#endif
+
+    if (ret) {
+        debug(DEBUG_LEVEL_NORMAL, __FILE__": unable to stat \"/dev\"\n");
+        return -1;
+    }
+
+    buf->st_dev = parent.st_dev;
+    buf->st_ino = 0xDEADBEEF;   /* FIXME: Can we do this in a safe way? */
+    buf->st_mode = S_IFCHR | S_IRUSR | S_IWUSR;
+    buf->st_nlink = 1;
+    buf->st_uid = getuid();
+    buf->st_gid = getgid();
+    buf->st_rdev = 0x0E03;      /* FIXME: Linux specific */
+    buf->st_size = 0;
+    buf->st_atime = 1181557705;
+    buf->st_mtime = 1181557705;
+    buf->st_ctime = 1181557705;
+    buf->st_blksize = 1;
+    buf->st_blocks = 0;
+
+    return 0;
+}
+
+#ifdef HAVE_OPEN64
+
+int stat64(const char *pathname, struct stat64 *buf) {
+    struct stat oldbuf;
+    int ret;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat64(%s)\n", pathname?pathname:"NULL");
+
+    if (!pathname ||
+        !buf ||
+        ( strcmp(pathname, "/dev/dsp") != 0 &&
+          strcmp(pathname, "/dev/adsp") != 0 &&
+          strcmp(pathname, "/dev/sndstat") != 0 &&
+          strcmp(pathname, "/dev/mixer") != 0 )) {
+        LOAD_STAT64_FUNC();
+        return _stat64(pathname, buf);
+    }
+
+    ret = stat(pathname, &oldbuf);
+    if (ret)
+        return ret;
+
+    buf->st_dev = oldbuf.st_dev;
+    buf->st_ino = oldbuf.st_ino;
+    buf->st_mode = oldbuf.st_mode;
+    buf->st_nlink = oldbuf.st_nlink;
+    buf->st_uid = oldbuf.st_uid;
+    buf->st_gid = oldbuf.st_gid;
+    buf->st_rdev = oldbuf.st_rdev;
+    buf->st_size = oldbuf.st_size;
+    buf->st_atime = oldbuf.st_atime;
+    buf->st_mtime = oldbuf.st_mtime;
+    buf->st_ctime = oldbuf.st_ctime;
+    buf->st_blksize = oldbuf.st_blksize;
+    buf->st_blocks = oldbuf.st_blocks;
+
+    return 0;
+}
+
+int open64(const char *filename, int flags, ...) {
+    va_list args;
+    mode_t mode = 0;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": open64(%s)\n", filename?filename:"NULL");
+
+    if (flags & O_CREAT) {
+        va_start(args, flags);
+        if (sizeof(mode_t) < sizeof(int))
+            mode = va_arg(args, int);
+        else
+            mode = va_arg(args, mode_t);
+        va_end(args);
+    }
+
+    if (!filename ||
+        ( strcmp(filename, "/dev/dsp") != 0 &&
+          strcmp(filename, "/dev/adsp") != 0 &&
+          strcmp(filename, "/dev/sndstat") != 0 &&
+          strcmp(filename, "/dev/mixer") != 0 )) {
+        LOAD_OPEN64_FUNC();
+        return _open64(filename, flags, mode);
+    }
+
+    return real_open(filename, flags, mode);
+}
+
+#endif
+
+#ifdef _STAT_VER
+
+int __xstat(int ver, const char *pathname, struct stat *buf) {
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat(%s)\n", pathname?pathname:"NULL");
+
+    if (!pathname ||
+        !buf ||
+        ( strcmp(pathname, "/dev/dsp") != 0 &&
+          strcmp(pathname, "/dev/adsp") != 0 &&
+          strcmp(pathname, "/dev/sndstat") != 0 &&
+          strcmp(pathname, "/dev/mixer") != 0 )) {
+        LOAD_XSTAT_FUNC();
+        return ___xstat(ver, pathname, buf);
+    }
+
+    if (ver != _STAT_VER) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    return stat(pathname, buf);
+}
+
+#ifdef HAVE_OPEN64
+
+int __xstat64(int ver, const char *pathname, struct stat64 *buf) {
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat64(%s)\n", pathname?pathname:"NULL");
+
+    if (!pathname ||
+        !buf ||
+        ( strcmp(pathname, "/dev/dsp") != 0 &&
+          strcmp(pathname, "/dev/adsp") != 0 &&
+          strcmp(pathname, "/dev/sndstat") != 0 &&
+          strcmp(pathname, "/dev/mixer") != 0 )) {
+        LOAD_XSTAT64_FUNC();
+        return ___xstat64(ver, pathname, buf);
+    }
+
+    if (ver != _STAT_VER) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    return stat64(pathname, buf);
+}
+
+#endif
+
+#endif
+
+FILE* fopen(const char *filename, const char *mode) {
+    FILE *f = NULL;
+    int fd;
+    mode_t m;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen(%s)\n", filename?filename:"NULL");
+
+    if (!filename ||
+        !mode ||
+        ( strcmp(filename, "/dev/dsp") != 0 &&
+          strcmp(filename, "/dev/adsp") != 0 &&
+          strcmp(filename, "/dev/sndstat") != 0 &&
+          strcmp(filename, "/dev/mixer") != 0 )) {
+        LOAD_FOPEN_FUNC();
+        return _fopen(filename, mode);
+    }
+
+    switch (mode[0]) {
+    case 'r':
+        m = O_RDONLY;
+        break;
+    case 'w':
+    case 'a':
+        m = O_WRONLY;
+        break;
+    default:
+        errno = EINVAL;
+        return NULL;
+    }
+
+    if ((((mode[1] == 'b') || (mode[1] == 't')) && (mode[2] == '+')) || (mode[1] == '+'))
+        m = O_RDWR;
+
+    if ((fd = real_open(filename, m, 0)) < 0)
+        return NULL;
+
+    if (!(f = fdopen(fd, mode))) {
+        close(fd);
+        return NULL;
+    }
+
+    return f;
+}
+
+#ifdef HAVE_OPEN64
+
+FILE *fopen64(const char *filename, const char *mode) {
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen64(%s)\n", filename?filename:"NULL");
+
+    if (!filename ||
+        !mode ||
+        ( strcmp(filename, "/dev/dsp") != 0 &&
+          strcmp(filename, "/dev/adsp") != 0 &&
+          strcmp(filename, "/dev/sndstat") != 0 &&
+          strcmp(filename, "/dev/mixer") != 0 )) {
+        LOAD_FOPEN64_FUNC();
+        return _fopen64(filename, mode);
+    }
+
+    return fopen(filename, mode);
+}
+
+#endif
+
+int fclose(FILE *f) {
+    fd_info *i;
+
+    debug(DEBUG_LEVEL_VERBOSE, __FILE__": fclose()\n");
+
+    if (!function_enter()) {
+        LOAD_FCLOSE_FUNC();
+        return _fclose(f);
+    }
+
+    if (!(i = fd_info_find(fileno(f)))) {
+        function_exit();
+        LOAD_FCLOSE_FUNC();
+        return _fclose(f);
+    }
+
+    fd_info_remove_from_list(i);
+
+    /* Dirty trick to avoid that the fd is not freed twice, once by us
+     * and once by the real fclose() */
+    i->app_fd = -1;
+
+    fd_info_unref(i);
+
+    function_exit();
+
+    LOAD_FCLOSE_FUNC();
+    return _fclose(f);
+}
similarity index 64%
rename from polyp/paplay.c
rename to src/utils/paplay.c
index 2eaf07c1e0c4f5ad8b8f35efd641bae8d28c2727..1b6228b125a77ac404c3b3c99e11723374339b91 100644 (file)
@@ -1,20 +1,21 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <stdio.h>
 #include <stdlib.h>
 #include <getopt.h>
+#include <locale.h>
 
 #include <sndfile.h>
 
-#include <polyp/polyplib.h>
-#include <polyp/polyplib-error.h>
-#include <polyp/mainloop.h>
-#include <polyp/mainloop-signal.h>
-#include <polyp/polyplib-version.h>
+#include <pulse/pulseaudio.h>
 
-#if PA_API_VERSION != 8
-#error Invalid Polypaudio API version
+#if PA_API_VERSION < 9
+#error Invalid PulseAudio API version
 #endif
 
-static struct pa_context *context = NULL;
-static struct pa_stream *stream = NULL;
-static struct pa_mainloop_api *mainloop_api = NULL;
+static pa_context *context = NULL;
+static pa_stream *stream = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
 
 static char *stream_name = NULL, *client_name = NULL, *device = NULL;
 
@@ -55,9 +53,11 @@ static pa_volume_t volume = PA_VOLUME_NORM;
 
 static SNDFILE* sndfile = NULL;
 
-static struct pa_sample_spec sample_spec = { 0, 0, 0 }; 
+static pa_sample_spec sample_spec = { 0, 0, 0 };
+static pa_channel_map channel_map;
+static int channel_map_set = 0;
 
-static sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
+static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames) = NULL;
 
 /* A shortcut for terminating the application */
 static void quit(int ret) {
@@ -66,26 +66,26 @@ static void quit(int ret) {
 }
 
 /* Connection draining complete */
-static void context_drain_complete(struct pa_context*c, void *userdata) {
+static void context_drain_complete(pa_context*c, void *userdata) {
     pa_context_disconnect(c);
 }
 
 /* Stream draining complete */
-static void stream_drain_complete(struct pa_stream*s, int success, void *userdata) {
-    struct pa_operation *o;
+static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
+    pa_operation *o;
 
     if (!success) {
         fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
         quit(1);
     }
-    
-    if (verbose)    
+
+    if (verbose)
         fprintf(stderr, "Playback stream drained.\n");
 
     pa_stream_disconnect(stream);
     pa_stream_unref(stream);
     stream = NULL;
-    
+
     if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
         pa_context_disconnect(context);
     else {
@@ -97,27 +97,31 @@ static void stream_drain_complete(struct pa_stream*s, int success, void *userdat
 }
 
 /* This is called whenever new data may be written to the stream */
-static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) {
-    size_t k;
-    sf_count_t f, n;
+static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
+    sf_count_t bytes;
     void *data;
     assert(s && length);
 
     if (!sndfile)
         return;
-    
-    k = pa_frame_size(&sample_spec);
 
-    data = malloc(length);
+    data = pa_xmalloc(length);
+
+    if (readf_function) {
+        size_t k = pa_frame_size(&sample_spec);
+
+        if ((bytes = readf_function(sndfile, data, length/k)) > 0)
+            bytes *= k;
 
-    n = length/k;
-    
-    f = readf_function(sndfile, data, n);
+    } else
+        bytes = sf_read_raw(sndfile, data, length);
 
-    if (f > 0)
-        pa_stream_write(s, data, f*k, free, 0);
+    if (bytes > 0)
+        pa_stream_write(s, data, bytes, pa_xfree, 0, PA_SEEK_RELATIVE);
+    else
+        pa_xfree(data);
 
-    if (f < n) {
+    if (bytes < (sf_count_t) length) {
         sf_close(sndfile);
         sndfile = NULL;
         pa_operation_unref(pa_stream_drain(s, stream_drain_complete, NULL));
@@ -125,7 +129,7 @@ static void stream_write_callback(struct pa_stream *s, size_t length, void *user
 }
 
 /* This routine is called whenever the stream state changes */
-static void stream_state_callback(struct pa_stream *s, void *userdata) {
+static void stream_state_callback(pa_stream *s, void *userdata) {
     assert(s);
 
     switch (pa_stream_get_state(s)) {
@@ -137,7 +141,7 @@ static void stream_state_callback(struct pa_stream *s, void *userdata) {
             if (verbose)
                 fprintf(stderr, "Stream successfully created\n");
             break;
-            
+
         case PA_STREAM_FAILED:
         default:
             fprintf(stderr, "Stream errror: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
@@ -146,7 +150,7 @@ static void stream_state_callback(struct pa_stream *s, void *userdata) {
 }
 
 /* This is called whenever the context status changes */
-static void context_state_callback(struct pa_context *c, void *userdata) {
+static void context_state_callback(pa_context *c, void *userdata) {
     assert(c);
 
     switch (pa_context_get_state(c)) {
@@ -154,23 +158,25 @@ static void context_state_callback(struct pa_context *c, void *userdata) {
         case PA_CONTEXT_AUTHORIZING:
         case PA_CONTEXT_SETTING_NAME:
             break;
-        
-        case PA_CONTEXT_READY:
-            
+
+        case PA_CONTEXT_READY: {
+            pa_cvolume cv;
+
             assert(c && !stream);
 
             if (verbose)
                 fprintf(stderr, "Connection established.\n");
 
-            stream = pa_stream_new(c, stream_name, &sample_spec);
+            stream = pa_stream_new(c, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL);
             assert(stream);
 
             pa_stream_set_state_callback(stream, stream_state_callback, NULL);
             pa_stream_set_write_callback(stream, stream_write_callback, NULL);
-            pa_stream_connect_playback(stream, device, NULL, 0, volume);
-                
+            pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL);
+
             break;
-            
+        }
+
         case PA_CONTEXT_TERMINATED:
             quit(0);
             break;
@@ -183,11 +189,11 @@ static void context_state_callback(struct pa_context *c, void *userdata) {
 }
 
 /* UNIX signal to quit recieved */
-static void exit_signal_callback(struct pa_mainloop_api*m, struct pa_signal_event *e, int sig, void *userdata) {
+static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
     if (verbose)
         fprintf(stderr, "Got SIGINT, exiting.\n");
     quit(0);
-    
+
 }
 
 static void help(const char *argv0) {
@@ -195,25 +201,28 @@ static void help(const char *argv0) {
     printf("%s [options] [FILE]\n\n"
            "  -h, --help                            Show this help\n"
            "      --version                         Show version\n\n"
-           "  -v, --verbose                         Enable verbose operations\n\n"
+           "  -v, --verbose                         Enable verbose operation\n\n"
            "  -s, --server=SERVER                   The name of the server to connect to\n"
-           "  -d, --device=DEVICE                   The name of the sink/source to connect to\n"
+           "  -d, --device=DEVICE                   The name of the sink to connect to\n"
            "  -n, --client-name=NAME                How to call this client on the server\n"
            "      --stream-name=NAME                How to call this stream on the server\n"
-           "      --volume=VOLUME                   Specify the initial (linear) volume in range 0...256\n",
+           "      --volume=VOLUME                   Specify the initial (linear) volume in range 0...65536\n"
+           "      --channel-map=CHANNELMAP          Set the channel map to the use\n",
            argv0);
 }
 
 enum {
     ARG_VERSION = 256,
     ARG_STREAM_NAME,
-    ARG_VOLUME
+    ARG_VOLUME,
+    ARG_CHANNELMAP
 };
 
 int main(int argc, char *argv[]) {
-    struct pa_mainloop* m = NULL;
+    pa_mainloop* m = NULL;
     int ret = 1, r, c;
-    char *bn, *server = NULL, *filename;
+    char *bn, *server = NULL;
+    const char *filename;
     SF_INFO sfinfo;
 
     static const struct option long_options[] = {
@@ -225,9 +234,12 @@ int main(int argc, char *argv[]) {
         {"help",        0, NULL, 'h'},
         {"verbose",     0, NULL, 'v'},
         {"volume",      1, NULL, ARG_VOLUME},
+        {"channel-map", 1, NULL, ARG_CHANNELMAP},
         {NULL,          0, NULL, 0}
     };
 
+    setlocale(LC_ALL, "");
+
     if (!(bn = strrchr(argv[0], '/')))
         bn = argv[0];
     else
@@ -240,30 +252,30 @@ int main(int argc, char *argv[]) {
                 help(bn);
                 ret = 0;
                 goto quit;
-                
+
             case ARG_VERSION:
-                printf("paplay "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
+                printf("paplay "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version());
                 ret = 0;
                 goto quit;
 
             case 'd':
-                free(device);
-                device = strdup(optarg);
+                pa_xfree(device);
+                device = pa_xstrdup(optarg);
                 break;
 
             case 's':
-                free(server);
-                server = strdup(optarg);
+                pa_xfree(server);
+                server = pa_xstrdup(optarg);
                 break;
 
             case 'n':
-                free(client_name);
-                client_name = strdup(optarg);
+                pa_xfree(client_name);
+                client_name = pa_xstrdup(optarg);
                 break;
 
             case ARG_STREAM_NAME:
-                free(stream_name);
-                stream_name = strdup(optarg);
+                pa_xfree(stream_name);
+                stream_name = pa_xstrdup(optarg);
                 break;
 
             case 'v':
@@ -276,20 +288,21 @@ int main(int argc, char *argv[]) {
                 break;
             }
 
+            case ARG_CHANNELMAP:
+                if (!pa_channel_map_parse(&channel_map, optarg)) {
+                    fprintf(stderr, "Invalid channel map\n");
+                    goto quit;
+                }
+
+                channel_map_set = 1;
+                break;
+
             default:
                 goto quit;
         }
     }
 
-
     filename = optind < argc ? argv[optind] : "STDIN";
-    
-    
-    if (!client_name)
-        client_name = strdup(bn);
-
-    if (!stream_name)
-        stream_name = strdup(filename);
 
     memset(&sfinfo, 0, sizeof(sfinfo));
 
@@ -302,31 +315,68 @@ int main(int argc, char *argv[]) {
         fprintf(stderr, "Failed to open file '%s'\n", filename);
         goto quit;
     }
-              
+
     sample_spec.rate = sfinfo.samplerate;
     sample_spec.channels = sfinfo.channels;
-    
+
+    readf_function = NULL;
+
     switch (sfinfo.format & 0xFF) {
         case SF_FORMAT_PCM_16:
         case SF_FORMAT_PCM_U8:
+        case SF_FORMAT_PCM_S8:
+            sample_spec.format = PA_SAMPLE_S16NE;
+            readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
+            break;
+
         case SF_FORMAT_ULAW:
+            sample_spec.format = PA_SAMPLE_ULAW;
+            break;
+
         case SF_FORMAT_ALAW:
-            sample_spec.format = PA_SAMPLE_S16NE;
-            readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short;
+            sample_spec.format = PA_SAMPLE_ALAW;
             break;
+
         case SF_FORMAT_FLOAT:
+        case SF_FORMAT_DOUBLE:
         default:
             sample_spec.format = PA_SAMPLE_FLOAT32NE;
-            readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
+            readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_float;
             break;
     }
 
+    assert(pa_sample_spec_valid(&sample_spec));
+
+    if (channel_map_set && channel_map.channels != sample_spec.channels) {
+        fprintf(stderr, "Channel map doesn't match file.\n");
+        goto quit;
+    }
+
+    if (!client_name) {
+        client_name = pa_locale_to_utf8(bn);
+        if (!client_name)
+            client_name = pa_utf8_filter(bn);
+    }
+
+    if (!stream_name) {
+        const char *n;
+
+        n = sf_get_string(sndfile, SF_STR_TITLE);
+
+        if (!n)
+            n = filename;
+
+        stream_name = pa_locale_to_utf8(n);
+        if (!stream_name)
+            stream_name = pa_utf8_filter(n);
+    }
+
     if (verbose) {
         char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
         pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
         fprintf(stderr, "Using sample spec '%s'\n", t);
     }
-    
+
     /* Set up a new main loop */
     if (!(m = pa_mainloop_new())) {
         fprintf(stderr, "pa_mainloop_new() failed.\n");
@@ -338,8 +388,10 @@ int main(int argc, char *argv[]) {
     r = pa_signal_init(mainloop_api);
     assert(r == 0);
     pa_signal_new(SIGINT, exit_signal_callback, NULL);
+#ifdef SIGPIPE
     signal(SIGPIPE, SIG_IGN);
-    
+#endif
+
     /* Create a new connection context */
     if (!(context = pa_context_new(mainloop_api, client_name))) {
         fprintf(stderr, "pa_context_new() failed.\n");
@@ -349,14 +401,14 @@ int main(int argc, char *argv[]) {
     pa_context_set_state_callback(context, context_state_callback, NULL);
 
     /* Connect the context */
-    pa_context_connect(context, server, 1, NULL);
+    pa_context_connect(context, server, 0, NULL);
 
     /* Run the main loop */
     if (pa_mainloop_run(m, &ret) < 0) {
         fprintf(stderr, "pa_mainloop_run() failed.\n");
         goto quit;
     }
-    
+
 quit:
     if (stream)
         pa_stream_unref(stream);
@@ -369,13 +421,13 @@ quit:
         pa_mainloop_free(m);
     }
 
-    free(server);
-    free(device);
-    free(client_name);
-    free(stream_name);
+    pa_xfree(server);
+    pa_xfree(device);
+    pa_xfree(client_name);
+    pa_xfree(stream_name);
 
     if (sndfile)
         sf_close(sndfile);
-    
+
     return ret;
 }
diff --git a/src/utils/pasuspender.c b/src/utils/pasuspender.c
new file mode 100644 (file)
index 0000000..5b4885d
--- /dev/null
@@ -0,0 +1,316 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; 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 <sys/wait.h>
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include <sndfile.h>
+
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
+
+#include <pulse/pulseaudio.h>
+#include <pulsecore/macro.h>
+
+#if PA_API_VERSION < 10
+#error Invalid PulseAudio API version
+#endif
+
+#define BUFSIZE 1024
+
+static pa_context *context = NULL;
+static pa_mainloop_api *mainloop_api = NULL;
+static char **child_argv = NULL;
+static int child_argc = 0;
+static pid_t child_pid = (pid_t) -1;
+static int child_ret = 0;
+static int dead = 1;
+
+static void quit(int ret) {
+    pa_assert(mainloop_api);
+    mainloop_api->quit(mainloop_api, ret);
+}
+
+
+static void context_drain_complete(pa_context *c, void *userdata) {
+    pa_context_disconnect(c);
+}
+
+static void drain(void) {
+    pa_operation *o;
+
+    if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
+        pa_context_disconnect(context);
+    else
+        pa_operation_unref(o);
+}
+
+static void start_child(void) {
+
+    if ((child_pid = fork()) < 0) {
+
+        fprintf(stderr, "fork(): %s\n", strerror(errno));
+        quit(1);
+
+    } else if (child_pid == 0) {
+        /* Child */
+
+#ifdef __linux__
+        prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
+#endif
+
+        if (execvp(child_argv[0], child_argv) < 0)
+            fprintf(stderr, "execvp(): %s\n", strerror(errno));
+
+        _exit(1);
+
+    } else {
+
+        /* parent */
+        dead = 0;
+    }
+}
+
+static void suspend_complete(pa_context *c, int success, void *userdata) {
+    static int n = 0;
+
+    n++;
+
+    if (!success) {
+        fprintf(stderr, "Failure to suspend: %s\n", pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (n >= 2)
+        start_child();
+}
+
+static void resume_complete(pa_context *c, int success, void *userdata) {
+    static int n = 0;
+
+    n++;
+
+    if (!success) {
+        fprintf(stderr, "Failure to resume: %s\n", pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (n >= 2)
+        drain(); /* drain and quit */
+}
+
+static void context_state_callback(pa_context *c, void *userdata) {
+    pa_assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY:
+            if (pa_context_is_local(c)) {
+                pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL));
+                pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL));
+            } else {
+                fprintf(stderr, "WARNING: Sound server is not local, not suspending.\n");
+                start_child();
+            }
+
+            break;
+
+        case PA_CONTEXT_TERMINATED:
+            quit(0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
+
+            pa_context_unref(context);
+            context = NULL;
+
+            if (child_pid == (pid_t) -1)
+                /* not started yet, then we do it now */
+                start_child();
+            else if (dead)
+                /* already started, and dead, so let's quit */
+                quit(1);
+
+            break;
+    }
+}
+
+static void sigint_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
+    fprintf(stderr, "Got SIGINT, exiting.\n");
+    quit(0);
+}
+
+static void sigchld_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
+    int status = 0;
+    pid_t p;
+
+    p = waitpid(-1, &status, WNOHANG);
+
+    if (p != child_pid)
+        return;
+
+    dead = 1;
+
+    if (WIFEXITED(status))
+        child_ret = WEXITSTATUS(status);
+    else if (WIFSIGNALED(status)) {
+        fprintf(stderr, "WARNING: Child process terminated by signal %u\n", WTERMSIG(status));
+        child_ret = 1;
+    }
+
+    if (context) {
+        if (pa_context_is_local(context)) {
+            /* A context is around, so let's resume */
+            pa_operation_unref(pa_context_suspend_sink_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
+            pa_operation_unref(pa_context_suspend_source_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
+        } else
+            drain();
+    } else
+        /* Hmm, no context here, so let's terminate right away */
+        quit(0);
+}
+
+static void help(const char *argv0) {
+
+    printf("%s [options] ... \n\n"
+           "  -h, --help                            Show this help\n"
+           "      --version                         Show version\n"
+           "  -s, --server=SERVER                   The name of the server to connect to\n\n",
+           argv0);
+}
+
+enum {
+    ARG_VERSION = 256
+};
+
+int main(int argc, char *argv[]) {
+    pa_mainloop* m = NULL;
+    int c, ret = 1;
+    char *server = NULL, *bn;
+
+    static const struct option long_options[] = {
+        {"server",      1, NULL, 's'},
+        {"version",     0, NULL, ARG_VERSION},
+        {"help",        0, NULL, 'h'},
+        {NULL,          0, NULL, 0}
+    };
+
+    if (!(bn = strrchr(argv[0], '/')))
+        bn = argv[0];
+    else
+        bn++;
+
+    while ((c = getopt_long(argc, argv, "s:h", long_options, NULL)) != -1) {
+        switch (c) {
+            case 'h' :
+                help(bn);
+                ret = 0;
+                goto quit;
+
+            case ARG_VERSION:
+                printf("pasuspender "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version());
+                ret = 0;
+                goto quit;
+
+            case 's':
+                pa_xfree(server);
+                server = pa_xstrdup(optarg);
+                break;
+
+            default:
+                goto quit;
+        }
+    }
+
+    child_argv = argv + optind;
+    child_argc = argc - optind;
+
+    if (child_argc <= 0) {
+        help(bn);
+        ret = 0;
+        goto quit;
+    }
+
+    if (!(m = pa_mainloop_new())) {
+        fprintf(stderr, "pa_mainloop_new() failed.\n");
+        goto quit;
+    }
+
+    pa_assert_se(mainloop_api = pa_mainloop_get_api(m));
+    pa_assert_se(pa_signal_init(mainloop_api) == 0);
+    pa_signal_new(SIGINT, sigint_callback, NULL);
+    pa_signal_new(SIGCHLD, sigchld_callback, NULL);
+#ifdef SIGPIPE
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    if (!(context = pa_context_new(mainloop_api, bn))) {
+        fprintf(stderr, "pa_context_new() failed.\n");
+        goto quit;
+    }
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+    pa_context_connect(context, server, PA_CONTEXT_NOAUTOSPAWN, NULL);
+
+    if (pa_mainloop_run(m, &ret) < 0) {
+        fprintf(stderr, "pa_mainloop_run() failed.\n");
+        goto quit;
+    }
+
+quit:
+    if (context)
+        pa_context_unref(context);
+
+    if (m) {
+        pa_signal_done();
+        pa_mainloop_free(m);
+    }
+
+    pa_xfree(server);
+
+    if (!dead)
+        kill(child_pid, SIGTERM);
+
+    return ret == 0 ? child_ret : ret;
+}
similarity index 60%
rename from polyp/pax11publish.c
rename to src/utils/pax11publish.c
index a1cb006abe520dff8974aefb881a45ca86775d77..eee7b6a80a6c1692198bea2d84d1817870cef5fb 100644 (file)
@@ -1,20 +1,20 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 2 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
 
-#include "util.h"
-#include "log.h"
-#include "authkey.h"
-#include "native-common.h"
-#include "client-conf.h"
-#include "x11prop.h"
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/authkey.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/x11prop.h>
+
+#include "../pulse/client-conf.h"
 
 int main(int argc, char *argv[]) {
     const char *dname = NULL, *sink = NULL, *source = NULL, *server = NULL, *cookie_file = PA_NATIVE_COOKIE_FILE;
@@ -51,10 +54,10 @@ int main(int argc, char *argv[]) {
                 break;
             case 'h':
                 printf("%s [-D display] [-S server] [-O sink] [-I source] [-c file]  [-d|-e|-i|-r]\n\n"
-                       " -d    Show current Polypaudio data attached to X11 display (default)\n"
-                       " -e    Export local Polypaudio data to X11 display\n"
-                       " -i    Import Polypaudio data from X11 display to local environment variables and cookie file.\n"
-                       " -r    Remove Polypaudio data from X11 display\n",
+                       " -d    Show current PulseAudio data attached to X11 display (default)\n"
+                       " -e    Export local PulseAudio data to X11 display\n"
+                       " -i    Import PulseAudio data from X11 display to local environment variables and cookie file.\n"
+                       " -r    Remove PulseAudio data from X11 display\n",
                        pa_path_get_filename(argv[0]));
                 ret = 0;
                 goto finish;
@@ -89,35 +92,35 @@ int main(int argc, char *argv[]) {
     }
 
     if (!(d = XOpenDisplay(dname))) {
-        pa_log(__FILE__": XOpenDisplay() failed\n");
+        pa_log("XOpenDisplay() failed");
         goto finish;
     }
 
     switch (mode) {
         case DUMP: {
             char t[1024];
-            if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) 
+            if (pa_x11_get_prop(d, "PULSE_SERVER", t, sizeof(t)))
                 printf("Server: %s\n", t);
-            if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t)))
+            if (pa_x11_get_prop(d, "PULSE_SOURCE", t, sizeof(t)))
                 printf("Source: %s\n", t);
-            if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t)))
+            if (pa_x11_get_prop(d, "PULSE_SINK", t, sizeof(t)))
                 printf("Sink: %s\n", t);
-            if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t)))
+            if (pa_x11_get_prop(d, "PULSE_COOKIE", t, sizeof(t)))
                 printf("Cookie: %s\n", t);
 
             break;
         }
-            
+
         case IMPORT: {
             char t[1024];
-            if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) 
-                printf("POLYP_SERVER='%s'\nexport POLYP_SERVER\n", t);
-            if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t)))
-                printf("POLYP_SOURCE='%s'\nexport POLYP_SOURCE\n", t);
-            if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t)))
-                printf("POLYP_SINK='%s'\nexport POLYP_SINK\n", t);
-
-            if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) {
+            if (pa_x11_get_prop(d, "PULSE_SERVER", t, sizeof(t)))
+                printf("PULSE_SERVER='%s'\nexport PULSE_SERVER\n", t);
+            if (pa_x11_get_prop(d, "PULSE_SOURCE", t, sizeof(t)))
+                printf("PULSE_SOURCE='%s'\nexport PULSE_SOURCE\n", t);
+            if (pa_x11_get_prop(d, "PULSE_SINK", t, sizeof(t)))
+                printf("PULSE_SINK='%s'\nexport PULSE_SINK\n", t);
+
+            if (pa_x11_get_prop(d, "PULSE_COOKIE", t, sizeof(t))) {
                 uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
                 size_t l;
                 if ((l = pa_parsehex(t, cookie, sizeof(cookie))) != sizeof(cookie)) {
@@ -135,83 +138,83 @@ int main(int argc, char *argv[]) {
         }
 
         case EXPORT: {
-            struct pa_client_conf *c = pa_client_conf_new();
+            pa_client_conf *conf = pa_client_conf_new();
             uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
             char hx[PA_NATIVE_COOKIE_LENGTH*2+1];
-            assert(c);
+            assert(conf);
 
-            if (pa_client_conf_load(c, NULL) < 0) {
+            if (pa_client_conf_load(conf, NULL) < 0) {
                 fprintf(stderr, "Failed to load client configuration file.\n");
                 goto finish;
             }
 
-            if (pa_client_conf_env(c) < 0) {
+            if (pa_client_conf_env(conf) < 0) {
                 fprintf(stderr, "Failed to read environment configuration data.\n");
                 goto finish;
             }
 
-            pa_x11_del_prop(d, "POLYP_SERVER");
-            pa_x11_del_prop(d, "POLYP_SINK");
-            pa_x11_del_prop(d, "POLYP_SOURCE");
-            pa_x11_del_prop(d, "POLYP_ID");
-            pa_x11_del_prop(d, "POLYP_COOKIE");
-            
+            pa_x11_del_prop(d, "PULSE_SERVER");
+            pa_x11_del_prop(d, "PULSE_SINK");
+            pa_x11_del_prop(d, "PULSE_SOURCE");
+            pa_x11_del_prop(d, "PULSE_ID");
+            pa_x11_del_prop(d, "PULSE_COOKIE");
+
             if (server)
-                pa_x11_set_prop(d, "POLYP_SERVER", server);
-            else if (c->default_server)
-                pa_x11_set_prop(d, "POLYP_SERVER", c->default_server);
+                pa_x11_set_prop(d, "PULSE_SERVER", server);
+            else if (conf->default_server)
+                pa_x11_set_prop(d, "PULSE_SERVER", conf->default_server);
             else {
                 char hn[256];
                 if (!pa_get_fqdn(hn, sizeof(hn))) {
                     fprintf(stderr, "Failed to get FQDN.\n");
                     goto finish;
                 }
-                    
-                pa_x11_set_prop(d, "POLYP_SERVER", hn);
+
+                pa_x11_set_prop(d, "PULSE_SERVER", hn);
             }
 
             if (sink)
-                pa_x11_set_prop(d, "POLYP_SINK", sink);
-            else if (c->default_sink)
-                pa_x11_set_prop(d, "POLYP_SINK", c->default_sink);
+                pa_x11_set_prop(d, "PULSE_SINK", sink);
+            else if (conf->default_sink)
+                pa_x11_set_prop(d, "PULSE_SINK", conf->default_sink);
 
             if (source)
-                pa_x11_set_prop(d, "POLYP_SOURCE", source);
-            if (c->default_source)
-                pa_x11_set_prop(d, "POLYP_SOURCE", c->default_source);
+                pa_x11_set_prop(d, "PULSE_SOURCE", source);
+            if (conf->default_source)
+                pa_x11_set_prop(d, "PULSE_SOURCE", conf->default_source);
+
+            pa_client_conf_free(conf);
 
-            pa_client_conf_free(c);
-            
             if (pa_authkey_load_auto(cookie_file, cookie, sizeof(cookie)) < 0) {
                 fprintf(stderr, "Failed to load cookie data\n");
                 goto finish;
             }
 
-            pa_x11_set_prop(d, "POLYP_COOKIE", pa_hexstr(cookie, sizeof(cookie), hx, sizeof(hx)));
+            pa_x11_set_prop(d, "PULSE_COOKIE", pa_hexstr(cookie, sizeof(cookie), hx, sizeof(hx)));
             break;
         }
 
         case REMOVE:
-            pa_x11_del_prop(d, "POLYP_SERVER");
-            pa_x11_del_prop(d, "POLYP_SINK");
-            pa_x11_del_prop(d, "POLYP_SOURCE");
-            pa_x11_del_prop(d, "POLYP_ID");
-            pa_x11_del_prop(d, "POLYP_COOKIE");
+            pa_x11_del_prop(d, "PULSE_SERVER");
+            pa_x11_del_prop(d, "PULSE_SINK");
+            pa_x11_del_prop(d, "PULSE_SOURCE");
+            pa_x11_del_prop(d, "PULSE_ID");
+            pa_x11_del_prop(d, "PULSE_COOKIE");
             break;
-            
+
         default:
             fprintf(stderr, "No yet implemented.\n");
             goto finish;
     }
 
     ret = 0;
-    
+
 finish:
 
     if (d) {
         XSync(d, False);
         XCloseDisplay(d);
     }
-    
+
     return ret;
 }
diff --git a/todo b/todo
new file mode 100644 (file)
index 0000000..da993a2
--- /dev/null
+++ b/todo
@@ -0,0 +1,49 @@
+Build System:
+- Remove symdef files and use macros (like most other projects)
+- Use own name mangling scheme instead of ltdl's, which will eliminate the
+  need for .la files or extra trickery.
+
+Porting:
+- rtp module ported to Win32 (sendmsg/recvmsg emulation)
+
+I18N:
+- iconv stuff sent from utils to server (UTF-8)
+- iconv sample loading in server
+- gettextify pulseaudio
+
+Cleanups:
+- drop dependency of libpulse on libX11, instead use an external mini binary
+- module-tunnel: improve latency calculation
+- use software volume when hardware doesn't support all channels (alsa done)
+- using POSIX monotonous clocks wherever possible instead of gettimeofday()
+
+Test:
+- autoload
+
+Auth/Crypto:
+- ssl
+- key rings for auth
+- challenge response auth
+- sasl auth 
+
+Features:
+- chroot()
+- use scatter/gather io for sockets
+- CODECs to reduce bandwidth usage (plug-in based)
+- multiline configuration statements
+- paplay needs to set a channel map. our default is only correct for AIFF.
+  (we need help from libsndfile for this)
+- examine if it is possible to mimic esd's handling of half duplex cards
+  (switch to capture when a recording client connects and drop playback during
+  that time)
+- Support for device selection in waveout driver
+- add an API to libpulse for allocating memory from the pa_context memory pool
+- better ".include" command in configuration files. should have glob support.
+- recursive .if
+
+Long term:
+- pass meta info for hearing impaired
+- X11: support for the X11 synchronization extension
+
+Backends for:
+- portaudio  (semi-done)